summaryrefslogtreecommitdiff
path: root/pr/src
diff options
context:
space:
mode:
Diffstat (limited to 'pr/src')
-rw-r--r--pr/src/Makefile316
-rw-r--r--pr/src/io/Makefile68
-rw-r--r--pr/src/io/pprmwait.h77
-rw-r--r--pr/src/io/prdir.c83
-rw-r--r--pr/src/io/prfile.c554
-rw-r--r--pr/src/io/prio.c184
-rw-r--r--pr/src/io/priometh.c232
-rw-r--r--pr/src/io/pripv6.c26
-rw-r--r--pr/src/io/prlayer.c574
-rw-r--r--pr/src/io/prlog.c418
-rw-r--r--pr/src/io/prmapopt.c408
-rw-r--r--pr/src/io/prmmap.c69
-rw-r--r--pr/src/io/prmwait.c702
-rw-r--r--pr/src/io/prprf.c1226
-rw-r--r--pr/src/io/prscanf.c637
-rw-r--r--pr/src/io/prsocket.c1519
-rw-r--r--pr/src/io/prstdio.c46
-rw-r--r--pr/src/linking/Makefile43
-rw-r--r--pr/src/linking/prlink.c950
-rw-r--r--pr/src/malloc/Makefile41
-rw-r--r--pr/src/malloc/prmalloc.c1156
-rw-r--r--pr/src/malloc/prmem.c233
-rw-r--r--pr/src/md/Makefile57
-rw-r--r--pr/src/md/mac/MacErrorHandling.h633
-rw-r--r--pr/src/md/mac/macdll.c361
-rw-r--r--pr/src/md/mac/macio.c1897
-rw-r--r--pr/src/md/mac/macsockclient.c558
-rw-r--r--pr/src/md/mac/macsocket.h219
-rw-r--r--pr/src/md/mac/macsockotpt.c1542
-rw-r--r--pr/src/md/mac/macthr.c381
-rw-r--r--pr/src/md/mac/mactime.c266
-rw-r--r--pr/src/md/mac/mdmac.c842
-rw-r--r--pr/src/md/mac/prcpucfg.h115
-rw-r--r--pr/src/md/os2/Makefile51
-rw-r--r--pr/src/md/os2/os2_errors.c1053
-rw-r--r--pr/src/md/os2/os2cv.c332
-rw-r--r--pr/src/md/os2/os2gc.c73
-rw-r--r--pr/src/md/os2/os2inrval.c81
-rw-r--r--pr/src/md/os2/os2io.c620
-rw-r--r--pr/src/md/os2/os2misc.c513
-rw-r--r--pr/src/md/os2/os2poll.c165
-rw-r--r--pr/src/md/os2/os2sem.c74
-rw-r--r--pr/src/md/os2/os2sock.c770
-rw-r--r--pr/src/md/os2/os2thred.c245
-rw-r--r--pr/src/md/prosdep.c87
-rw-r--r--pr/src/md/unix/Makefile229
-rw-r--r--pr/src/md/unix/aix.c165
-rw-r--r--pr/src/md/unix/aixwrap.c46
-rw-r--r--pr/src/md/unix/bsdi.c93
-rw-r--r--pr/src/md/unix/freebsd.c232
-rw-r--r--pr/src/md/unix/hpux.c334
-rw-r--r--pr/src/md/unix/irix.c1308
-rw-r--r--pr/src/md/unix/linux.c230
-rw-r--r--pr/src/md/unix/ncr.c367
-rw-r--r--pr/src/md/unix/nec.c81
-rw-r--r--pr/src/md/unix/objs.mk176
-rw-r--r--pr/src/md/unix/os_AIX.s102
-rw-r--r--pr/src/md/unix/os_BSD_386_2.s54
-rw-r--r--pr/src/md/unix/os_ReliantUNIX.s108
-rw-r--r--pr/src/md/unix/os_SunOS.s51
-rw-r--r--pr/src/md/unix/os_SunOS_ultrasparc.s157
-rw-r--r--pr/src/md/unix/os_SunOS_x86.s60
-rw-r--r--pr/src/md/unix/osf1.c88
-rw-r--r--pr/src/md/unix/pthreads_user.c465
-rw-r--r--pr/src/md/unix/reliantunix.c114
-rw-r--r--pr/src/md/unix/scoos.c154
-rw-r--r--pr/src/md/unix/solaris.c808
-rw-r--r--pr/src/md/unix/sony.c90
-rw-r--r--pr/src/md/unix/sunos4.c77
-rw-r--r--pr/src/md/unix/unix.c3210
-rw-r--r--pr/src/md/unix/unix_errors.c1484
-rw-r--r--pr/src/md/unix/unixware.c548
-rw-r--r--pr/src/md/unix/uxproces.c715
-rw-r--r--pr/src/md/unix/uxwrap.c518
-rw-r--r--pr/src/md/windows/Makefile75
-rw-r--r--pr/src/md/windows/ntgc.c107
-rw-r--r--pr/src/md/windows/ntinrval.c86
-rw-r--r--pr/src/md/windows/ntio.c3776
-rw-r--r--pr/src/md/windows/ntmisc.c603
-rw-r--r--pr/src/md/windows/ntsem.c65
-rw-r--r--pr/src/md/windows/ntthread.c434
-rw-r--r--pr/src/md/windows/w16callb.c243
-rw-r--r--pr/src/md/windows/w16error.c233
-rw-r--r--pr/src/md/windows/w16fmem.c59
-rw-r--r--pr/src/md/windows/w16gc.c67
-rw-r--r--pr/src/md/windows/w16io.c836
-rw-r--r--pr/src/md/windows/w16mem.c66
-rw-r--r--pr/src/md/windows/w16null.c97
-rw-r--r--pr/src/md/windows/w16proc.c58
-rw-r--r--pr/src/md/windows/w16sock.c1151
-rw-r--r--pr/src/md/windows/w16stdio.c150
-rw-r--r--pr/src/md/windows/w16thred.c407
-rw-r--r--pr/src/md/windows/w32poll.c189
-rw-r--r--pr/src/md/windows/w95cv.c328
-rw-r--r--pr/src/md/windows/w95io.c871
-rw-r--r--pr/src/md/windows/w95sock.c614
-rw-r--r--pr/src/md/windows/w95thred.c226
-rw-r--r--pr/src/md/windows/win32_errors.c1104
-rw-r--r--pr/src/memory/Makefile42
-rw-r--r--pr/src/memory/prseg.c192
-rw-r--r--pr/src/misc/Makefile64
-rw-r--r--pr/src/misc/pralarm.c263
-rw-r--r--pr/src/misc/pratom.c105
-rw-r--r--pr/src/misc/prdtoa.c3189
-rw-r--r--pr/src/misc/prenv.c55
-rw-r--r--pr/src/misc/prerror.c89
-rw-r--r--pr/src/misc/prinit.c519
-rw-r--r--pr/src/misc/prinrval.c130
-rw-r--r--pr/src/misc/prlog2.c62
-rw-r--r--pr/src/misc/prlong.c259
-rw-r--r--pr/src/misc/prnetdb.c830
-rw-r--r--pr/src/misc/prsystem.c76
-rw-r--r--pr/src/misc/prthinfo.c229
-rw-r--r--pr/src/misc/prtime.c1930
-rw-r--r--pr/src/nspr.rc126
-rw-r--r--pr/src/pthreads/Makefile47
-rw-r--r--pr/src/pthreads/ptio.c3381
-rw-r--r--pr/src/pthreads/ptmisc.c45
-rw-r--r--pr/src/pthreads/ptsynch.c702
-rw-r--r--pr/src/pthreads/ptthread.c1309
-rw-r--r--pr/src/resource.h33
-rw-r--r--pr/src/threads/Makefile59
-rw-r--r--pr/src/threads/combined/Makefile52
-rw-r--r--pr/src/threads/combined/README62
-rw-r--r--pr/src/threads/combined/prucpu.c304
-rw-r--r--pr/src/threads/combined/prucv.c634
-rw-r--r--pr/src/threads/combined/prulock.c420
-rw-r--r--pr/src/threads/combined/prustack.c183
-rw-r--r--pr/src/threads/combined/pruthr.c1794
-rw-r--r--pr/src/threads/prcmon.c392
-rw-r--r--pr/src/threads/prcthr.c414
-rw-r--r--pr/src/threads/prdump.c124
-rw-r--r--pr/src/threads/prmon.c202
-rw-r--r--pr/src/threads/prsem.c155
-rw-r--r--pr/src/threads/prtpd.c296
135 files changed, 63174 insertions, 0 deletions
diff --git a/pr/src/Makefile b/pr/src/Makefile
new file mode 100644
index 00000000..6e514e2d
--- /dev/null
+++ b/pr/src/Makefile
@@ -0,0 +1,316 @@
+#
+# The contents of this file are subject to the Netscape Public License
+# Version 1.0 (the "NPL"); you may not use this file except in
+# compliance with the NPL. You may obtain a copy of the NPL at
+# http://www.mozilla.org/NPL/
+#
+# Software distributed under the NPL is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+# for the specific language governing rights and limitations under the
+# NPL.
+#
+# The Initial Developer of this code under the NPL is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1998 Netscape Communications Corporation. All Rights
+# Reserved.
+#
+
+#! gmake
+
+MOD_DEPTH = ../..
+
+include $(MOD_DEPTH)/config/config.mk
+
+ifdef USE_PTHREADS
+ DIRS = io linking malloc md memory misc pthreads threads
+else
+ DIRS = io linking malloc md memory misc threads
+endif
+
+#
+# Define platform-dependent OS_LIBS
+#
+
+# Disable optimization of the nspr on SunOS4.1.3
+ifeq ($(OS_ARCH),SunOS)
+ifeq ($(OS_RELEASE),4.1.3_U1)
+OPTIMIZER =
+export OPTIMIZER
+OS_LIBS = -lm
+else # 4.1.3_U1
+ifeq ($(OS_RELEASE),5.4_i86pc)
+OS_LIBS = -lthread -lposix4 -lsocket -lnsl -lgen -lresolv -ldl
+else
+ifdef USE_PTHREADS
+OS_LIBS = -lpthread -lposix4 -lsocket -lnsl -ldl
+else
+ifdef LOCAL_THREADS_ONLY
+OS_LIBS = -lsocket -lnsl -ldl
+else
+OS_LIBS = -lthread -lposix4 -lsocket -lnsl -ldl
+endif # LOCAL_THREADS_ONLY
+endif # USE_PTHREADS
+endif # 5.4_i86pc
+endif # 4.1.3_U1
+endif # SunOS
+
+ifeq ($(OS_ARCH), IRIX)
+ifeq ($(USE_PTHREADS), 1)
+OS_LIBS += -lpthread
+endif
+endif
+
+ifeq ($(OS_ARCH),AIX)
+ifeq ($(CLASSIC_NSPR),1)
+ifeq ($(OS_RELEASE),4.1)
+OS_LIBS += -lsvld -lc
+else
+OS_LIBS += -ldl -lc
+endif
+else
+ifeq ($(OS_RELEASE),4.1)
+OS_LIBS += -lsvld -lC_r -lC -lpthreads -lc_r -lm /usr/lib/libc.a
+else
+OS_LIBS += -ldl -lC_r -lC -lpthreads -lc_r -lm /usr/lib/libc.a
+endif
+endif
+endif
+
+# On AIX, we override malloc in non-pthread versions. On AIX 4.2 or
+# above, this requires that we use the rtl-enabled version of libc.a.
+ifeq ($(OS_ARCH),AIX)
+ifneq ($(OS_RELEASE),4.1)
+ifneq ($(USE_PTHREADS),1)
+BUILD_AIX_RTL_LIBC = 1
+AIX_RTL_LIBC = $(OBJDIR)/libc.a
+endif
+endif
+endif
+
+ifeq ($(OS_ARCH),OSF1)
+ifneq ($(OS_RELEASE),V2.0)
+OS_LIBS = -lc_r
+endif
+ifeq ($(USE_PTHREADS), 1)
+OS_LIBS += -lpthread -lrt
+endif
+ifeq ($(USE_IPV6), 1)
+OS_LIBS += -lip6
+endif
+endif
+
+ifeq ($(OS_ARCH),Linux)
+ifeq ($(USE_PTHREADS), 1)
+OS_LIBS = -L/lib -lpthread -ldl -lc
+else
+OS_LIBS = -L/lib -ldl -lc
+endif
+endif
+
+ifeq ($(OS_ARCH),HP-UX)
+ifeq ($(OS_RELEASE),A.09.03)
+OS_LIBS = -ldld -L/lib/pa1.1 -lm
+else
+OS_LIBS = -ldld -lm -lc
+endif
+ifeq ($(USE_PTHREADS), 1)
+OS_LIBS += -lpthread
+endif
+ifeq ($(PTHREADS_USER), 1)
+OS_LIBS += -lpthread
+endif
+endif
+
+ifeq ($(OS_ARCH),UNIXWARE)
+OS_LIBS = -lsocket -lc
+endif
+
+ifeq ($(OS_ARCH),NEWS-OS)
+OS_LIBS = -lsocket -lnsl -lgen -lresolv
+endif
+
+ifeq ($(OS_ARCH),WINNT)
+ifeq ($(OS_TARGET),OS2)
+OS_LIBS = so32dll.lib tcp32dll.lib
+else
+ifneq ($(OS_TARGET),WIN16)
+OS_LIBS = wsock32.lib winmm.lib
+endif
+endif
+endif
+
+#
+# Define platform-dependent OBJS
+#
+
+OBJS = \
+ io/$(OBJDIR)/prmwait.o \
+ io/$(OBJDIR)/prmapopt.o \
+ io/$(OBJDIR)/priometh.o \
+ io/$(OBJDIR)/prlayer.o \
+ io/$(OBJDIR)/prlog.o \
+ io/$(OBJDIR)/prmmap.o \
+ io/$(OBJDIR)/prprf.o \
+ io/$(OBJDIR)/prscanf.o \
+ io/$(OBJDIR)/prstdio.o \
+ threads/$(OBJDIR)/prcmon.o \
+ linking/$(OBJDIR)/prlink.o \
+ malloc/$(OBJDIR)/prmalloc.o \
+ malloc/$(OBJDIR)/prmem.o \
+ md/$(OBJDIR)/prosdep.o \
+ memory/$(OBJDIR)/prseg.o \
+ misc/$(OBJDIR)/pralarm.o \
+ misc/$(OBJDIR)/pratom.o \
+ misc/$(OBJDIR)/prdtoa.o \
+ misc/$(OBJDIR)/prenv.o \
+ misc/$(OBJDIR)/prerror.o \
+ misc/$(OBJDIR)/prinit.o \
+ misc/$(OBJDIR)/prinrval.o \
+ misc/$(OBJDIR)/prlog2.o \
+ misc/$(OBJDIR)/prlong.o \
+ misc/$(OBJDIR)/prnetdb.o \
+ misc/$(OBJDIR)/prsystem.o \
+ misc/$(OBJDIR)/prthinfo.o \
+ misc/$(OBJDIR)/prtime.o
+
+ifdef USE_PTHREADS
+OBJS += \
+ pthreads/$(OBJDIR)/ptsynch.o \
+ pthreads/$(OBJDIR)/ptio.o \
+ pthreads/$(OBJDIR)/ptthread.o \
+ pthreads/$(OBJDIR)/ptmisc.o
+else
+OBJS += \
+ io/$(OBJDIR)/prdir.o \
+ io/$(OBJDIR)/prfile.o \
+ io/$(OBJDIR)/prio.o \
+ io/$(OBJDIR)/prsocket.o \
+ threads/$(OBJDIR)/prcthr.o \
+ threads/$(OBJDIR)/prdump.o \
+ threads/$(OBJDIR)/prmon.o \
+ threads/$(OBJDIR)/prsem.o \
+ threads/$(OBJDIR)/prtpd.o \
+ threads/combined/$(OBJDIR)/prucpu.o \
+ threads/combined/$(OBJDIR)/prucv.o \
+ threads/combined/$(OBJDIR)/prulock.o \
+ threads/combined/$(OBJDIR)/prustack.o \
+ threads/combined/$(OBJDIR)/pruthr.o
+endif
+
+ifeq ($(USE_IPV6), 1)
+OBJS += io/$(OBJDIR)/pripv6.o
+endif
+
+ifeq ($(OS_ARCH), WINNT)
+ifneq (,$(filter-out WIN16 OS2,$(OS_TARGET)))
+DLLBASE=/BASE:0x30000000
+RES=$(OBJDIR)/nspr.res
+RESNAME=nspr.rc
+endif
+
+ifeq ($(OS_TARGET), WIN16)
+OBJS += md/windows/$(OBJDIR)/w16null.o \
+ md/windows/$(OBJDIR)/w16proc.o \
+ md/windows/$(OBJDIR)/w16thred.o \
+ md/windows/$(OBJDIR)/w16fmem.o \
+ md/windows/$(OBJDIR)/w16sock.o \
+ md/windows/$(OBJDIR)/w16mem.o \
+ md/windows/$(OBJDIR)/w16io.o \
+ md/windows/$(OBJDIR)/w16gc.o \
+ md/windows/$(OBJDIR)/w16error.o \
+ md/windows/$(OBJDIR)/w16callb.o \
+ md/windows/$(OBJDIR)/ntinrval.o
+EXTRA_LIBS += $(MOD_DEPTH)/tools/winsock.lib
+W16_EXPORTS = EXPORT _malloc.2=_PR_MD_malloc RESIDENT, \
+ _realloc.3=_PR_MD_realloc RESIDENT, \
+ _calloc.4=_PR_MD_calloc RESIDENT, \
+ _free.5=_PR_MD_free RESIDENT, \
+ _getenv.9=_PR_MD_getenv RESIDENT, \
+ _printf.11=_PR_MD_printf RESIDENT, \
+ _strftime.13=_PR_MD_strftime RESIDENT, \
+ _sscanf.33=_PR_MD_sscanf RESIDENT, \
+ _putenv.10=_PR_MD_putenv RESIDENT, \
+ _fprintf.12=_PR_MD_fprintf RESIDENT
+else
+ifeq ($(OS_TARGET), WIN95)
+OBJS += md/windows/$(OBJDIR)/w95io.o \
+ md/windows/$(OBJDIR)/w95sock.o \
+ md/windows/$(OBJDIR)/w95thred.o \
+ md/windows/$(OBJDIR)/w95cv.o \
+ md/windows/$(OBJDIR)/ntgc.o \
+ md/windows/$(OBJDIR)/ntmisc.o \
+ md/windows/$(OBJDIR)/ntinrval.o \
+ md/windows/$(OBJDIR)/ntsem.o \
+ md/windows/$(OBJDIR)/win32_errors.o \
+ md/windows/$(OBJDIR)/w32poll.o
+else
+ifeq ($(OS_TARGET), OS2)
+OBJS += md/os2/$(OBJDIR)/os2io.o \
+ md/os2/$(OBJDIR)/os2sock.o \
+ md/os2/$(OBJDIR)/os2thred.o \
+ md/os2/$(OBJDIR)/os2cv.o \
+ md/os2/$(OBJDIR)/os2gc.o \
+ md/os2/$(OBJDIR)/os2misc.o \
+ md/os2/$(OBJDIR)/os2inrval.o \
+ md/os2/$(OBJDIR)/os2sem.o \
+ md/os2/$(OBJDIR)/os2_errors.o \
+ md/os2/$(OBJDIR)/os2poll.o \
+ md/os2/$(OBJDIR)/ramsem.o \
+ md/os2/$(OBJDIR)/sem_x86.o
+else
+OBJS += md/windows/$(OBJDIR)/ntio.o \
+ md/windows/$(OBJDIR)/ntgc.o \
+ md/windows/$(OBJDIR)/ntthread.o \
+ md/windows/$(OBJDIR)/ntmisc.o \
+ md/windows/$(OBJDIR)/ntinrval.o \
+ md/windows/$(OBJDIR)/ntsem.o \
+ md/windows/$(OBJDIR)/win32_errors.o \
+ md/windows/$(OBJDIR)/w32poll.o
+endif
+endif
+endif
+
+else
+
+ THREAD_DIR=threads/combined/$(OBJDIR)
+
+ifeq ($(OS_ARCH), MAC)
+ MD_DIR = md/mac/$(OBJDIR)
+else
+ MD_DIR = md/unix/$(OBJDIR)
+ include md/unix/objs.mk
+endif
+
+endif
+
+LIBRARY_NAME = nspr
+LIBRARY_VERSION = $(MOD_VERSION)
+
+RELEASE_LIBS = $(TARGETS)
+
+include $(MOD_DEPTH)/config/rules.mk
+
+ifeq ($(BUILD_AIX_RTL_LIBC),1)
+TARGETS += $(AIX_RTL_LIBC)
+# XXX is this a shared library?
+endif
+
+#
+# The Client build wants the shared libraries in $(DIST)/bin,
+# so we also install them there.
+#
+
+export:: $(TARGETS)
+ $(INSTALL) -m 444 $(TARGETS) $(DIST)/lib
+ $(INSTALL) -m 444 $(SHARED_LIBRARY) $(DIST)/bin
+ifeq ($(MOZ_BITS),16)
+ $(INSTALL) -m 444 $(TARGETS) $(MOZ_DIST)/lib
+ $(INSTALL) -m 444 $(TARGETS) $(MOZ_DIST)/bin
+endif
+
+ifeq ($(BUILD_AIX_RTL_LIBC),1)
+$(AIX_RTL_LIBC): /usr/ccs/lib/libc.a
+ rtl_enable -o $@ $<
+endif
+
+install:: export
diff --git a/pr/src/io/Makefile b/pr/src/io/Makefile
new file mode 100644
index 00000000..68dda471
--- /dev/null
+++ b/pr/src/io/Makefile
@@ -0,0 +1,68 @@
+#
+# The contents of this file are subject to the Netscape Public License
+# Version 1.0 (the "NPL"); you may not use this file except in
+# compliance with the NPL. You may obtain a copy of the NPL at
+# http://www.mozilla.org/NPL/
+#
+# Software distributed under the NPL is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+# for the specific language governing rights and limitations under the
+# NPL.
+#
+# The Initial Developer of this code under the NPL is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1998 Netscape Communications Corporation. All Rights
+# Reserved.
+#
+
+#! gmake
+
+MOD_DEPTH = ../../..
+
+include $(MOD_DEPTH)/config/config.mk
+
+# Disable optimization of the nspr on SunOS4.1.3
+ifeq ($(OS_ARCH),SunOS)
+ifeq ($(OS_RELEASE),4.1.3_U1)
+OPTIMIZER =
+endif
+endif
+
+CSRCS = \
+ prmwait.c \
+ priometh.c \
+ prmapopt.c \
+ prlayer.c \
+ prlog.c \
+ prmmap.c \
+ prprf.c \
+ prscanf.c \
+ prstdio.c \
+ $(NULL)
+
+ifndef USE_PTHREADS
+ CSRCS += \
+ prdir.c \
+ prfile.c \
+ prio.c \
+ prlog.c \
+ prmmap.c \
+ prprf.c \
+ prsocket.c \
+ prstdio.c \
+ $(NULL)
+endif
+
+ifdef USE_IPV6
+CSRCS += pripv6.c
+endif
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(DIST)/include/private -I$(DIST)/include
+
+include $(MOD_DEPTH)/config/rules.mk
+
+export:: $(TARGETS)
+
+install:: export
diff --git a/pr/src/io/pprmwait.h b/pr/src/io/pprmwait.h
new file mode 100644
index 00000000..55b258e0
--- /dev/null
+++ b/pr/src/io/pprmwait.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#if defined(_PPRMWAIT_H)
+#else
+#define _PPRMWAIT_H
+
+#include "prlock.h"
+#include "prcvar.h"
+#include "prclist.h"
+#include "prthread.h"
+
+#define _PR_HASH_OFFSET 75013
+#define MAX_POLLING_INTERVAL 100
+#define _PR_POLL_COUNT_FUDGE 64
+#define MAX_POLLING_INTERVAL 100
+#define _PR_DEFAULT_HASH_LENGTH 59
+
+#define _MW_REHASH(a, i, m) _MW_HASH((PRUint32)(a) + (i) + _PR_HASH_OFFSET, m)
+#define _MW_HASH(a, m) ((((PRUint32)(a) >> 4) ^ ((PRUint32)(a) >> 10)) % (m))
+#define _MW_ABORTED(_rv) \
+ ((PR_FAILURE == (_rv)) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError()))
+
+typedef enum {_prmw_success, _prmw_rehash, _prmw_error} _PR_HashStory;
+
+typedef struct _PRWaiterHash
+{
+ PRUint16 count; /* current number in hash table */
+ PRUint16 length; /* current size of the hash table */
+ PRRecvWait *recv_wait; /* hash table of receive wait objects */
+} _PRWaiterHash;
+
+typedef enum {_prmw_running, _prmw_stopping, _prmw_stopped} PRMWGroupState;
+
+struct PRWaitGroup
+{
+ PRCList group_link; /* all groups are linked to each other */
+ PRCList io_ready; /* list of I/O requests that are ready */
+ PRMWGroupState state; /* state of this group (so we can shut down) */
+
+ PRLock *ml; /* lock for synchronizing this wait group */
+ PRCondVar *io_taken; /* calling threads notify when they take I/O */
+ PRCondVar *io_complete; /* calling threads wait here for completions */
+ PRCondVar *new_business; /* polling thread waits here more work */
+ PRCondVar *mw_manage; /* used to manage group lists */
+ PRThread* poller; /* thread that's actually doing the poll() */
+ PRUint16 waiting_threads; /* number of threads waiting for recv */
+ PRUint16 polling_count; /* number of elements in the polling list */
+ PRPollDesc *polling_list; /* list poller builds for polling */
+ PRIntervalTime last_poll; /* last time we polled */
+ _PRWaiterHash *waiter; /* pointer to hash table of wait receive objects */
+};
+
+typedef struct _PRGlobalState
+{
+ PRCList group_list; /* master of the group list */
+ PRWaitGroup *group; /* the default (NULL) group */
+} _PRGlobalState;
+
+#endif /* defined(_PPRMWAIT_H) */
+
+/* pprmwait.h */
diff --git a/pr/src/io/prdir.c b/pr/src/io/prdir.c
new file mode 100644
index 00000000..4d6a91fd
--- /dev/null
+++ b/pr/src/io/prdir.c
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+PR_IMPLEMENT(PRDir*) PR_OpenDir(const char *name)
+{
+ PRDir *dir;
+ PRStatus sts;
+
+ dir = PR_NEW(PRDir);
+ if (dir) {
+ sts = _PR_MD_OPEN_DIR(&dir->md,name);
+ if (sts != PR_SUCCESS) {
+ PR_DELETE(dir);
+ return NULL;
+ }
+ } else {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ }
+ return dir;
+}
+
+PR_IMPLEMENT(PRDirEntry*) PR_ReadDir(PRDir *dir, PRDirFlags flags)
+{
+ /* _MD_READ_DIR return a char* to the name; allocation in machine-dependent code */
+ char* name = _PR_MD_READ_DIR(&dir->md, flags);
+ dir->d.name = name;
+ return name ? &dir->d : NULL;
+}
+
+PR_IMPLEMENT(PRStatus) PR_CloseDir(PRDir *dir)
+{
+PRInt32 rv;
+
+ if (dir) {
+ rv = _PR_MD_CLOSE_DIR(&dir->md);
+ PR_DELETE(dir);
+ if (rv < 0) {
+ return PR_FAILURE;
+ } else
+ return PR_SUCCESS;
+ }
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus) PR_MkDir(const char *name, PRIntn mode)
+{
+PRInt32 rv;
+
+ rv = _PR_MD_MKDIR(name, mode);
+ if (rv < 0) {
+ return PR_FAILURE;
+ } else
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus) PR_RmDir(const char *name)
+{
+PRInt32 rv;
+
+ rv = _PR_MD_RMDIR(name);
+ if (rv < 0) {
+ return PR_FAILURE;
+ } else
+ return PR_SUCCESS;
+}
+
diff --git a/pr/src/io/prfile.c b/pr/src/io/prfile.c
new file mode 100644
index 00000000..ab20b2a3
--- /dev/null
+++ b/pr/src/io/prfile.c
@@ -0,0 +1,554 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+#include <string.h>
+#include <fcntl.h>
+
+#ifdef XP_UNIX
+#ifdef AIX
+/* To pick up sysconf */
+#include <unistd.h>
+#else
+/* To pick up getrlimit, setrlimit */
+#include <sys/time.h>
+#include <sys/resource.h>
+#endif
+#endif /* XP_UNIX */
+
+extern PRLock *_pr_flock_lock;
+
+static PRInt32 PR_CALLBACK FileRead(PRFileDesc *fd, void *buf, PRInt32 amount)
+{
+ PRInt32 rv = 0;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ rv = -1;
+ }
+ if (rv == -1)
+ return rv;
+
+ rv = _PR_MD_READ(fd, buf, amount);
+ if (rv < 0) {
+ PR_ASSERT(rv == -1);
+ }
+ PR_LOG(_pr_io_lm, PR_LOG_MAX, ("read -> %d", rv));
+ return rv;
+}
+
+static PRInt32 PR_CALLBACK FileWrite(PRFileDesc *fd, const void *buf, PRInt32 amount)
+{
+ PRInt32 rv = 0;
+ PRInt32 temp, count;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ rv = -1;
+ }
+ if (rv != 0)
+ return rv;
+
+ count = 0;
+ while (amount > 0) {
+ temp = _PR_MD_WRITE(fd, buf, amount);
+ if (temp < 0) {
+ count = -1;
+ break;
+ }
+ count += temp;
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ buf = (const void*) ((const char*)buf + temp);
+ amount -= temp;
+ }
+ PR_LOG(_pr_io_lm, PR_LOG_MAX, ("write -> %d", count));
+ return count;
+}
+
+static PRInt32 PR_CALLBACK FileSeek(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence)
+{
+ PRInt32 result;
+
+ result = _PR_MD_LSEEK(fd, offset, whence);
+ return result;
+}
+
+static PRInt64 PR_CALLBACK FileSeek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence)
+{
+ PRInt64 result;
+
+ result = _PR_MD_LSEEK64(fd, offset, whence);
+ return result;
+}
+
+static PRInt32 PR_CALLBACK FileAvailable(PRFileDesc *fd)
+{
+ PRInt32 result, cur, end;
+
+ cur = _PR_MD_LSEEK(fd, 0, PR_SEEK_CUR);
+
+ if (cur >= 0)
+ end = _PR_MD_LSEEK(fd, 0, PR_SEEK_END);
+
+ if ((cur < 0) || (end < 0)) {
+ return -1;
+ }
+
+ result = end - cur;
+ _PR_MD_LSEEK(fd, cur, PR_SEEK_SET);
+
+ return result;
+}
+
+static PRInt64 PR_CALLBACK FileAvailable64(PRFileDesc *fd)
+{
+ PRInt64 result, cur, end;
+ PRInt64 minus_one;
+
+ LL_I2L(minus_one, -1);
+ cur = _PR_MD_LSEEK64(fd, LL_ZERO, PR_SEEK_CUR);
+
+ if (LL_GE_ZERO(cur))
+ end = _PR_MD_LSEEK64(fd, LL_ZERO, PR_SEEK_END);
+
+ if (!LL_GE_ZERO(cur) || !LL_GE_ZERO(end)) return minus_one;
+
+ LL_SUB(result, end, cur);
+ (void)_PR_MD_LSEEK64(fd, cur, PR_SEEK_SET);
+
+ return result;
+}
+
+static PRStatus PR_CALLBACK FileInfo(PRFileDesc *fd, PRFileInfo *info)
+{
+ PRInt32 rv;
+
+ rv = _PR_MD_GETOPENFILEINFO(fd, info);
+ if (rv < 0) {
+ return PR_FAILURE;
+ } else
+ return PR_SUCCESS;
+}
+
+static PRStatus PR_CALLBACK FileInfo64(PRFileDesc *fd, PRFileInfo64 *info)
+{
+ /* $$$$ NOT YET IMPLEMENTED */
+ PRInt32 rv;
+
+ rv = _PR_MD_GETOPENFILEINFO64(fd, info);
+ if (rv < 0) return PR_FAILURE;
+ else return PR_SUCCESS;
+}
+
+static PRStatus PR_CALLBACK FileSync(PRFileDesc *fd)
+{
+ PRInt32 result;
+ result = _PR_MD_FSYNC(fd);
+ if (result < 0) {
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+static PRStatus PR_CALLBACK FileClose(PRFileDesc *fd)
+{
+ PRInt32 rv;
+
+ if (!fd || fd->secret->state != _PR_FILEDESC_OPEN) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ fd->secret->state = _PR_FILEDESC_CLOSED;
+
+ rv = _PR_MD_CLOSE_FILE(fd->secret->md.osfd);
+ PR_FreeFileDesc(fd);
+ if (rv < 0) {
+ return PR_FAILURE;
+ } else {
+ return PR_SUCCESS;
+ }
+}
+
+PRIOMethods _pr_fileMethods = {
+ PR_DESC_FILE,
+ FileClose,
+ FileRead,
+ FileWrite,
+ FileAvailable,
+ FileAvailable64,
+ FileSync,
+ FileSeek,
+ FileSeek64,
+ FileInfo,
+ FileInfo64,
+ (PRWritevFN)_PR_InvalidInt,
+ (PRConnectFN)_PR_InvalidStatus,
+ (PRAcceptFN)_PR_InvalidDesc,
+ (PRBindFN)_PR_InvalidStatus,
+ (PRListenFN)_PR_InvalidStatus,
+ (PRShutdownFN)_PR_InvalidStatus,
+ (PRRecvFN)_PR_InvalidInt,
+ (PRSendFN)_PR_InvalidInt,
+ (PRRecvfromFN)_PR_InvalidInt,
+ (PRSendtoFN)_PR_InvalidInt,
+ (PRPollFN)0,
+ (PRAcceptreadFN)_PR_InvalidInt,
+ (PRTransmitfileFN)_PR_InvalidInt,
+ (PRGetsocknameFN)_PR_InvalidStatus,
+ (PRGetpeernameFN)_PR_InvalidStatus,
+ (PRGetsockoptFN)_PR_InvalidStatus,
+ (PRSetsockoptFN)_PR_InvalidStatus,
+ (PRGetsocketoptionFN)_PR_InvalidStatus,
+ (PRSetsocketoptionFN)_PR_InvalidStatus,
+};
+
+PR_IMPLEMENT(PRIOMethods*) PR_GetFileMethods(void)
+{
+ return &_pr_fileMethods;
+}
+
+PR_IMPLEMENT(PRFileDesc*) PR_Open(const char *name, PRIntn flags, PRIntn mode)
+{
+ PRInt32 osfd;
+ PRFileDesc *fd = 0;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ /* Map pr open flags and mode to os specific flags */
+
+ osfd = _PR_MD_OPEN(name, flags, mode);
+ if (osfd != -1) {
+ fd = PR_AllocFileDesc(osfd, &_pr_fileMethods);
+ if (!fd) {
+ (void) _PR_MD_CLOSE_FILE(osfd);
+ }
+ }
+ return fd;
+}
+
+PRInt32 PR_GetSysfdTableMax(void)
+{
+#if defined(XP_UNIX) && !defined(AIX)
+ struct rlimit rlim;
+
+ if ( getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
+ /* XXX need to call PR_SetError() */
+ return -1;
+ }
+
+ return rlim.rlim_max;
+#elif defined(AIX)
+ return sysconf(_SC_OPEN_MAX);
+#elif defined(WIN32) || defined(OS2)
+ /*
+ * There is a systemwide limit of 65536 user handles.
+ * Not sure on OS/2, but sounds good.
+ */
+ return 16384;
+#elif defined (WIN16)
+ return FOPEN_MAX;
+#elif defined (XP_MAC)
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return -1;
+#else
+ write me;
+#endif
+}
+
+PRInt32 PR_SetSysfdTableSize(int table_size)
+{
+#if defined(XP_UNIX) && !defined(AIX)
+ struct rlimit rlim;
+ PRInt32 tableMax = PR_GetSysfdTableMax();
+
+ if (tableMax < 0)
+ return -1;
+
+ if (tableMax > FD_SETSIZE)
+ tableMax = FD_SETSIZE;
+
+ rlim.rlim_max = tableMax;
+
+ /* Grow as much as we can; even if too big */
+ if ( rlim.rlim_max < table_size )
+ rlim.rlim_cur = rlim.rlim_max;
+ else
+ rlim.rlim_cur = table_size;
+
+ if ( setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
+ /* XXX need to call PR_SetError() */
+ return -1;
+ }
+
+ return rlim.rlim_cur;
+#elif defined(AIX) || defined(WIN32) || defined(WIN16) || defined(OS2)
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return -1;
+#elif defined (XP_MAC)
+#pragma unused (table_size)
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return -1;
+#else
+ write me;
+#endif
+}
+
+PR_IMPLEMENT(PRStatus) PR_Delete(const char *name)
+{
+ PRInt32 rv;
+
+ rv = _PR_MD_DELETE(name);
+ if (rv < 0) {
+ return PR_FAILURE;
+ } else
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetFileInfo(const char *fn, PRFileInfo *info)
+{
+ PRInt32 rv;
+
+ rv = _PR_MD_GETFILEINFO(fn, info);
+ if (rv < 0) {
+ return PR_FAILURE;
+ } else
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetFileInfo64(const char *fn, PRFileInfo64 *info)
+{
+ PRInt32 rv;
+
+ rv = _PR_MD_GETFILEINFO64(fn, info);
+ if (rv < 0) {
+ return PR_FAILURE;
+ } else {
+ return PR_SUCCESS;
+ }
+}
+
+PR_IMPLEMENT(PRStatus) PR_Rename(const char *from, const char *to)
+{
+ PRInt32 rv;
+
+ rv = _PR_MD_RENAME(from, to);
+ if (rv < 0) {
+ return PR_FAILURE;
+ } else
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus) PR_Access(const char *name, PRAccessHow how)
+{
+PRInt32 rv;
+
+ rv = _PR_MD_ACCESS(name, how);
+ if (rv < 0) {
+ return PR_FAILURE;
+ } else
+ return PR_SUCCESS;
+}
+
+/*
+** Import an existing OS file to NSPR
+*/
+PR_IMPLEMENT(PRFileDesc*) PR_ImportFile(PRInt32 osfd)
+{
+ PRFileDesc *fd = NULL;
+
+ fd = PR_AllocFileDesc(osfd, &_pr_fileMethods);
+ if( !fd ) {
+ (void) _PR_MD_CLOSE_FILE(osfd);
+ }
+
+ return fd;
+}
+
+#ifndef NO_NSPR_10_SUPPORT
+/*
+** PR_Stat() for Win16 is defined in w16io.c
+** it is a hack to circumvent problems in Gromit and Java
+** See also: BugSplat: 98516.
+*/
+#if !defined(WIN16)
+/*
+ * This function is supposed to be for backward compatibility with
+ * nspr 1.0. Therefore, it still uses the nspr 1.0 error-reporting
+ * mechanism -- returns a PRInt32, which is the error code when the call
+ * fails.
+ *
+ * If we need this function in nspr 2.0, it should be changed to
+ * return PRStatus, as follows:
+ *
+ * PR_IMPLEMENT(PRStatus) PR_Stat(const char *name, struct stat *buf)
+ * {
+ * PRInt32 rv;
+ *
+ * rv = _PR_MD_STAT(name, buf);
+ * if (rv < 0)
+ * return PR_FAILURE;
+ * else
+ * return PR_SUCCESS;
+ * }
+ *
+ * -- wtc, 2/14/97.
+ */
+PR_IMPLEMENT(PRInt32) PR_Stat(const char *name, struct stat *buf)
+{
+ PRInt32 rv;
+
+ rv = _PR_MD_STAT(name, buf);
+ return rv;
+}
+
+#endif /* !defined(WIN16) */
+#endif /* ! NO_NSPR_10_SUPPORT */
+
+PR_IMPLEMENT(PRStatus) PR_LockFile(PRFileDesc *fd)
+{
+ PRStatus rv = PR_SUCCESS;
+
+ PR_Lock(_pr_flock_lock);
+ if (fd->secret->lockCount == 0) {
+ rv = _PR_MD_LOCKFILE(fd->secret->md.osfd);
+ if (rv == PR_SUCCESS)
+ fd->secret->lockCount = 1;
+ } else {
+ fd->secret->lockCount++;
+ }
+ PR_Unlock(_pr_flock_lock);
+
+ return rv;
+}
+
+PR_IMPLEMENT(PRStatus) PR_TLockFile(PRFileDesc *fd)
+{
+ PRStatus rv = PR_SUCCESS;
+
+ PR_Lock(_pr_flock_lock);
+ if (fd->secret->lockCount == 0) {
+ rv = _PR_MD_TLOCKFILE(fd->secret->md.osfd);
+ PR_ASSERT(rv == PR_SUCCESS || fd->secret->lockCount == 0);
+ if (rv == PR_SUCCESS)
+ fd->secret->lockCount = 1;
+ } else {
+ fd->secret->lockCount++;
+ }
+ PR_Unlock(_pr_flock_lock);
+
+ return rv;
+}
+
+PR_IMPLEMENT(PRStatus) PR_UnlockFile(PRFileDesc *fd)
+{
+ PRStatus rv = PR_SUCCESS;
+
+ PR_Lock(_pr_flock_lock);
+ if (fd->secret->lockCount == 1) {
+ rv = _PR_MD_UNLOCKFILE(fd->secret->md.osfd);
+ if (rv == PR_SUCCESS)
+ fd->secret->lockCount = 0;
+ } else {
+ fd->secret->lockCount--;
+ }
+ PR_Unlock(_pr_flock_lock);
+
+ return rv;
+}
+
+PR_IMPLEMENT(PRStatus) PR_CreatePipe(
+ PRFileDesc **readPipe,
+ PRFileDesc **writePipe
+)
+{
+#if defined(XP_MAC)
+#pragma unused (readPipe, writePipe)
+#endif
+
+#ifdef WIN32
+ HANDLE readEnd, writeEnd;
+ SECURITY_ATTRIBUTES pipeAttributes;
+
+ ZeroMemory(&pipeAttributes, sizeof(pipeAttributes));
+ pipeAttributes.nLength = sizeof(pipeAttributes);
+ pipeAttributes.bInheritHandle = TRUE;
+ if (CreatePipe(&readEnd, &writeEnd, &pipeAttributes, 0) == 0) {
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ return PR_FAILURE;
+ }
+ *readPipe = PR_AllocFileDesc((PRInt32)readEnd, &_pr_fileMethods);
+ if (NULL == *readPipe) {
+ CloseHandle(readEnd);
+ CloseHandle(writeEnd);
+ return PR_FAILURE;
+ }
+ *writePipe = PR_AllocFileDesc((PRInt32)writeEnd, &_pr_fileMethods);
+ if (NULL == *writePipe) {
+ PR_Close(*readPipe);
+ CloseHandle(writeEnd);
+ return PR_FAILURE;
+ }
+#ifdef WINNT
+ (*readPipe)->secret->md.nonoverlapped = PR_TRUE;
+ (*writePipe)->secret->md.nonoverlapped = PR_TRUE;
+#endif
+ return PR_SUCCESS;
+#elif defined(XP_UNIX)
+ int pipefd[2];
+
+ if (pipe(pipefd) == -1) {
+ /* XXX map pipe error */
+ PR_SetError(PR_UNKNOWN_ERROR, errno);
+ return PR_FAILURE;
+ }
+ *readPipe = PR_AllocFileDesc(pipefd[0], &_pr_fileMethods);
+ if (NULL == *readPipe) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return PR_FAILURE;
+ }
+ *writePipe = PR_AllocFileDesc(pipefd[1], &_pr_fileMethods);
+ if (NULL == *writePipe) {
+ PR_Close(*readPipe);
+ close(pipefd[1]);
+ return PR_FAILURE;
+ }
+ _MD_MakeNonblock(*readPipe);
+ _MD_MakeNonblock(*writePipe);
+ return PR_SUCCESS;
+#else
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+#endif
+}
diff --git a/pr/src/io/prio.c b/pr/src/io/prio.c
new file mode 100644
index 00000000..123b3854
--- /dev/null
+++ b/pr/src/io/prio.c
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+#include <string.h> /* for memset() */
+
+
+/************************************************************************/
+
+PRLock *_pr_flock_lock;
+
+PRFileDesc *_pr_filedesc_freelist;
+PRLock *_pr_filedesc_freelist_lock;
+
+void _PR_InitIO(void)
+{
+ PRIOMethods *methods = PR_GetFileMethods();
+
+ _pr_filedesc_freelist = NULL;
+ _pr_filedesc_freelist_lock = PR_NewLock();
+ _pr_flock_lock = PR_NewLock();
+
+#ifdef WIN32
+ _pr_stdin = PR_AllocFileDesc((PRInt32)GetStdHandle(STD_INPUT_HANDLE),
+ methods);
+ _pr_stdout = PR_AllocFileDesc((PRInt32)GetStdHandle(STD_OUTPUT_HANDLE),
+ methods);
+ _pr_stderr = PR_AllocFileDesc((PRInt32)GetStdHandle(STD_ERROR_HANDLE),
+ methods);
+#ifdef WINNT
+ _pr_stdin->secret->md.nonoverlapped = PR_TRUE;
+ _pr_stdout->secret->md.nonoverlapped = PR_TRUE;
+ _pr_stderr->secret->md.nonoverlapped = PR_TRUE;
+#endif
+#else
+ _pr_stdin = PR_AllocFileDesc(0, methods);
+ _pr_stdout = PR_AllocFileDesc(1, methods);
+ _pr_stderr = PR_AllocFileDesc(2, methods);
+#endif
+
+ _PR_MD_INIT_IO();
+}
+
+PR_IMPLEMENT(PRFileDesc*) PR_GetSpecialFD(PRSpecialFD osfd)
+{
+ PRFileDesc *result = NULL;
+ PR_ASSERT(osfd >= PR_StandardInput && osfd <= PR_StandardError);
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ switch (osfd)
+ {
+ case PR_StandardInput: result = _pr_stdin; break;
+ case PR_StandardOutput: result = _pr_stdout; break;
+ case PR_StandardError: result = _pr_stderr; break;
+ default:
+ (void)PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ }
+ return result;
+}
+
+PR_IMPLEMENT(PRFileDesc*) PR_AllocFileDesc(PRInt32 osfd, PRIOMethods *methods)
+{
+ PRFileDesc *fd;
+
+#ifdef XP_UNIX
+ /*
+ * Assert that the file descriptor is small enough to fit in the
+ * fd_set passed to select
+ */
+ PR_ASSERT(osfd < FD_SETSIZE);
+#endif
+ if (_pr_filedesc_freelist) {
+ PR_Lock(_pr_filedesc_freelist_lock);
+ if (_pr_filedesc_freelist) {
+ PRFilePrivate *secretSave;
+ fd = _pr_filedesc_freelist;
+ _pr_filedesc_freelist = _pr_filedesc_freelist->secret->next;
+ PR_Unlock(_pr_filedesc_freelist_lock);
+ secretSave = fd->secret;
+ memset(fd, 0, sizeof(PRFileDesc));
+ memset(secretSave, 0, sizeof(PRFilePrivate));
+ fd->secret = secretSave;
+ } else {
+ PR_Unlock(_pr_filedesc_freelist_lock);
+ fd = PR_NEWZAP(PRFileDesc);
+ fd->secret = PR_NEWZAP(PRFilePrivate);
+ }
+ } else {
+ fd = PR_NEWZAP(PRFileDesc);
+ fd->secret = PR_NEWZAP(PRFilePrivate);
+ }
+ if (fd) {
+ /* Initialize the members of PRFileDesc and PRFilePrivate */
+ fd->methods = methods;
+ fd->secret->state = _PR_FILEDESC_OPEN;
+ fd->secret->md.osfd = osfd;
+ } else {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ }
+
+ return fd;
+}
+
+PR_IMPLEMENT(void) PR_FreeFileDesc(PRFileDesc *fd)
+{
+ PR_ASSERT(fd);
+ PR_ASSERT(fd->secret->state == _PR_FILEDESC_CLOSED);
+
+ fd->secret->state = _PR_FILEDESC_FREED;
+
+ PR_Lock(_pr_filedesc_freelist_lock);
+
+ /* Add to head of list- this is a LIFO structure to avoid constantly
+ * using different structs
+ */
+ fd->secret->next = _pr_filedesc_freelist;
+ _pr_filedesc_freelist = fd;
+
+ PR_Unlock(_pr_filedesc_freelist_lock);
+}
+
+#ifdef XP_UNIX
+#include <fcntl.h> /* to pick up F_GETFL */
+#endif
+
+/*
+** Wait for some i/o to finish on one or more more poll descriptors.
+*/
+PR_IMPLEMENT(PRInt32) PR_Poll(PRPollDesc *pds, PRIntn npds,
+ PRIntervalTime timeout)
+{
+ PRPollDesc *pd, *epd;
+ PRInt32 n;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ /*
+ ** Before we do the poll operation, check each descriptor and see if
+ ** it needs special poll handling. If it does, use the method poll
+ ** proc to check for data before blocking.
+ */
+ pd = pds;
+ n = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ PRFileDesc *fd = pd->fd;
+ PRInt16 in_flags;
+ if (NULL == fd) {
+ continue;
+ }
+ in_flags = pd->in_flags;
+ if (in_flags && fd->methods->poll) {
+ PRInt16 out_flags = (*fd->methods->poll)(fd, in_flags);
+ if (out_flags) {
+ pd->out_flags = out_flags;
+ n++;
+ }
+ }
+ }
+ if (n != 0) {
+ return n;
+ }
+ return(_PR_MD_PR_POLL(pds, npds, timeout));
+}
diff --git a/pr/src/io/priometh.c b/pr/src/io/priometh.c
new file mode 100644
index 00000000..a65b5c53
--- /dev/null
+++ b/pr/src/io/priometh.c
@@ -0,0 +1,232 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+/*
+ * An invalid method that returns an integer
+ */
+
+PRIntn _PR_InvalidInt()
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return -1;
+} /* _PR_InvalidInt */
+
+PRInt64 _PR_InvalidInt64()
+{
+ PRInt64 rv = LL_INIT( 0xffffffff, 0xffffffff );
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return rv;
+} /* _PR_InvalidInt */
+
+/*
+ * An invalid method that returns PRStatus
+ */
+
+PRStatus _PR_InvalidStatus()
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return PR_FAILURE;
+} /* _PR_InvalidDesc */
+
+/*
+ * An invalid method that returns a pointer
+ */
+
+PRFileDesc *_PR_InvalidDesc()
+{
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return NULL;
+} /* _PR_InvalidDesc */
+
+PR_IMPLEMENT(PRDescType) PR_GetDescType(PRFileDesc *file)
+{
+ return file->methods->file_type;
+}
+
+PR_IMPLEMENT(PRStatus) PR_Close(PRFileDesc *fd)
+{
+ return (fd->methods->close)(fd);
+}
+
+PR_IMPLEMENT(PRInt32) PR_Read(PRFileDesc *fd, void *buf, PRInt32 amount)
+{
+ return((fd->methods->read)(fd,buf,amount));
+}
+
+PR_IMPLEMENT(PRInt32) PR_Write(PRFileDesc *fd, const void *buf, PRInt32 amount)
+{
+ return((fd->methods->write)(fd,buf,amount));
+}
+
+PR_IMPLEMENT(PRInt32) PR_Seek(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence)
+{
+ return((fd->methods->seek)(fd, offset, whence));
+}
+
+PR_IMPLEMENT(PRInt64) PR_Seek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence)
+{
+ return((fd->methods->seek64)(fd, offset, whence));
+}
+
+PR_IMPLEMENT(PRInt32) PR_Available(PRFileDesc *fd)
+{
+ return((fd->methods->available)(fd));
+}
+
+PR_IMPLEMENT(PRInt64) PR_Available64(PRFileDesc *fd)
+{
+ return((fd->methods->available64)(fd));
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetOpenFileInfo(PRFileDesc *fd, PRFileInfo *info)
+{
+ return((fd->methods->fileInfo)(fd, info));
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetOpenFileInfo64(PRFileDesc *fd, PRFileInfo64 *info)
+{
+ return((fd->methods->fileInfo64)(fd, info));
+}
+
+PR_IMPLEMENT(PRStatus) PR_Sync(PRFileDesc *fd)
+{
+ return((fd->methods->fsync)(fd));
+}
+
+PR_IMPLEMENT(PRStatus) PR_Connect(
+ PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
+{
+ return((fd->methods->connect)(fd,addr,timeout));
+}
+
+PR_IMPLEMENT(PRFileDesc*) PR_Accept(PRFileDesc *fd, PRNetAddr *addr,
+PRIntervalTime timeout)
+{
+ return((fd->methods->accept)(fd,addr,timeout));
+}
+
+PR_IMPLEMENT(PRStatus) PR_Bind(PRFileDesc *fd, const PRNetAddr *addr)
+{
+ return((fd->methods->bind)(fd,addr));
+}
+
+PR_IMPLEMENT(PRStatus) PR_Shutdown(PRFileDesc *fd, PRShutdownHow how)
+{
+ return((fd->methods->shutdown)(fd,how));
+}
+
+PR_IMPLEMENT(PRStatus) PR_Listen(PRFileDesc *fd, PRIntn backlog)
+{
+ return((fd->methods->listen)(fd,backlog));
+}
+
+PR_IMPLEMENT(PRInt32) PR_Recv(PRFileDesc *fd, void *buf, PRInt32 amount,
+PRIntn flags, PRIntervalTime timeout)
+{
+ return((fd->methods->recv)(fd,buf,amount,flags,timeout));
+}
+
+PR_IMPLEMENT(PRInt32) PR_Send(PRFileDesc *fd, const void *buf, PRInt32 amount,
+PRIntn flags, PRIntervalTime timeout)
+{
+ return((fd->methods->send)(fd,buf,amount,flags,timeout));
+}
+
+PR_IMPLEMENT(PRInt32) PR_Writev(PRFileDesc *fd, PRIOVec *iov,PRInt32 iov_size,
+PRIntervalTime timeout)
+{
+ if (iov_size > PR_MAX_IOVECTOR_SIZE)
+ {
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
+ return -1;
+ }
+ return((fd->methods->writev)(fd,iov,iov_size,timeout));
+}
+
+PR_IMPLEMENT(PRInt32) PR_RecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount,
+PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout)
+{
+ return((fd->methods->recvfrom)(fd,buf,amount,flags,addr,timeout));
+}
+
+PR_IMPLEMENT(PRInt32) PR_SendTo(
+ PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout)
+{
+ return((fd->methods->sendto)(fd,buf,amount,flags,addr,timeout));
+}
+
+PR_IMPLEMENT(PRInt32) PR_TransmitFile(
+ PRFileDesc *sd, PRFileDesc *fd, const void *hdr, PRInt32 hlen,
+ PRTransmitFileFlags flags, PRIntervalTime timeout)
+{
+ return((sd->methods->transmitfile)(sd,fd,hdr,hlen,flags,timeout));
+}
+
+PR_IMPLEMENT(PRInt32) PR_AcceptRead(
+ PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr,
+ void *buf, PRInt32 amount, PRIntervalTime timeout)
+{
+ return((sd->methods->acceptread)(sd, nd, raddr, buf, amount,timeout));
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetSockName(PRFileDesc *fd, PRNetAddr *addr)
+{
+ return((fd->methods->getsockname)(fd,addr));
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetPeerName(PRFileDesc *fd, PRNetAddr *addr)
+{
+ return((fd->methods->getpeername)(fd,addr));
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetSockOpt(
+ PRFileDesc *fd, PRSockOption optname, void* optval, PRInt32* optlen)
+{
+#if defined(DEBUG)
+ static PRBool warn = PR_TRUE;
+ if (warn) warn = _PR_Obsolete("PR_GetSockOpt()", "PR_GetSocketOption()");
+#endif
+ return((fd->methods->getsockopt)(fd, optname, optval, optlen));
+}
+
+PR_IMPLEMENT(PRStatus) PR_SetSockOpt(
+ PRFileDesc *fd, PRSockOption optname, const void* optval, PRInt32 optlen)
+{
+#if defined(DEBUG)
+ static PRBool warn = PR_TRUE;
+ if (warn) warn = _PR_Obsolete("PR_SetSockOpt()", "PR_SetSocketOption()");
+#endif
+ return((fd->methods->setsockopt)(fd, optname, optval, optlen));
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetSocketOption(
+ PRFileDesc *fd, PRSocketOptionData *data)
+{
+ return((fd->methods->getsocketoption)(fd, data));
+}
+
+PR_IMPLEMENT(PRStatus) PR_SetSocketOption(
+ PRFileDesc *fd, const PRSocketOptionData *data)
+{
+ return((fd->methods->setsocketoption)(fd, data));
+}
+
+/* priometh.c */
diff --git a/pr/src/io/pripv6.c b/pr/src/io/pripv6.c
new file mode 100644
index 00000000..b2685e56
--- /dev/null
+++ b/pr/src/io/pripv6.c
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+** File: pripv6.c
+** Description: Support for various functions unique to IPv6
+*/
+
+
+/* pripv6.c */
+
diff --git a/pr/src/io/prlayer.c b/pr/src/io/prlayer.c
new file mode 100644
index 00000000..d70a935e
--- /dev/null
+++ b/pr/src/io/prlayer.c
@@ -0,0 +1,574 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+** File: prlayer.c
+** Description: Routines for handling pushable protocol modules on sockets.
+*/
+
+#include "primpl.h"
+#include "prerror.h"
+#include "prmem.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prio.h"
+
+#include <string.h> /* for memset() */
+
+void PR_CALLBACK pl_FDDestructor(PRFileDesc *fd)
+{
+ PR_ASSERT(fd != NULL);
+ if (NULL != fd->lower) fd->lower->higher = fd->higher;
+ if (NULL != fd->higher) fd->higher->lower = fd->lower;
+ PR_DELETE(fd);
+}
+
+/*
+** Default methods that just call down to the next fd.
+*/
+static PRStatus PR_CALLBACK pl_TopClose (PRFileDesc *fd)
+{
+ PRStatus status;
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+ PR_ASSERT(fd->secret == NULL);
+ PR_ASSERT(fd->methods->file_type == PR_DESC_LAYERED);
+
+ status = (fd->lower->methods->close)(fd->lower);
+
+ fd->dtor(fd);
+ return status;
+}
+
+static PRInt32 PR_CALLBACK pl_DefRead (PRFileDesc *fd, void *buf, PRInt32 amount)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->read)(fd->lower, buf, amount);
+}
+
+static PRInt32 PR_CALLBACK pl_DefWrite (
+ PRFileDesc *fd, const void *buf, PRInt32 amount)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->write)(fd->lower, buf, amount);
+}
+
+static PRInt32 PR_CALLBACK pl_DefAvailable (PRFileDesc *fd)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->available)(fd->lower);
+}
+
+static PRInt64 PR_CALLBACK pl_DefAvailable64 (PRFileDesc *fd)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->available64)(fd->lower);
+}
+
+static PRStatus PR_CALLBACK pl_DefFsync (PRFileDesc *fd)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->fsync)(fd->lower);
+}
+
+static PRInt32 PR_CALLBACK pl_DefSeek (
+ PRFileDesc *fd, PRInt32 offset, PRSeekWhence how)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->seek)(fd->lower, offset, how);
+}
+
+static PRInt64 PR_CALLBACK pl_DefSeek64 (
+ PRFileDesc *fd, PRInt64 offset, PRSeekWhence how)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->seek64)(fd->lower, offset, how);
+}
+
+static PRStatus PR_CALLBACK pl_DefFileInfo (PRFileDesc *fd, PRFileInfo *info)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->fileInfo)(fd->lower, info);
+}
+
+static PRStatus PR_CALLBACK pl_DefFileInfo64 (PRFileDesc *fd, PRFileInfo64 *info)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->fileInfo64)(fd->lower, info);
+}
+
+static PRInt32 PR_CALLBACK pl_DefWritev (PRFileDesc *fd, PRIOVec *iov, PRInt32 size,
+ PRIntervalTime timeout)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->writev)(fd->lower, iov, size, timeout);
+}
+
+static PRStatus PR_CALLBACK pl_DefConnect (
+ PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->connect)(fd->lower, addr, timeout);
+}
+
+static PRFileDesc * PR_CALLBACK pl_TopAccept (
+ PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PRFileDesc *newfd;
+
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ newfd = (fd->lower->methods->accept)(fd->lower, addr, timeout);
+ if (newfd != NULL)
+ {
+ if (PR_FAILURE == PR_PushIOLayer(fd, fd->identity, newfd))
+ {
+ PR_Close(newfd);
+ newfd = NULL;
+ }
+ }
+ return newfd;
+}
+
+static PRStatus PR_CALLBACK pl_DefBind (PRFileDesc *fd, const PRNetAddr *addr)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->bind)(fd->lower, addr);
+}
+
+static PRStatus PR_CALLBACK pl_DefListen (PRFileDesc *fd, PRIntn backlog)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->listen)(fd->lower, backlog);
+}
+
+static PRStatus PR_CALLBACK pl_DefShutdown (PRFileDesc *fd, PRIntn how)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->shutdown)(fd->lower, how);
+}
+
+static PRInt32 PR_CALLBACK pl_DefRecv (
+ PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->recv)(
+ fd->lower, buf, amount, flags, timeout);
+}
+
+static PRInt32 PR_CALLBACK pl_DefSend (
+ PRFileDesc *fd, const void *buf,
+ PRInt32 amount, PRIntn flags, PRIntervalTime timeout)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->send)(fd->lower, buf, amount, flags, timeout);
+}
+
+static PRInt32 PR_CALLBACK pl_DefRecvfrom (
+ PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->recvfrom)(
+ fd->lower, buf, amount, flags, addr, timeout);
+}
+
+static PRInt32 PR_CALLBACK pl_DefSendto (
+ PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ const PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->sendto)(
+ fd->lower, buf, amount, flags, addr, timeout);
+}
+
+static PRInt16 PR_CALLBACK pl_DefPoll (PRFileDesc *fd, PRInt16 how_flags)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->poll)(fd->lower, how_flags);
+}
+
+static PRInt32 PR_CALLBACK pl_DefAcceptread (
+ PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, void *buf,
+ PRInt32 amount, PRIntervalTime t)
+{
+ PR_ASSERT(sd != NULL);
+ PR_ASSERT(sd->lower != NULL);
+
+ return sd->lower->methods->acceptread(sd->lower, nd, raddr, buf, amount, t);
+}
+
+static PRInt32 PR_CALLBACK pl_DefTransmitfile (
+ PRFileDesc *sd, PRFileDesc *fd, const void *headers, PRInt32 hlen,
+ PRTransmitFileFlags flags, PRIntervalTime t)
+{
+ PR_ASSERT(sd != NULL);
+ PR_ASSERT(sd->lower != NULL);
+
+ return sd->lower->methods->transmitfile(
+ sd->lower, fd, headers, hlen, flags, t);
+}
+
+static PRStatus PR_CALLBACK pl_DefGetsockname (PRFileDesc *fd, PRNetAddr *addr)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->getsockname)(fd->lower, addr);
+}
+
+static PRStatus PR_CALLBACK pl_DefGetpeername (PRFileDesc *fd, PRNetAddr *addr)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->getpeername)(fd->lower, addr);
+}
+
+static PRStatus PR_CALLBACK pl_DefGetsockopt (
+ PRFileDesc *fd, PRSockOption optname, void* optval, PRInt32* optlen)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->getsockopt)(fd->lower, optname, optval, optlen);
+}
+
+static PRStatus PR_CALLBACK pl_DefSetsockopt (
+ PRFileDesc *fd, PRSockOption optname, const void* optval, PRInt32 optlen)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->setsockopt)(fd->lower, optname, optval, optlen);
+}
+
+static PRStatus PR_CALLBACK pl_DefGetsocketoption (
+ PRFileDesc *fd, PRSocketOptionData *data)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->getsocketoption)(fd->lower, data);
+}
+
+static PRStatus PR_CALLBACK pl_DefSetsocketoption (
+ PRFileDesc *fd, const PRSocketOptionData *data)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->setsocketoption)(fd->lower, data);
+}
+
+/* Methods for the top of the stack. Just call down to the next fd. */
+static struct PRIOMethods pl_methods = {
+ PR_DESC_LAYERED,
+ pl_TopClose,
+ pl_DefRead,
+ pl_DefWrite,
+ pl_DefAvailable,
+ pl_DefAvailable64,
+ pl_DefFsync,
+ pl_DefSeek,
+ pl_DefSeek64,
+ pl_DefFileInfo,
+ pl_DefFileInfo64,
+ pl_DefWritev,
+ pl_DefConnect,
+ pl_TopAccept,
+ pl_DefBind,
+ pl_DefListen,
+ pl_DefShutdown,
+ pl_DefRecv,
+ pl_DefSend,
+ pl_DefRecvfrom,
+ pl_DefSendto,
+ pl_DefPoll,
+ pl_DefAcceptread,
+ pl_DefTransmitfile,
+ pl_DefGetsockname,
+ pl_DefGetpeername,
+ pl_DefGetsockopt,
+ pl_DefSetsockopt,
+ pl_DefGetsocketoption,
+ pl_DefSetsocketoption
+};
+
+PR_IMPLEMENT(PRIOMethods const*) PR_GetDefaultIOMethods()
+{
+ return &pl_methods;
+} /* PR_GetDefaultIOMethods */
+
+PR_IMPLEMENT(PRFileDesc*) PR_CreateIOLayerStub(
+ PRDescIdentity ident, PRIOMethods const *methods)
+{
+ PRFileDesc *fd = NULL;
+ PR_ASSERT((PR_NSPR_IO_LAYER != ident) && (PR_TOP_IO_LAYER != ident));
+ if ((PR_NSPR_IO_LAYER == ident) || (PR_TOP_IO_LAYER == ident))
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ else
+ {
+ fd = PR_NEWZAP(PRFileDesc);
+ if (NULL == fd)
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ else
+ {
+ fd->methods = methods;
+ fd->dtor = pl_FDDestructor;
+ fd->identity = ident;
+ }
+ }
+ return fd;
+} /* PR_CreateIOLayerStub */
+
+PR_IMPLEMENT(PRStatus) PR_PushIOLayer(
+ PRFileDesc *stack, PRDescIdentity id, PRFileDesc *fd)
+{
+ PRFileDesc *insert = PR_GetIdentitiesLayer(stack, id);
+
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(stack != NULL);
+ PR_ASSERT(insert != NULL);
+ if ((NULL == stack) || (NULL == fd) || (NULL == insert))
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ if (stack == insert)
+ {
+ /* going on top of the stack */
+ PRFileDesc copy = *stack;
+ *stack = *fd;
+ *fd = copy;
+ fd->higher = stack;
+ stack->lower = fd;
+ }
+ else
+ {
+ /* going somewhere in the middle of the stack */
+ fd->lower = insert;
+ fd->higher = insert->higher;
+
+ insert->higher = fd;
+ insert->higher->lower = fd;
+ }
+
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRFileDesc*) PR_PopIOLayer(PRFileDesc *stack, PRDescIdentity id)
+{
+ PRFileDesc *extract = PR_GetIdentitiesLayer(stack, id);
+
+ PR_ASSERT(0 != id);
+ PR_ASSERT(NULL != stack);
+ PR_ASSERT(NULL != extract);
+ if ((NULL == stack) || (0 == id) || (NULL == extract))
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+ }
+
+ if (extract == stack)
+ {
+ /* popping top layer of the stack */
+ PRFileDesc copy = *stack;
+ extract = stack->lower;
+ *stack = *extract;
+ *extract = copy;
+ stack->higher = NULL;
+ }
+ else
+ {
+ extract->lower->higher = extract->higher;
+ extract->higher->lower = extract->lower;
+ }
+ extract->higher = extract->lower = NULL;
+ return extract;
+} /* PR_PopIOLayer */
+
+#define ID_CACHE_INCREMENT 16
+typedef struct _PRIdentity_cache
+{
+ PRLock *ml;
+ char **name;
+ PRIntn length;
+ PRDescIdentity ident;
+} _PRIdentity_cache;
+
+static _PRIdentity_cache identity_cache;
+
+PR_IMPLEMENT(PRDescIdentity) PR_GetUniqueIdentity(const char *layer_name)
+{
+ PRDescIdentity identity, length;
+ char **names = NULL, *name = NULL, **old = NULL;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ PR_ASSERT((PRDescIdentity)0x7fff > identity_cache.ident);
+
+ if (NULL != layer_name)
+ {
+ name = (char*)PR_Malloc(strlen(layer_name) + 1);
+ if (NULL == name)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return PR_INVALID_IO_LAYER;
+ }
+ strcpy(name, layer_name);
+ }
+
+ /* this initial code runs unsafe */
+retry:
+ PR_ASSERT(NULL == names);
+ length = identity_cache.length;
+ if (length < (identity_cache.ident + 1))
+ {
+ length += ID_CACHE_INCREMENT;
+ names = (char**)PR_CALLOC(length * sizeof(char*));
+ if (NULL == names)
+ {
+ if (NULL != name) PR_DELETE(name);
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return PR_INVALID_IO_LAYER;
+ }
+ }
+
+ /* now we get serious about thread safety */
+ PR_Lock(identity_cache.ml);
+ PR_ASSERT(identity_cache.ident <= identity_cache.length);
+ identity = identity_cache.ident + 1;
+ if (identity > identity_cache.length) /* there's no room */
+ {
+ /* we have to do something - hopefully it's already done */
+ if ((NULL != names) && (length >= identity))
+ {
+ /* what we did is still okay */
+ memcpy(
+ names, identity_cache.name,
+ identity_cache.length * sizeof(char*));
+ old = identity_cache.name;
+ identity_cache.name = names;
+ identity_cache.length = length;
+ names = NULL;
+ }
+ else
+ {
+ PR_ASSERT(identity_cache.ident <= identity_cache.length);
+ PR_Unlock(identity_cache.ml);
+ if (NULL != names) PR_DELETE(names);
+ goto retry;
+ }
+ }
+ if (NULL != name) /* there's a name to be stored */
+ {
+ identity_cache.name[identity] = name;
+ }
+ identity_cache.ident = identity;
+ PR_ASSERT(identity_cache.ident <= identity_cache.length);
+ PR_Unlock(identity_cache.ml);
+
+ if (NULL != old) PR_DELETE(old);
+ if (NULL != names) PR_DELETE(names);
+
+ return identity;
+} /* PR_GetUniqueIdentity */
+
+PR_IMPLEMENT(const char*) PR_GetNameForIdentity(PRDescIdentity ident)
+{
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ if (PR_TOP_IO_LAYER == ident) return NULL;
+
+ PR_ASSERT(ident <= identity_cache.ident);
+ return (ident > identity_cache.ident) ? NULL : identity_cache.name[ident];
+} /* PR_GetNameForIdentity */
+
+PR_IMPLEMENT(PRDescIdentity) PR_GetLayersIdentity(PRFileDesc* fd)
+{
+ PR_ASSERT(NULL != fd);
+ return fd->identity;
+} /* PR_GetLayersIdentity */
+
+PR_IMPLEMENT(PRFileDesc*) PR_GetIdentitiesLayer(PRFileDesc* fd, PRDescIdentity id)
+{
+ PRFileDesc *layer = fd;
+
+ if (PR_TOP_IO_LAYER == id) return fd;
+
+ for (layer = fd; layer != NULL; layer = layer->lower)
+ {
+ if (id == layer->identity) return layer;
+ }
+ for (layer = fd; layer != NULL; layer = layer->higher)
+ {
+ if (id == layer->identity) return layer;
+ }
+ return NULL;
+} /* PR_GetIdentitiesLayer */
+
+void _PR_InitLayerCache()
+{
+ memset(&identity_cache, 0, sizeof(identity_cache));
+ identity_cache.ml = PR_NewLock();
+ PR_ASSERT(NULL != identity_cache.ml);
+} /* _PR_InitLayerCache */
+
+/* prlayer.c */
diff --git a/pr/src/io/prlog.c b/pr/src/io/prlog.c
new file mode 100644
index 00000000..cd1b6d4e
--- /dev/null
+++ b/pr/src/io/prlog.c
@@ -0,0 +1,418 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+#include "prenv.h"
+#include "prprf.h"
+#include <string.h>
+
+/*
+ * Lock used to lock the log.
+ *
+ * We can't define _PR_LOCK_LOG simply as PR_Lock because PR_Lock may
+ * contain assertions. We have to avoid assertions in _PR_LOCK_LOG
+ * because PR_ASSERT calls PR_LogPrint, which in turn calls _PR_LOCK_LOG.
+ * This can lead to infinite recursion.
+ */
+static PRLock *_pr_logLock;
+#if (defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY))
+#define _PR_LOCK_LOG() { _PR_LOCK_LOCK(_pr_logLock)
+#define _PR_UNLOCK_LOG() _PR_LOCK_UNLOCK(_pr_logLock); }
+#else
+
+#define _PR_LOCK_LOG() \
+{ \
+ PRIntn _is; \
+ PRThread *_me = _PR_MD_CURRENT_THREAD(); \
+ if (!_PR_IS_NATIVE_THREAD(_me)) \
+ _PR_INTSOFF(_is); \
+ _PR_LOCK_LOCK(_pr_logLock)
+
+#define _PR_UNLOCK_LOG() \
+ _PR_LOCK_UNLOCK(_pr_logLock); \
+ PR_ASSERT(_me == _PR_MD_CURRENT_THREAD()); \
+ if (!_PR_IS_NATIVE_THREAD(_me)) \
+ _PR_INTSON(_is); \
+}
+
+#endif
+
+#ifdef XP_PC
+#define strcasecmp stricmp
+#define strncasecmp strnicmp
+#endif
+
+/*
+ * On NT, we can't define _PUT_LOG as PR_Write or _PR_MD_WRITE,
+ * because every asynchronous file io operation leads to a fiber context
+ * switch. So we define _PUT_LOG as fputs (from stdio.h). A side
+ * benefit is that fputs handles the LF->CRLF translation. This
+ * code can also be used on other platforms with file stream io.
+ */
+#if defined(WIN32) || defined(XP_OS2)
+#define _PR_USE_STDIO_FOR_LOGGING
+#endif
+
+/* Macros used to reduce #ifdef pollution */
+
+#if defined(_PR_USE_STDIO_FOR_LOGGING)
+#define _PUT_LOG(fd, buf, nb) fputs(buf, fd)
+#elif defined(_PR_PTHREADS)
+#define _PUT_LOG(fd, buf, nb) PR_Write(fd, buf, nb)
+#elif defined(XP_MAC)
+#define _PUT_LOG(fd, buf, nb) _PR_MD_WRITE_SYNC(fd, buf, nb)
+#else
+#define _PUT_LOG(fd, buf, nb) _PR_MD_WRITE(fd, buf, nb)
+#endif
+
+/************************************************************************/
+
+static PRLogModuleInfo *logModules;
+
+#ifdef PR_LOGGING
+static char *logBuf = NULL;
+static char *logp;
+static char *logEndp;
+#ifdef _PR_USE_STDIO_FOR_LOGGING
+static FILE *logFile = NULL;
+#else
+static PRFileDesc *logFile = 0;
+#endif
+
+#define LINE_BUF_SIZE 200
+#define DEFAULT_BUF_SIZE 16384
+
+#ifdef _PR_NEED_STRCASECMP
+
+/*
+ * strcasecmp is defined in /usr/ucblib/libucb.a on some platforms
+ * such as NCR and Unixware. Linking with both libc and libucb
+ * may cause some problem, so I just provide our own implementation
+ * of strcasecmp here.
+ */
+
+static const unsigned char uc[] =
+{
+ '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
+ '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
+ '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
+ '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
+ ' ', '!', '"', '#', '$', '%', '&', '\'',
+ '(', ')', '*', '+', ',', '-', '.', '/',
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', ':', ';', '<', '=', '>', '?',
+ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
+ 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
+ '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
+ 'X', 'Y', 'Z', '{', '|', '}', '~', '\177'
+};
+
+PRIntn strcasecmp(const char *a, const char *b)
+{
+ const unsigned char *ua = (const unsigned char *)a;
+ const unsigned char *ub = (const unsigned char *)b;
+
+ if( ((const char *)0 == a) || (const char *)0 == b )
+ return (PRIntn)(a-b);
+
+ while( (uc[*ua] == uc[*ub]) && ('\0' != *a) )
+ {
+ a++;
+ ua++;
+ ub++;
+ }
+
+ return (PRIntn)(uc[*ua] - uc[*ub]);
+}
+
+#endif /* _PR_NEED_STRCASECMP */
+
+void _PR_InitLog(void)
+{
+ char *ev;
+
+ _pr_logLock = PR_NewLock();
+
+ ev = PR_GetEnv("NSPR_LOG_MODULES");
+ if (ev && ev[0]) {
+ char module[64];
+ PRBool isSync = PR_FALSE;
+ PRIntn evlen = strlen(ev), pos = 0;
+ PRInt32 bufSize = DEFAULT_BUF_SIZE;
+ while (pos < evlen) {
+ PRIntn level = 1, count = 0, delta = 0;
+ count = sscanf(&ev[pos], "%64[A-Za-z0-9]%n:%d%n",
+ module, &delta, &level, &delta);
+ pos += delta;
+ if (count == 0) break;
+
+ /*
+ ** If count == 2, then we got module and level. If count
+ ** == 1, then level defaults to 1 (module enabled).
+ */
+ if (strcasecmp(module, "sync") == 0) {
+ isSync = PR_TRUE;
+ } else if (strcasecmp(module, "bufsize") == 0) {
+ if (level >= LINE_BUF_SIZE) {
+ bufSize = level;
+ }
+ } else {
+ PRLogModuleInfo *lm = logModules;
+ PRBool skip_modcheck =
+ (0 == strcasecmp (module, "all")) ? PR_TRUE : PR_FALSE;
+
+ while (lm != NULL) {
+ if (skip_modcheck) lm -> level = (PRLogModuleLevel)level;
+ else if (strcasecmp(module, lm->name) == 0) {
+ lm->level = (PRLogModuleLevel)level;
+ break;
+ }
+ lm = lm->next;
+ }
+ if (NULL == lm) {
+#ifdef XP_PC
+ char* str = PR_smprintf("Unrecognized NSPR_LOG_MODULE: %s=%d\n",
+ module, level);
+ if (str) {
+ OutputDebugString(str);
+ PR_smprintf_free(str);
+ }
+#else
+ fprintf(stderr, "Unrecognized NSPR_LOG_MODULE: %s=%d\n",
+ module, level);
+#endif
+ }
+ }
+ /*found:*/
+ count = sscanf(&ev[pos], " , %n", &delta);
+ pos += delta;
+ if (count == -1) break;
+ }
+ PR_SetLogBuffering(isSync ? bufSize : 0);
+
+ ev = PR_GetEnv("NSPR_LOG_FILE");
+ if (ev && ev[0]) {
+ if (!PR_SetLogFile(ev)) {
+#ifdef XP_PC
+ char* str = PR_smprintf("Unable to create nspr log file '%s'\n", ev);
+ if (str) {
+ OutputDebugString(str);
+ PR_smprintf_free(str);
+ }
+#else
+ fprintf(stderr, "Unable to create nspr log file '%s'\n", ev);
+#endif
+ }
+ } else {
+#ifdef _PR_USE_STDIO_FOR_LOGGING
+ logFile = stderr;
+#else
+ logFile = _pr_stderr;
+#endif
+ }
+ }
+}
+
+void _PR_LogCleanup(void)
+{
+ PR_LogFlush();
+
+#ifdef _PR_USE_STDIO_FOR_LOGGING
+ if (logFile && logFile != stdout && logFile != stderr) {
+ fclose(logFile);
+ }
+#else
+ if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) {
+ PR_Close(logFile);
+ }
+#endif
+}
+
+#endif /* PR_LOGGING */
+
+PR_IMPLEMENT(PRLogModuleInfo*) PR_NewLogModule(const char *name)
+{
+ PRLogModuleInfo *lm;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ lm = PR_NEWZAP(PRLogModuleInfo);
+ if (lm) {
+ lm->name = strdup(name);
+ lm->level = PR_LOG_NONE;
+ lm->next = logModules;
+ logModules = lm;
+ }
+ return lm;
+}
+
+PR_IMPLEMENT(PRBool) PR_SetLogFile(const char *file)
+{
+#ifdef PR_LOGGING
+#ifdef _PR_USE_STDIO_FOR_LOGGING
+ FILE *newLogFile;
+
+ newLogFile = fopen(file, "w");
+ if (newLogFile) {
+ /* We do buffering ourselves. */
+ setvbuf(newLogFile, NULL, _IONBF, 0);
+ if (logFile && logFile != stdout && logFile != stderr) {
+ fclose(logFile);
+ }
+ logFile = newLogFile;
+ }
+ return (PRBool) (newLogFile != 0);
+#else
+ PRFileDesc *newLogFile;
+
+ newLogFile = PR_Open(file, PR_WRONLY|PR_CREATE_FILE, 0666);
+ if (newLogFile) {
+ if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) {
+ PR_Close(logFile);
+ }
+ logFile = newLogFile;
+#if defined(XP_MAC)
+ SetLogFileTypeCreator(file);
+#endif
+ }
+ return (PRBool) (newLogFile != 0);
+#endif /* _PR_USE_STDIO_FOR_LOGGING */
+#else /* PR_LOGGING */
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FALSE;
+#endif /* PR_LOGGING */
+}
+
+PR_IMPLEMENT(void) PR_SetLogBuffering(PRIntn buffer_size)
+{
+#ifdef PR_LOGGING
+ PR_LogFlush();
+
+ if (logBuf)
+ PR_DELETE(logBuf);
+ logBuf = 0;
+
+ if (buffer_size >= LINE_BUF_SIZE) {
+ logp = logBuf = (char*) PR_MALLOC(buffer_size);
+ logEndp = logp + buffer_size;
+ }
+#endif /* PR_LOGGING */
+}
+
+PR_IMPLEMENT(void) PR_LogPrint(const char *fmt, ...)
+{
+#ifdef PR_LOGGING
+ va_list ap;
+ char line[LINE_BUF_SIZE];
+ PRUint32 nb;
+ PRThread *me;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ if (!logFile) {
+ return;
+ }
+
+ va_start(ap, fmt);
+ me = PR_GetCurrentThread();
+ nb = PR_snprintf(line, sizeof(line)-1, "%ld[%p]: ",
+#if defined(_PR_DCETHREADS)
+ /* The problem is that for _PR_DCETHREADS, pthread_t is not a
+ * pointer, but a structure; so you can't easily print it...
+ */
+ me ? &(me->id): 0L, me);
+#else
+ me ? me->id : 0L, me);
+#endif
+
+ nb += PR_vsnprintf(line+nb, sizeof(line)-nb-1, fmt, ap);
+ if (nb && (line[nb-1] != '\n')) {
+#ifndef XP_MAC
+ line[nb++] = '\n';
+#else
+ line[nb++] = '\015';
+#endif
+ line[nb] = '\0';
+ } else {
+#ifdef XP_MAC
+ line[nb-1] = '\015';
+#endif
+ }
+ va_end(ap);
+
+ _PR_LOCK_LOG();
+ if (logBuf == 0) {
+ _PUT_LOG(logFile, line, nb);
+ } else {
+ if (logp + nb > logEndp) {
+ _PUT_LOG(logFile, logBuf, logp - logBuf);
+ logp = logBuf;
+ }
+ memcpy(logp, line, nb);
+ logp += nb;
+ }
+ _PR_UNLOCK_LOG();
+ PR_LogFlush();
+#endif /* PR_LOGGING */
+}
+
+PR_IMPLEMENT(void) PR_LogFlush(void)
+{
+#ifdef PR_LOGGING
+ if (logBuf && logFile) {
+ _PR_LOCK_LOG();
+ if (logp > logBuf) {
+ _PUT_LOG(logFile, logBuf, logp - logBuf);
+ logp = logBuf;
+ }
+ _PR_UNLOCK_LOG();
+ }
+#endif /* PR_LOGGING */
+}
+
+PR_IMPLEMENT(void) PR_Abort(void)
+{
+#ifdef PR_LOGGING
+ PR_LogPrint("Aborting");
+ abort();
+#endif /* PR_LOGGING */
+ PR_ASSERT(1);
+}
+
+PR_IMPLEMENT(void) PR_Assert(const char *s, const char *file, PRIntn ln)
+{
+#ifdef PR_LOGGING
+ PR_LogPrint("Assertion failure: %s, at %s:%d\n", s, file, ln);
+#if defined(XP_UNIX) || defined(XP_OS2)
+ fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln);
+#endif
+#ifdef XP_MAC
+ dprintf("Assertion failure: %s, at %s:%d\n", s, file, ln);
+#endif
+#ifdef WIN32
+ DebugBreak();
+#endif
+#ifndef XP_MAC
+ abort();
+#endif
+#endif /* PR_LOGGING */
+}
diff --git a/pr/src/io/prmapopt.c b/pr/src/io/prmapopt.c
new file mode 100644
index 00000000..c9ed829a
--- /dev/null
+++ b/pr/src/io/prmapopt.c
@@ -0,0 +1,408 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * This file defines _PR_MapOptionName(). The purpose of putting
+ * _PR_MapOptionName() in a separate file is to work around a Winsock
+ * header file problem on Windows NT.
+ *
+ * On Windows NT, if we define _WIN32_WINNT to be 0x0400 (in order
+ * to use Service Pack 3 extensions), windows.h includes winsock2.h
+ * (instead of winsock.h), which doesn't define many socket options
+ * defined in winsock.h.
+ *
+ * We need the socket options defined in winsock.h. So this file
+ * includes winsock.h, with _WIN32_WINNT undefined.
+ */
+
+#ifdef WINNT
+#include <winsock.h>
+#endif
+
+#include "primpl.h"
+
+#if defined(XP_UNIX) || defined(OS2)
+#include <netinet/tcp.h> /* TCP_NODELAY, TCP_MAXSEG */
+#endif
+
+/*
+ * Not every platform has all the socket options we want to
+ * support. Some older operating systems such as SunOS 4.1.3
+ * don't have the IP multicast socket options. Win32 doesn't
+ * have TCP_MAXSEG.
+ *
+ * To deal with this problem, we define the missing socket
+ * options as _PR_NO_SUCH_SOCKOPT. _PR_MapOptionName() fails with
+ * PR_OPERATION_NOT_SUPPORTED_ERROR if a socket option not
+ * available on the platform is requested.
+ */
+
+/*
+ * Sanity check. SO_LINGER and TCP_NODELAY should be available
+ * on all platforms. Just to make sure we have included the
+ * appropriate header files. Then any undefined socket options
+ * are really missing.
+ */
+
+#if !defined(SO_LINGER)
+#error "SO_LINGER is not defined"
+#endif
+
+#if !defined(TCP_NODELAY)
+#error "TCP_NODELAY is not defined"
+#endif
+
+/*
+ * Make sure the value of _PR_NO_SUCH_SOCKOPT is not
+ * a valid socket option.
+ */
+#define _PR_NO_SUCH_SOCKOPT -1
+
+#ifndef IP_MULTICAST_IF /* set/get IP multicast interface */
+#define IP_MULTICAST_IF _PR_NO_SUCH_SOCKOPT
+#endif
+
+#ifndef IP_MULTICAST_TTL /* set/get IP multicast timetolive */
+#define IP_MULTICAST_TTL _PR_NO_SUCH_SOCKOPT
+#endif
+
+#ifndef IP_MULTICAST_LOOP /* set/get IP multicast loopback */
+#define IP_MULTICAST_LOOP _PR_NO_SUCH_SOCKOPT
+#endif
+
+#ifndef IP_ADD_MEMBERSHIP /* add an IP group membership */
+#define IP_ADD_MEMBERSHIP _PR_NO_SUCH_SOCKOPT
+#endif
+
+#ifndef IP_DROP_MEMBERSHIP /* drop an IP group membership */
+#define IP_DROP_MEMBERSHIP _PR_NO_SUCH_SOCKOPT
+#endif
+
+#ifndef IP_TTL /* set/get IP Time To Live */
+#define IP_TTL _PR_NO_SUCH_SOCKOPT
+#endif
+
+#ifndef IP_TOS /* set/get IP Type Of Service */
+#define IP_TOS _PR_NO_SUCH_SOCKOPT
+#endif
+
+#ifndef TCP_MAXSEG /* maxumum segment size for tcp */
+#define TCP_MAXSEG _PR_NO_SUCH_SOCKOPT
+#endif
+
+PRStatus _PR_MapOptionName(
+ PRSockOption optname, PRInt32 *level, PRInt32 *name)
+{
+ static PRInt32 socketOptions[PR_SockOpt_Last] =
+ {
+ 0, SO_LINGER, SO_REUSEADDR, SO_KEEPALIVE, SO_RCVBUF, SO_SNDBUF,
+ IP_TTL, IP_TOS, IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP,
+ IP_MULTICAST_IF, IP_MULTICAST_TTL, IP_MULTICAST_LOOP,
+ TCP_NODELAY, TCP_MAXSEG
+ };
+ static PRInt32 socketLevels[PR_SockOpt_Last] =
+ {
+ 0, SOL_SOCKET, SOL_SOCKET, SOL_SOCKET, SOL_SOCKET, SOL_SOCKET,
+ IPPROTO_IP, IPPROTO_IP, IPPROTO_IP, IPPROTO_IP,
+ IPPROTO_IP, IPPROTO_IP, IPPROTO_IP,
+ IPPROTO_TCP, IPPROTO_TCP
+ };
+
+ if ((optname < PR_SockOpt_Linger)
+ && (optname > PR_SockOpt_MaxSegment))
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ if (socketOptions[optname] == _PR_NO_SUCH_SOCKOPT)
+ {
+ PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0);
+ return PR_FAILURE;
+ }
+ *name = socketOptions[optname];
+ *level = socketLevels[optname];
+ return PR_SUCCESS;
+} /* _PR_MapOptionName */
+
+#ifndef _PR_PTHREADS
+
+PRStatus PR_CALLBACK _PR_SocketGetSocketOption(PRFileDesc *fd, PRSocketOptionData *data)
+{
+ PRStatus rv;
+ PRInt32 length;
+ PRInt32 level, name;
+
+ /*
+ * PR_SockOpt_Nonblocking is a special case that does not
+ * translate to a getsockopt() call
+ */
+ if (PR_SockOpt_Nonblocking == data->option)
+ {
+ data->value.non_blocking = fd->secret->nonblocking;
+ return PR_SUCCESS;
+ }
+
+ rv = _PR_MapOptionName(data->option, &level, &name);
+ if (PR_SUCCESS == rv)
+ {
+ switch (data->option)
+ {
+ case PR_SockOpt_Linger:
+ {
+ struct linger linger;
+ length = sizeof(linger);
+ rv = _PR_MD_GETSOCKOPT(
+ fd, level, name, (char *) &linger, &length);
+ if (PR_SUCCESS == rv)
+ {
+ PR_ASSERT(sizeof(linger) == length);
+ data->value.linger.polarity =
+ (linger.l_onoff) ? PR_TRUE : PR_FALSE;
+ data->value.linger.linger =
+ PR_SecondsToInterval(linger.l_linger);
+ }
+ break;
+ }
+ case PR_SockOpt_Reuseaddr:
+ case PR_SockOpt_Keepalive:
+ case PR_SockOpt_NoDelay:
+ {
+#ifdef WIN32 /* Winsock */
+ BOOL value;
+#else
+ PRIntn value;
+#endif
+ length = sizeof(value);
+ rv = _PR_MD_GETSOCKOPT(
+ fd, level, name, (char*)&value, &length);
+ if (PR_SUCCESS == rv)
+ data->value.reuse_addr = (0 == value) ? PR_FALSE : PR_TRUE;
+ break;
+ }
+ case PR_SockOpt_McastLoopback:
+ {
+#ifdef WIN32 /* Winsock */
+ BOOL bool;
+#else
+ PRUint8 bool;
+#endif
+ length = sizeof(bool);
+ rv = _PR_MD_GETSOCKOPT(
+ fd, level, name, (char*)&bool, &length);
+ if (PR_SUCCESS == rv)
+ data->value.mcast_loopback = (0 == bool) ? PR_FALSE : PR_TRUE;
+ break;
+ }
+ case PR_SockOpt_RecvBufferSize:
+ case PR_SockOpt_SendBufferSize:
+ case PR_SockOpt_MaxSegment:
+ {
+ PRIntn value;
+ length = sizeof(value);
+ rv = _PR_MD_GETSOCKOPT(
+ fd, level, name, (char*)&value, &length);
+ if (PR_SUCCESS == rv)
+ data->value.recv_buffer_size = value;
+ break;
+ }
+ case PR_SockOpt_IpTimeToLive:
+ case PR_SockOpt_IpTypeOfService:
+ {
+ /* These options should really be an int (or PRIntn). */
+ length = sizeof(PRUintn);
+ rv = _PR_MD_GETSOCKOPT(
+ fd, level, name, (char*)&data->value.ip_ttl, &length);
+ break;
+ }
+ case PR_SockOpt_McastTimeToLive:
+ {
+#ifdef WIN32 /* Winsock */
+ int ttl;
+#else
+ PRUint8 ttl;
+#endif
+ length = sizeof(ttl);
+ rv = _PR_MD_GETSOCKOPT(
+ fd, level, name, (char*)&ttl, &length);
+ if (PR_SUCCESS == rv)
+ data->value.mcast_ttl = ttl;
+ break;
+ }
+ case PR_SockOpt_AddMember:
+ case PR_SockOpt_DropMember:
+ {
+ struct ip_mreq mreq;
+ length = sizeof(mreq);
+ rv = _PR_MD_GETSOCKOPT(
+ fd, level, name, (char*)&mreq, &length);
+ if (PR_SUCCESS == rv)
+ {
+ data->value.add_member.mcaddr.inet.ip =
+ mreq.imr_multiaddr.s_addr;
+ data->value.add_member.ifaddr.inet.ip =
+ mreq.imr_interface.s_addr;
+ }
+ break;
+ }
+ case PR_SockOpt_McastInterface:
+ {
+ /* This option is a struct in_addr. */
+ length = sizeof(data->value.mcast_if.inet.ip);
+ rv = _PR_MD_GETSOCKOPT(
+ fd, level, name,
+ (char*)&data->value.mcast_if.inet.ip, &length);
+ break;
+ }
+ default:
+ PR_NOT_REACHED("Unknown socket option");
+ break;
+ }
+ }
+ return rv;
+} /* _PR_SocketGetSocketOption */
+
+PRStatus PR_CALLBACK _PR_SocketSetSocketOption(PRFileDesc *fd, const PRSocketOptionData *data)
+{
+ PRStatus rv;
+ PRInt32 level, name;
+
+ /*
+ * PR_SockOpt_Nonblocking is a special case that does not
+ * translate to a setsockopt call.
+ */
+ if (PR_SockOpt_Nonblocking == data->option)
+ {
+#ifdef WINNT
+ PR_ASSERT((fd->secret->md.io_model_committed == PR_FALSE)
+ || (fd->secret->nonblocking == data->value.non_blocking));
+ if (fd->secret->md.io_model_committed
+ && (fd->secret->nonblocking != data->value.non_blocking))
+ {
+ /*
+ * On NT, once we have associated a socket with the io
+ * completion port, we can't disassociate it. So we
+ * can't change the nonblocking option of the socket
+ * afterwards.
+ */
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+#endif
+ fd->secret->nonblocking = data->value.non_blocking;
+ return PR_SUCCESS;
+ }
+
+ rv = _PR_MapOptionName(data->option, &level, &name);
+ if (PR_SUCCESS == rv)
+ {
+ switch (data->option)
+ {
+ case PR_SockOpt_Linger:
+ {
+ struct linger linger;
+ linger.l_onoff = data->value.linger.polarity;
+ linger.l_linger = PR_IntervalToSeconds(data->value.linger.linger);
+ rv = _PR_MD_SETSOCKOPT(
+ fd, level, name, (char*)&linger, sizeof(linger));
+ break;
+ }
+ case PR_SockOpt_Reuseaddr:
+ case PR_SockOpt_Keepalive:
+ case PR_SockOpt_NoDelay:
+ {
+#ifdef WIN32 /* Winsock */
+ BOOL value;
+#else
+ PRIntn value;
+#endif
+ value = (data->value.reuse_addr) ? 1 : 0;
+ rv = _PR_MD_SETSOCKOPT(
+ fd, level, name, (char*)&value, sizeof(value));
+ break;
+ }
+ case PR_SockOpt_McastLoopback:
+ {
+#ifdef WIN32 /* Winsock */
+ BOOL bool;
+#else
+ PRUint8 bool;
+#endif
+ bool = data->value.mcast_loopback ? 1 : 0;
+ rv = _PR_MD_SETSOCKOPT(
+ fd, level, name, (char*)&bool, sizeof(bool));
+ break;
+ }
+ case PR_SockOpt_RecvBufferSize:
+ case PR_SockOpt_SendBufferSize:
+ case PR_SockOpt_MaxSegment:
+ {
+ PRIntn value = data->value.recv_buffer_size;
+ rv = _PR_MD_SETSOCKOPT(
+ fd, level, name, (char*)&value, sizeof(value));
+ break;
+ }
+ case PR_SockOpt_IpTimeToLive:
+ case PR_SockOpt_IpTypeOfService:
+ {
+ /* These options should really be an int (or PRIntn). */
+ rv = _PR_MD_SETSOCKOPT(
+ fd, level, name, (char*)&data->value.ip_ttl, sizeof(PRUintn));
+ break;
+ }
+ case PR_SockOpt_McastTimeToLive:
+ {
+#ifdef WIN32 /* Winsock */
+ int ttl;
+#else
+ PRUint8 ttl;
+#endif
+ ttl = data->value.mcast_ttl;
+ rv = _PR_MD_SETSOCKOPT(
+ fd, level, name, (char*)&ttl, sizeof(ttl));
+ break;
+ }
+ case PR_SockOpt_AddMember:
+ case PR_SockOpt_DropMember:
+ {
+ struct ip_mreq mreq;
+ mreq.imr_multiaddr.s_addr =
+ data->value.add_member.mcaddr.inet.ip;
+ mreq.imr_interface.s_addr =
+ data->value.add_member.ifaddr.inet.ip;
+ rv = _PR_MD_SETSOCKOPT(
+ fd, level, name, (char*)&mreq, sizeof(mreq));
+ break;
+ }
+ case PR_SockOpt_McastInterface:
+ {
+ /* This option is a struct in_addr. */
+ rv = _PR_MD_SETSOCKOPT(
+ fd, level, name, (char*)&data->value.mcast_if.inet.ip,
+ sizeof(data->value.mcast_if.inet.ip));
+ break;
+ }
+ default:
+ PR_NOT_REACHED("Unknown socket option");
+ break;
+ }
+ }
+ return rv;
+} /* _PR_SocketSetSocketOption */
+
+#endif /* ! _PR_PTHREADS */
diff --git a/pr/src/io/prmmap.c b/pr/src/io/prmmap.c
new file mode 100644
index 00000000..f5f66465
--- /dev/null
+++ b/pr/src/io/prmmap.c
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ *********************************************************************
+ *
+ * Memory-mapped files
+ *
+ *********************************************************************
+ */
+
+#include "primpl.h"
+
+PR_IMPLEMENT(PRFileMap *) PR_CreateFileMap(
+ PRFileDesc *fd,
+ PRInt64 size,
+ PRFileMapProtect prot)
+{
+ PRFileMap *fmap;
+
+ PR_ASSERT(prot == PR_PROT_READONLY || prot == PR_PROT_READWRITE
+ || prot == PR_PROT_WRITECOPY);
+ fmap = PR_NEWZAP(PRFileMap);
+ if (NULL == fmap) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+ fmap->fd = fd;
+ fmap->prot = prot;
+ if (_PR_MD_CREATE_FILE_MAP(fmap, size) == PR_SUCCESS) {
+ return fmap;
+ } else {
+ PR_DELETE(fmap);
+ return NULL;
+ }
+}
+
+PR_IMPLEMENT(void *) PR_MemMap(
+ PRFileMap *fmap,
+ PRInt64 offset,
+ PRUint32 len)
+{
+ return _PR_MD_MEM_MAP(fmap, offset, len);
+}
+
+PR_IMPLEMENT(PRStatus) PR_MemUnmap(void *addr, PRUint32 len)
+{
+ return _PR_MD_MEM_UNMAP(addr, len);
+}
+
+PR_IMPLEMENT(PRStatus) PR_CloseFileMap(PRFileMap *fmap)
+{
+ return _PR_MD_CLOSE_FILE_MAP(fmap);
+}
diff --git a/pr/src/io/prmwait.c b/pr/src/io/prmwait.c
new file mode 100644
index 00000000..c29628ea
--- /dev/null
+++ b/pr/src/io/prmwait.c
@@ -0,0 +1,702 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "prlog.h"
+#include "prmem.h"
+#include "primpl.h"
+#include "prmwait.h"
+#include "prerror.h"
+#include "pprmwait.h"
+
+static PRLock *mw_lock = NULL;
+static _PRGlobalState *mw_state = NULL;
+
+static PRIntervalTime max_polling_interval;
+
+/******************************************************************/
+/******************************************************************/
+/************************ The private portion *********************/
+/******************************************************************/
+/******************************************************************/
+static PRStatus MW_Init(void)
+{
+ if (NULL != mw_lock) return PR_SUCCESS;
+ if (NULL != (mw_lock = PR_NewLock()))
+ {
+ _PRGlobalState *state = PR_NEWZAP(_PRGlobalState);
+ if (state == NULL) goto failed;
+
+ PR_INIT_CLIST(&state->group_list);
+
+ PR_Lock(mw_lock);
+ if (NULL == mw_state) /* is it still NULL? */
+ {
+ mw_state = state; /* if yes, set our value */
+ state = NULL; /* and indicate we've done the job */
+ max_polling_interval = PR_MillisecondsToInterval(MAX_POLLING_INTERVAL);
+ }
+ PR_Unlock(mw_lock);
+ if (NULL != state) PR_DELETE(state);
+ return PR_SUCCESS;
+ }
+
+failed:
+ return PR_FAILURE;
+} /* MW_Init */
+
+static PRWaitGroup *MW_Init2(void)
+{
+ PRWaitGroup *group = mw_state->group; /* it's the null group */
+ if (NULL == group) /* there is this special case */
+ {
+ group = PR_CreateWaitGroup(_PR_DEFAULT_HASH_LENGTH);
+ if (NULL == group) goto failed_alloc;
+ PR_Lock(mw_lock);
+ if (NULL == mw_state->group)
+ {
+ mw_state->group = group;
+ group = NULL;
+ }
+ PR_Unlock(mw_lock);
+ if (group != NULL) (void)PR_DestroyWaitGroup(group);
+ group = mw_state->group; /* somebody beat us to it */
+ }
+failed_alloc:
+ return group; /* whatever */
+} /* MW_Init2 */
+
+static _PR_HashStory MW_AddHashInternal(PRRecvWait *desc, _PRWaiterHash *hash)
+{
+ /*
+ ** The entries are put in the table using the fd (PRFileDesc*) of
+ ** the receive descriptor as the key. This allows us to locate
+ ** the appropriate entry aqain when the poll operation finishes.
+ **
+ ** The pointer to the file descriptor object is first divided by
+ ** the natural alignment of a pointer in the belief that object
+ ** will have at least that many zeros in the low order bits.
+ ** This may not be a good assuption.
+ **
+ ** We try to put the entry in by rehashing three times. After
+ ** that we declare defeat and force the table to be reconstructed.
+ ** Since some fds might be added more than once, won't that cause
+ ** collisions even in an empty table?
+ */
+ PRIntn rehash = 11;
+ PRRecvWait **waiter;
+ PRUintn hidx = _MW_HASH(desc->fd, hash->length);
+ do
+ {
+ waiter = &hash->recv_wait;
+ if (NULL == waiter[hidx])
+ {
+ waiter[hidx] = desc;
+ hash->count += 1;
+#if 0
+ printf("Adding 0x%x->0x%x ", desc, desc->fd);
+ printf(
+ "table[%u:%u:*%u]: 0x%x->0x%x\n",
+ hidx, hash->count, hash->length, waiter[hidx], waiter[hidx]->fd);
+#endif
+ return _prmw_success;
+ }
+ if (desc == waiter[hidx])
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); /* desc already in table */
+ return _prmw_error;
+ }
+#if 0
+ printf("Failing 0x%x->0x%x ", desc, desc->fd);
+ printf(
+ "table[*%u:%u:%u]: 0x%x->0x%x\n",
+ hidx, hash->count, hash->length, waiter[hidx], waiter[hidx]->fd);
+#endif
+ hidx = _MW_REHASH(desc->fd, hidx, hash->length);
+ } while (--rehash > 0);
+ return _prmw_rehash;
+} /* MW_AddHashInternal */
+
+static _PR_HashStory MW_ExpandHashInternal(PRWaitGroup *group)
+{
+ PRRecvWait **desc;
+ PRUint32 pidx, length = 0;
+ _PRWaiterHash *newHash, *oldHash = group->waiter;
+
+
+ static const PRInt32 prime_number[] = {
+ _PR_DEFAULT_HASH_LENGTH, 179, 521, 907, 1427,
+ 2711, 3917, 5021, 8219, 11549, 18911, 26711, 33749, 44771};
+ PRUintn primes = (sizeof(prime_number) / sizeof(PRIntn));
+
+ /* look up the next size we'd like to use for the hash table */
+ for (pidx = 0; pidx < primes; ++pidx)
+ {
+ if (prime_number[pidx] == oldHash->length)
+ {
+ length = prime_number[pidx + 1];
+ break;
+ }
+ }
+ if (0 == length)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return _prmw_error; /* we're hosed */
+ }
+
+ /* allocate the new hash table and fill it in with the old */
+ newHash = (_PRWaiterHash*)PR_CALLOC(
+ sizeof(_PRWaiterHash) + (length * sizeof(PRRecvWait*)));
+
+ newHash->length = length;
+ for (desc = &oldHash->recv_wait; newHash->count < oldHash->count; ++desc)
+ {
+ if (NULL != *desc)
+ {
+ if (_prmw_success != MW_AddHashInternal(*desc, newHash))
+ {
+ PR_ASSERT(!"But, but, but ...");
+ PR_DELETE(newHash);
+ return _prmw_error;
+ }
+ }
+ }
+ PR_DELETE(group->waiter);
+ group->waiter = newHash;
+ return _prmw_success;
+} /* MW_ExpandHashInternal */
+
+static void _MW_DoneInternal(
+ PRWaitGroup *group, PRRecvWait **waiter, PRMWStatus outcome)
+{
+ /*
+ ** Add this receive wait object to the list of finished I/O
+ ** operations for this particular group. If there are other
+ ** threads waiting on the group, notify one. If not, arrange
+ ** for this thread to return.
+ */
+
+#if 0
+ printf("Removing 0x%x->0x%x\n", *waiter, (*waiter)->fd);
+#endif
+ (*waiter)->outcome = outcome;
+ PR_APPEND_LINK(&((*waiter)->internal), &group->io_ready);
+ PR_NotifyCondVar(group->io_complete);
+ PR_ASSERT(0 != group->waiter->count);
+ group->waiter->count -= 1;
+ *waiter = NULL;
+} /* _MW_DoneInternal */
+
+static PRRecvWait **_MW_LookupInternal(PRWaitGroup *group, PRFileDesc *fd)
+{
+ /*
+ ** Find the receive wait object corresponding to the file descriptor.
+ ** Only search the wait group specified.
+ */
+ PRRecvWait **desc;
+ PRIntn rehash = 11;
+ _PRWaiterHash *hash = group->waiter;
+ PRUintn hidx = _MW_HASH(fd, hash->length);
+
+ while (rehash-- > 0)
+ {
+ desc = (&hash->recv_wait) + hidx;
+ if ((*desc != NULL) && ((*desc)->fd == fd)) return desc;
+ hidx = _MW_REHASH(fd, hidx, hash->length);
+ }
+ return NULL;
+} /* _MW_LookupInternal */
+
+static PRStatus _MW_PollInternal(PRWaitGroup *group)
+{
+ PRRecvWait **waiter;
+ PRStatus rv = PR_FAILURE;
+ PRUintn count, count_ready;
+ PRIntervalTime polling_interval;
+
+ group->poller = PR_GetCurrentThread();
+
+ PR_Unlock(group->ml);
+
+ while (PR_TRUE)
+ {
+ PRIntervalTime now, since_last_poll;
+ PRPollDesc *poll_list = group->polling_list;
+ /*
+ ** There's something to do. See if our existing polling list
+ ** is large enough for what we have to do?
+ */
+
+ while (group->polling_count < group->waiter->count)
+ {
+ PRUint32 old_count = group->waiter->count;
+ PRUint32 new_count = PR_ROUNDUP(old_count, _PR_POLL_COUNT_FUDGE);
+ PRSize new_size = sizeof(PRPollDesc) * new_count;
+ poll_list = (PRPollDesc*)PR_CALLOC(new_size);
+ if (NULL == poll_list) goto failed_alloc;
+ if (NULL != group->polling_list)
+ PR_DELETE(group->polling_list);
+ group->polling_list = poll_list;
+ group->polling_count = new_count;
+ }
+
+ now = PR_IntervalNow();
+ polling_interval = max_polling_interval;
+ since_last_poll = now - group->last_poll;
+ PR_Lock(group->ml);
+ waiter = &group->waiter->recv_wait;
+
+ for (count = 0; count < group->waiter->count; ++waiter)
+ {
+ if (NULL != *waiter) /* a live one! */
+ {
+ if (since_last_poll >= (*waiter)->timeout)
+ _MW_DoneInternal(group, waiter, PR_MW_TIMEOUT);
+ else
+ {
+ if (PR_INTERVAL_NO_TIMEOUT != (*waiter)->timeout)
+ {
+ (*waiter)->timeout -= since_last_poll;
+ if ((*waiter)->timeout < polling_interval)
+ polling_interval = (*waiter)->timeout;
+ }
+ poll_list->fd = (*waiter)->fd;
+ poll_list->in_flags = PR_POLL_READ;
+ poll_list->out_flags = 0;
+#if 0
+ printf(
+ "Polling 0x%x[%d]: [fd: 0x%x, tmo: %u]\n",
+ poll_list, count, poll_list->fd, (*waiter)->timeout);
+#endif
+ poll_list += 1;
+ count += 1;
+ }
+ }
+ }
+
+ PR_ASSERT(count == group->waiter->count);
+ if (0 == count) break;
+
+ group->last_poll = now;
+
+ PR_Unlock(group->ml);
+
+ count_ready = PR_Poll(group->polling_list, count, polling_interval);
+
+ PR_Lock(group->ml);
+
+ if (-1 == count_ready) goto failed_poll; /* that's a shame */
+ for (poll_list = group->polling_list; count > 0; poll_list++, count--)
+ {
+ if (poll_list->out_flags != 0)
+ {
+ waiter = _MW_LookupInternal(group, poll_list->fd);
+ if (NULL != waiter)
+ _MW_DoneInternal(group, waiter, PR_MW_SUCCESS);
+ }
+ }
+ /*
+ ** If there are no more threads waiting for completion,
+ ** we need to return.
+ ** This thread was "borrowed" to do the polling, but it really
+ ** belongs to the client.
+ */
+ if ((_prmw_running != group->state)
+ || (0 == group->waiting_threads)) break;
+ PR_Unlock(group->ml);
+ }
+
+ rv = PR_SUCCESS;
+
+failed_poll:
+failed_alloc:
+ group->poller = NULL; /* we were that, not we ain't */
+ return rv; /* we return with the lock held */
+} /* _MW_PollInternal */
+
+static PRMWGroupState MW_TestForShutdownInternal(PRWaitGroup *group)
+{
+ PRMWGroupState rv = group->state;
+ /*
+ ** Looking at the group's fields is safe because
+ ** once the group's state is no longer running, it
+ ** cannot revert and there is a safe check on entry
+ ** to make sure no more threads are made to wait.
+ */
+ if ((_prmw_stopping == rv)
+ && (0 == group->waiting_threads)
+ && PR_CLIST_IS_EMPTY(&group->io_ready)
+ && (0 == group->waiter->count))
+ {
+ rv = group->state = _prmw_stopped;
+ PR_NotifyCondVar(group->mw_manage);
+ }
+ return rv;
+} /* MW_TestForShutdownInternal */
+
+static void _MW_InitialRecv(PRCList *io_ready)
+{
+ PRRecvWait *desc = (PRRecvWait*)io_ready;
+ if ((NULL == desc->buffer.start)
+ || (0 == desc->buffer.length))
+ desc->bytesRecv = 0;
+ else
+ {
+ desc->bytesRecv = desc->fd->methods->recv(
+ desc->fd, desc->buffer.start,
+ desc->buffer.length, 0, desc->timeout);
+ if (desc->bytesRecv < 0) /* SetError should already be there */
+ desc->outcome = PR_MW_FAILURE;
+ }
+} /* _MW_InitialRecv */
+
+/******************************************************************/
+/******************************************************************/
+/********************** The public API portion ********************/
+/******************************************************************/
+/******************************************************************/
+PR_IMPLEMENT(PRStatus) PR_AddWaitFileDesc(
+ PRWaitGroup *group, PRRecvWait *desc)
+{
+ _PR_HashStory hrv;
+ PRStatus rv = PR_FAILURE;
+ if (PR_FAILURE == MW_Init()) goto failed_init;
+ if ((NULL == group) && (NULL == (group = MW_Init2()))) goto failed_init;
+
+ PR_ASSERT(NULL != desc->fd);
+
+ desc->outcome = PR_MW_PENDING; /* nice, well known value */
+ desc->bytesRecv = 0; /* likewise, though this value is ambiguious */
+
+ PR_Lock(group->ml);
+
+ if (_prmw_running != group->state)
+ {
+ /* Not allowed to add after cancelling the group */
+ desc->outcome = PR_MW_INTERRUPT;
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ goto invalid_state;
+ }
+
+ /*
+ ** If the waiter count is zero at this point, there's no telling
+ ** how long we've been idle. Therefore, initialize the beginning
+ ** of the timing interval. As long as the list doesn't go empty,
+ ** it will maintain itself.
+ */
+ if (0 == group->waiter->count)
+ group->last_poll = PR_IntervalNow();
+ do
+ {
+ hrv = MW_AddHashInternal(desc, group->waiter);
+ if (_prmw_rehash != hrv) break;
+ hrv = MW_ExpandHashInternal(group); /* gruesome */
+ if (_prmw_success != hrv) break;
+ } while (PR_TRUE);
+
+ PR_NotifyCondVar(group->new_business); /* tell the world */
+ rv = (_prmw_success == hrv) ? PR_SUCCESS : PR_FAILURE;
+
+failed_init:
+invalid_state:
+ PR_Unlock(group->ml);
+ return rv;
+} /* PR_AddWaitFileDesc */
+
+PR_IMPLEMENT(PRRecvWait*) PR_WaitRecvReady(PRWaitGroup *group)
+{
+ PRStatus rv = PR_SUCCESS;
+ PRCList *io_ready = NULL;
+ if (PR_FAILURE == MW_Init()) goto failed_init;
+ if ((NULL == group) && (NULL == (group = MW_Init2()))) goto failed_init;
+
+ PR_Lock(group->ml);
+
+ if (_prmw_running != group->state)
+ {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ goto invalid_state;
+ }
+
+ group->waiting_threads += 1; /* the polling thread is counted */
+
+ do
+ {
+ /*
+ ** If the I/O ready list isn't empty, have this thread
+ ** return with the first receive wait object that's available.
+ */
+ if (PR_CLIST_IS_EMPTY(&group->io_ready))
+ {
+ while ((NULL == group->waiter) || (0 == group->waiter->count))
+ {
+ if (_prmw_running != group->state) goto aborted;
+ rv = PR_WaitCondVar(group->new_business, PR_INTERVAL_NO_TIMEOUT);
+ if (_MW_ABORTED(rv)) goto aborted;
+ }
+
+ /*
+ ** Is there a polling thread yet? If not, grab this thread
+ ** and use it.
+ */
+ if (NULL == group->poller)
+ {
+ /*
+ ** This thread will stay do polling until it becomes the only one
+ ** left to service a completion. Then it will return and there will
+ ** be none left to actually poll or to run completions.
+ **
+ ** The polling function should only return w/ failure or
+ ** with some I/O ready.
+ */
+ if (PR_FAILURE == _MW_PollInternal(group)) goto failed_poll;
+ if (PR_CLIST_IS_EMPTY(&group->io_ready)) continue; /* timeout */
+ }
+ else
+ {
+ while (PR_CLIST_IS_EMPTY(&group->io_ready))
+ {
+ rv = PR_WaitCondVar(group->io_complete, PR_INTERVAL_NO_TIMEOUT);
+ if (_MW_ABORTED(rv)) goto aborted;
+ }
+ }
+ }
+ io_ready = PR_LIST_HEAD(&group->io_ready);
+ PR_NotifyCondVar(group->io_taken);
+ PR_ASSERT(io_ready != NULL);
+ PR_REMOVE_LINK(io_ready);
+
+ /* If the operation failed, record the reason why */
+ switch (((PRRecvWait*)io_ready)->outcome)
+ {
+ case PR_MW_PENDING:
+ PR_ASSERT(PR_MW_PENDING != ((PRRecvWait*)io_ready)->outcome);
+ break;
+ case PR_MW_SUCCESS:
+ _MW_InitialRecv(io_ready);
+ break;
+ case PR_MW_TIMEOUT:
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ break;
+ case PR_MW_INTERRUPT:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ break;
+ default: break;
+ }
+ } while (NULL == io_ready);
+
+aborted:
+failed_poll:
+ group->waiting_threads -= 1;
+invalid_state:
+ (void)MW_TestForShutdownInternal(group);
+ PR_Unlock(group->ml);
+
+failed_init:
+
+ return (PRRecvWait*)io_ready;
+} /* PR_WaitRecvReady */
+
+PR_IMPLEMENT(PRStatus) PR_CancelWaitFileDesc(PRWaitGroup *group, PRRecvWait *desc)
+{
+ PRRecvWait **recv_wait;
+ PRStatus rv = PR_SUCCESS;
+ if (PR_FAILURE == MW_Init()) return rv;
+ if (NULL == group) group = mw_state->group;
+ PR_ASSERT(NULL != group);
+ if (NULL == group)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ PR_Lock(group->ml);
+
+ if (_prmw_running != group->state)
+ {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ rv = PR_FAILURE;
+ goto stopping;
+ }
+
+ if (NULL != (recv_wait = _MW_LookupInternal(group, desc->fd)))
+ {
+ /* it was in the wait table */
+ _MW_DoneInternal(group, recv_wait, PR_MW_INTERRUPT);
+ goto found;
+ }
+ if (!PR_CLIST_IS_EMPTY(&group->io_ready))
+ {
+ /* is it already complete? */
+ PRCList *head = PR_LIST_HEAD(&group->io_ready);
+ do
+ {
+ PRRecvWait *done = (PRRecvWait*)head;
+ if (done == desc) goto found;
+ head = PR_NEXT_LINK(head);
+ } while (head != &group->io_ready);
+ }
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ rv = PR_FAILURE;
+
+found:
+stopping:
+ PR_Unlock(group->ml);
+ return rv;
+} /* PR_CancelWaitFileDesc */
+
+PR_IMPLEMENT(PRRecvWait*) PR_CancelWaitGroup(PRWaitGroup *group)
+{
+ PRRecvWait **desc;
+ PRRecvWait *recv_wait = NULL;
+ if (PR_FAILURE == MW_Init())
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+ }
+ if (NULL == group) group = mw_state->group;
+ PR_ASSERT(NULL != group);
+ if (NULL == group)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+ }
+
+ PR_Lock(group->ml);
+ if (_prmw_stopped != group->state)
+ {
+ if (_prmw_running == group->state)
+ group->state = _prmw_stopping; /* so nothing new comes in */
+ if (0 == group->waiting_threads) /* is there anybody else? */
+ group->state = _prmw_stopped; /* we can stop right now */
+ while (_prmw_stopped != group->state)
+ (void)PR_WaitCondVar(group->mw_manage, PR_INTERVAL_NO_TIMEOUT);
+
+ /* make all the existing descriptors look done/interrupted */
+ for (desc = &group->waiter->recv_wait; group->waiter->count > 0; ++desc)
+ {
+ if (NULL != *desc)
+ _MW_DoneInternal(group, desc, PR_MW_INTERRUPT);
+ }
+
+ PR_NotifyAllCondVar(group->new_business);
+ }
+
+ /* take first element of finished list and return it or NULL */
+ if (PR_CLIST_IS_EMPTY(&group->io_ready))
+ PR_SetError(PR_GROUP_EMPTY_ERROR, 0);
+ else
+ {
+ PRCList *head = PR_LIST_HEAD(&group->io_ready);
+ PR_REMOVE_AND_INIT_LINK(head);
+ recv_wait = (PRRecvWait*)head;
+ }
+ PR_Unlock(group->ml);
+
+ return recv_wait;
+} /* PR_CancelWaitGroup */
+
+PR_IMPLEMENT(PRWaitGroup*) PR_CreateWaitGroup(PRInt32 size /* ignored */)
+{
+ PRWaitGroup *wg = NULL;
+ if (PR_FAILURE == MW_Init()) goto failed;
+
+ if (NULL == (wg = PR_NEWZAP(PRWaitGroup))) goto failed;
+ /* the wait group itself */
+ wg->ml = PR_NewLock();
+ if (NULL == wg->ml) goto failed_lock;
+ wg->io_taken = PR_NewCondVar(wg->ml);
+ if (NULL == wg->io_taken) goto failed_cvar0;
+ wg->io_complete = PR_NewCondVar(wg->ml);
+ if (NULL == wg->io_complete) goto failed_cvar1;
+ wg->new_business = PR_NewCondVar(wg->ml);
+ if (NULL == wg->new_business) goto failed_cvar2;
+ wg->mw_manage = PR_NewCondVar(wg->ml);
+ if (NULL == wg->mw_manage) goto failed_cvar3;
+
+ PR_INIT_CLIST(&wg->group_link);
+ PR_INIT_CLIST(&wg->io_ready);
+
+ /* the waiters sequence */
+ wg->waiter = (_PRWaiterHash*)PR_CALLOC(
+ sizeof(_PRWaiterHash) +
+ (_PR_DEFAULT_HASH_LENGTH * sizeof(PRRecvWait*)));
+ if (NULL == wg->waiter) goto failed_waiter;
+ wg->waiter->count = 0;
+ wg->waiter->length = _PR_DEFAULT_HASH_LENGTH;
+
+ PR_Lock(mw_lock);
+ PR_APPEND_LINK(&wg->group_link, &mw_state->group_list);
+ PR_Unlock(mw_lock);
+ return wg;
+
+failed_waiter:
+ PR_DestroyCondVar(wg->mw_manage);
+failed_cvar3:
+ PR_DestroyCondVar(wg->new_business);
+failed_cvar2:
+ PR_DestroyCondVar(wg->io_taken);
+failed_cvar1:
+ PR_DestroyCondVar(wg->io_complete);
+failed_cvar0:
+ PR_DestroyLock(wg->ml);
+failed_lock:
+ PR_DELETE(wg);
+
+failed:
+ return wg;
+} /* MW_CreateWaitGroup */
+
+PR_IMPLEMENT(PRStatus) PR_DestroyWaitGroup(PRWaitGroup *group)
+{
+ PRStatus rv = PR_SUCCESS;
+ if (NULL == group) group = mw_state->group;
+ PR_ASSERT(NULL != group);
+ if (NULL != group)
+ {
+ if (_prmw_stopped != group->state) /* quick, unsafe test */
+ {
+ PRMWGroupState mws;
+ /* One shot to correct the situation */
+ PR_Lock(group->ml);
+ if (group->state < _prmw_stopped) /* safer test */
+ group->state = _prmw_stopping;
+ mws = MW_TestForShutdownInternal(group);
+ PR_Unlock(group->ml);
+ if (_prmw_stopped != mws) /* quick test again */
+ {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return PR_FAILURE;
+ }
+ }
+
+ PR_Lock(mw_lock);
+ PR_REMOVE_LINK(&group->group_link);
+ PR_Unlock(mw_lock);
+
+ PR_DELETE(group->waiter);
+ PR_DestroyCondVar(group->new_business);
+ PR_DestroyCondVar(group->io_complete);
+ PR_DestroyCondVar(group->io_taken);
+ PR_DestroyLock(group->ml);
+ if (group == mw_state->group) mw_state->group = NULL;
+ PR_DELETE(group);
+ }
+ return rv;
+} /* PR_DestroyWaitGroup */
+
+/* prmwait.c */
diff --git a/pr/src/io/prprf.c b/pr/src/io/prprf.c
new file mode 100644
index 00000000..ce8d5330
--- /dev/null
+++ b/pr/src/io/prprf.c
@@ -0,0 +1,1226 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+** Portable safe sprintf code.
+**
+** Author: Kipp E.B. Hickman
+*/
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include "primpl.h"
+#include "prprf.h"
+#include "prlong.h"
+#include "prlog.h"
+#include "prmem.h"
+
+/*
+** Note: on some platforms va_list is defined as an array,
+** and requires array notation.
+*/
+#if defined(MACLINUX) || defined(WIN16)
+#define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0]
+#else
+#define VARARGS_ASSIGN(foo, bar) (foo) = (bar)
+#endif /*MACLINUX*/
+
+/*
+** WARNING: This code may *NOT* call PR_LOG (because PR_LOG calls it)
+*/
+
+/*
+** XXX This needs to be internationalized!
+*/
+
+typedef struct SprintfStateStr SprintfState;
+
+struct SprintfStateStr {
+ int (*stuff)(SprintfState *ss, const char *sp, PRUint32 len);
+
+ char *base;
+ char *cur;
+ PRUint32 maxlen;
+
+ int (*func)(void *arg, const char *sp, PRUint32 len);
+ void *arg;
+};
+
+/*
+** Numbered Arguement State
+*/
+struct NumArgState{
+ int type; /* type of the current ap */
+ va_list ap; /* point to the corresponding position on ap */
+};
+
+static PRBool l10n_debug_init = PR_FALSE;
+static PRBool l10n_debug = PR_FALSE;
+
+#define NAS_DEFAULT_NUM 20 /* default number of NumberedArgumentState array */
+
+
+#define TYPE_INT16 0
+#define TYPE_UINT16 1
+#define TYPE_INTN 2
+#define TYPE_UINTN 3
+#define TYPE_INT32 4
+#define TYPE_UINT32 5
+#define TYPE_INT64 6
+#define TYPE_UINT64 7
+#define TYPE_STRING 8
+#define TYPE_DOUBLE 9
+#define TYPE_INTSTR 10
+#define TYPE_UNKNOWN 20
+
+#define _LEFT 0x1
+#define _SIGNED 0x2
+#define _SPACED 0x4
+#define _ZEROS 0x8
+#define _NEG 0x10
+
+/*
+** Fill into the buffer using the data in src
+*/
+static int fill2(SprintfState *ss, const char *src, int srclen, int width,
+ int flags)
+{
+ char space = ' ';
+ int rv;
+
+ width -= srclen;
+ if ((width > 0) && ((flags & _LEFT) == 0)) { /* Right adjusting */
+ if (flags & _ZEROS) {
+ space = '0';
+ }
+ while (--width >= 0) {
+ rv = (*ss->stuff)(ss, &space, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ }
+
+ /* Copy out the source data */
+ rv = (*ss->stuff)(ss, src, srclen);
+ if (rv < 0) {
+ return rv;
+ }
+
+ if ((width > 0) && ((flags & _LEFT) != 0)) { /* Left adjusting */
+ while (--width >= 0) {
+ rv = (*ss->stuff)(ss, &space, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+** Fill a number. The order is: optional-sign zero-filling conversion-digits
+*/
+static int fill_n(SprintfState *ss, const char *src, int srclen, int width,
+ int prec, int type, int flags)
+{
+ int zerowidth = 0;
+ int precwidth = 0;
+ int signwidth = 0;
+ int leftspaces = 0;
+ int rightspaces = 0;
+ int cvtwidth;
+ int rv;
+ char sign;
+
+ if ((type & 1) == 0) {
+ if (flags & _NEG) {
+ sign = '-';
+ signwidth = 1;
+ } else if (flags & _SIGNED) {
+ sign = '+';
+ signwidth = 1;
+ } else if (flags & _SPACED) {
+ sign = ' ';
+ signwidth = 1;
+ }
+ }
+ cvtwidth = signwidth + srclen;
+
+ if (prec > 0) {
+ if (prec > srclen) {
+ precwidth = prec - srclen; /* Need zero filling */
+ cvtwidth += precwidth;
+ }
+ }
+
+ if ((flags & _ZEROS) && (prec < 0)) {
+ if (width > cvtwidth) {
+ zerowidth = width - cvtwidth; /* Zero filling */
+ cvtwidth += zerowidth;
+ }
+ }
+
+ if (flags & _LEFT) {
+ if (width > cvtwidth) {
+ /* Space filling on the right (i.e. left adjusting) */
+ rightspaces = width - cvtwidth;
+ }
+ } else {
+ if (width > cvtwidth) {
+ /* Space filling on the left (i.e. right adjusting) */
+ leftspaces = width - cvtwidth;
+ }
+ }
+ while (--leftspaces >= 0) {
+ rv = (*ss->stuff)(ss, " ", 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ if (signwidth) {
+ rv = (*ss->stuff)(ss, &sign, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ while (--precwidth >= 0) {
+ rv = (*ss->stuff)(ss, "0", 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ while (--zerowidth >= 0) {
+ rv = (*ss->stuff)(ss, "0", 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ rv = (*ss->stuff)(ss, src, srclen);
+ if (rv < 0) {
+ return rv;
+ }
+ while (--rightspaces >= 0) {
+ rv = (*ss->stuff)(ss, " ", 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ return 0;
+}
+
+/*
+** Convert a long into its printable form
+*/
+static int cvt_l(SprintfState *ss, long num, int width, int prec, int radix,
+ int type, int flags, const char *hexp)
+{
+ char cvtbuf[100];
+ char *cvt;
+ int digits;
+
+ /* according to the man page this needs to happen */
+ if ((prec == 0) && (num == 0)) {
+ return 0;
+ }
+
+ /*
+ ** Converting decimal is a little tricky. In the unsigned case we
+ ** need to stop when we hit 10 digits. In the signed case, we can
+ ** stop when the number is zero.
+ */
+ cvt = cvtbuf + sizeof(cvtbuf);
+ digits = 0;
+ while (num) {
+ int digit = (((unsigned long)num) % radix) & 0xF;
+ *--cvt = hexp[digit];
+ digits++;
+ num = (long)((unsigned long)num) / radix;
+ }
+ if (digits == 0) {
+ *--cvt = '0';
+ digits++;
+ }
+
+ /*
+ ** Now that we have the number converted without its sign, deal with
+ ** the sign and zero padding.
+ */
+ return fill_n(ss, cvt, digits, width, prec, type, flags);
+}
+
+/*
+** Convert a 64-bit integer into its printable form
+*/
+static int cvt_ll(SprintfState *ss, PRInt64 num, int width, int prec, int radix,
+ int type, int flags, const char *hexp)
+{
+ char cvtbuf[100];
+ char *cvt;
+ int digits;
+ PRInt64 rad;
+
+ /* according to the man page this needs to happen */
+ if ((prec == 0) && (LL_IS_ZERO(num))) {
+ return 0;
+ }
+
+ /*
+ ** Converting decimal is a little tricky. In the unsigned case we
+ ** need to stop when we hit 10 digits. In the signed case, we can
+ ** stop when the number is zero.
+ */
+ LL_I2L(rad, radix);
+ cvt = cvtbuf + sizeof(cvtbuf);
+ digits = 0;
+ while (!LL_IS_ZERO(num)) {
+ PRInt32 digit;
+ PRInt64 quot, rem;
+ LL_UDIVMOD(&quot, &rem, num, rad);
+ LL_L2I(digit, rem);
+ *--cvt = hexp[digit & 0xf];
+ digits++;
+ num = quot;
+ }
+ if (digits == 0) {
+ *--cvt = '0';
+ digits++;
+ }
+
+ /*
+ ** Now that we have the number converted without its sign, deal with
+ ** the sign and zero padding.
+ */
+ return fill_n(ss, cvt, digits, width, prec, type, flags);
+}
+
+/*
+** Convert a double precision floating point number into its printable
+** form.
+**
+** XXX stop using sprintf to convert floating point
+*/
+static int cvt_f(SprintfState *ss, double d, const char *fmt0, const char *fmt1)
+{
+ char fin[20];
+ char fout[300];
+ int amount = fmt1 - fmt0;
+
+ PR_ASSERT((amount > 0) && (amount < sizeof(fin)));
+ if (amount >= sizeof(fin)) {
+ /* Totally bogus % command to sprintf. Just ignore it */
+ return 0;
+ }
+ memcpy(fin, fmt0, amount);
+ fin[amount] = 0;
+
+ /* Convert floating point using the native sprintf code */
+#ifdef DEBUG
+ {
+ const char *p = fin;
+ while (*p) {
+ PR_ASSERT(*p != 'L');
+ p++;
+ }
+ }
+#endif
+ sprintf(fout, fin, d);
+
+ /*
+ ** This assert will catch overflow's of fout, when building with
+ ** debugging on. At least this way we can track down the evil piece
+ ** of calling code and fix it!
+ */
+ PR_ASSERT(strlen(fout) < sizeof(fout));
+
+ return (*ss->stuff)(ss, fout, strlen(fout));
+}
+
+/*
+** Convert a string into its printable form. "width" is the output
+** width. "prec" is the maximum number of characters of "s" to output,
+** where -1 means until NUL.
+*/
+static int cvt_s(SprintfState *ss, const char *s, int width, int prec,
+ int flags)
+{
+ int slen;
+
+ if (prec == 0)
+ return 0;
+
+ /* Limit string length by precision value */
+ slen = s ? strlen(s) : 6;
+ if (prec > 0) {
+ if (prec < slen) {
+ slen = prec;
+ }
+ }
+
+ /* and away we go */
+ return fill2(ss, s ? s : "(null)", slen, width, flags);
+}
+
+/*
+** BiuldArgArray stands for Numbered Argument list Sprintf
+** for example,
+** fmp = "%4$i, %2$d, %3s, %1d";
+** the number must start from 1, and no gap among them
+*/
+
+static struct NumArgState* BuildArgArray( const char *fmt, va_list ap, int* rv, struct NumArgState* nasArray )
+{
+ int number = 0, cn = 0, i;
+ const char* p;
+ char c;
+ struct NumArgState* nas;
+
+
+ /*
+ ** set the l10n_debug flag
+ ** this routine should be executed only once
+ ** 'cause getenv does take time
+ */
+ if( !l10n_debug_init ){
+ l10n_debug_init = PR_TRUE;
+ p = getenv( "NETSCAPE_LOCALIZATION_DEBUG" );
+ if( ( p != NULL ) && ( *p == '1' ) ){
+ l10n_debug = PR_TRUE;
+ }
+ }
+
+
+ /*
+ ** first pass:
+ ** detemine how many legal % I have got, then allocate space
+ */
+
+ p = fmt;
+ *rv = 0;
+ i = 0;
+ while( ( c = *p++ ) != 0 ){
+ if( c != '%' )
+ continue;
+ if( ( c = *p++ ) == '%' ) /* skip %% case */
+ continue;
+
+ while( c != 0 ){
+ if( c > '9' || c < '0' ){
+ if( c == '$' ){ /* numbered argument csae */
+ if( i > 0 ){
+ *rv = -1;
+ if( l10n_debug )
+ printf( "either no *OR* all arguments are numbered \"%s\"\n", fmt );
+ return NULL;
+ }
+ number++;
+ break;
+
+ } else{ /* non-numbered argument case */
+ if( number > 0 ){
+ if( l10n_debug )
+ printf( "either no *OR* all arguments are numbered \"%s\"\n", fmt );
+ *rv = -1;
+ return NULL;
+ }
+ i = 1;
+ break;
+ }
+ }
+
+ c = *p++;
+ }
+ }
+
+ if( number == 0 ){
+ return NULL;
+ }
+
+
+ if( number > NAS_DEFAULT_NUM ){
+ nas = (struct NumArgState*)PR_MALLOC( number * sizeof( struct NumArgState ) );
+ if( !nas ){
+ *rv = -1;
+ if( l10n_debug )
+ printf( "PR_MALLOC() error for \"%s\"\n", fmt );
+ return NULL;
+ }
+ } else {
+ nas = nasArray;
+ }
+
+ for( i = 0; i < number; i++ ){
+ nas[i].type = TYPE_UNKNOWN;
+ }
+
+
+ /*
+ ** second pass:
+ ** set nas[].type
+ */
+
+ p = fmt;
+ while( ( c = *p++ ) != 0 ){
+ if( c != '%' ) continue;
+ c = *p++;
+ if( c == '%' ) continue;
+
+ cn = 0;
+ while( c && c != '$' ){ /* should imporve error check later */
+ cn = cn*10 + c - '0';
+ c = *p++;
+ }
+
+ if( !c || cn < 1 || cn > number ){
+ *rv = -1;
+ if( l10n_debug )
+ printf( "invalid argument number (valid range [1, %d]), \"%s\"\n", number, fmt );
+ break;
+ }
+
+ /* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */
+ cn--;
+ if( nas[cn].type != TYPE_UNKNOWN )
+ continue;
+
+ c = *p++;
+
+ /* width */
+ if (c == '*') {
+ /* not supported feature, for the argument is not numbered */
+ *rv = -1;
+ if( l10n_debug )
+ printf( "* width specifier not support for numbered arguments \"%s\"\n", fmt );
+ break;
+ } else {
+ while ((c >= '0') && (c <= '9')) {
+ c = *p++;
+ }
+ }
+
+ /* precision */
+ if (c == '.') {
+ c = *p++;
+ if (c == '*') {
+ /* not supported feature, for the argument is not numbered */
+ if( l10n_debug )
+ printf( "* precision specifier not support for numbered arguments \"%s\"\n", fmt );
+ *rv = -1;
+ break;
+ } else {
+ while ((c >= '0') && (c <= '9')) {
+ c = *p++;
+ }
+ }
+ }
+
+ /* size */
+ nas[cn].type = TYPE_INTN;
+ if (c == 'h') {
+ nas[cn].type = TYPE_INT16;
+ c = *p++;
+ } else if (c == 'L') {
+ /* XXX not quite sure here */
+ nas[cn].type = TYPE_INT64;
+ c = *p++;
+ } else if (c == 'l') {
+ nas[cn].type = TYPE_INT32;
+ c = *p++;
+ if (c == 'l') {
+ nas[cn].type = TYPE_INT64;
+ c = *p++;
+ }
+ }
+
+ /* format */
+ switch (c) {
+ case 'd':
+ case 'c':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ break;
+
+ case 'e':
+ case 'f':
+ case 'g':
+ nas[ cn ].type = TYPE_DOUBLE;
+ break;
+
+ case 'p':
+ /* XXX should use cpp */
+ if (sizeof(void *) == sizeof(PRInt32)) {
+ nas[ cn ].type = TYPE_UINT32;
+ } else if (sizeof(void *) == sizeof(PRInt64)) {
+ nas[ cn ].type = TYPE_UINT64;
+ } else if (sizeof(void *) == sizeof(PRIntn)) {
+ nas[ cn ].type = TYPE_UINTN;
+ } else {
+ nas[ cn ].type = TYPE_UNKNOWN;
+ }
+ break;
+
+ case 'C':
+ case 'S':
+ case 'E':
+ case 'G':
+ /* XXX not supported I suppose */
+ PR_ASSERT(0);
+ nas[ cn ].type = TYPE_UNKNOWN;
+ break;
+
+ case 's':
+ nas[ cn ].type = TYPE_STRING;
+ break;
+
+ case 'n':
+ nas[ cn ].type = TYPE_INTSTR;
+ break;
+
+ default:
+ PR_ASSERT(0);
+ nas[ cn ].type = TYPE_UNKNOWN;
+ break;
+ }
+
+ /* get a legal para. */
+ if( nas[ cn ].type == TYPE_UNKNOWN ){
+ if( l10n_debug )
+ printf( "unknown type \"%s\"\n", fmt );
+ *rv = -1;
+ break;
+ }
+ }
+
+
+ /*
+ ** third pass
+ ** fill the nas[cn].ap
+ */
+
+ if( *rv < 0 ){
+ if( nas != nasArray )
+ PR_DELETE( nas );
+ return NULL;
+ }
+
+ cn = 0;
+ while( cn < number ){
+ if( nas[cn].type == TYPE_UNKNOWN ){
+ cn++;
+ continue;
+ }
+
+ VARARGS_ASSIGN(nas[cn].ap, ap);
+
+ switch( nas[cn].type ){
+ case TYPE_INT16:
+ case TYPE_UINT16:
+ case TYPE_INTN:
+ case TYPE_UINTN: (void)va_arg( ap, PRIntn ); break;
+
+ case TYPE_INT32: (void)va_arg( ap, PRInt32 ); break;
+
+ case TYPE_UINT32: (void)va_arg( ap, PRUint32 ); break;
+
+ case TYPE_INT64: (void)va_arg( ap, PRInt64 ); break;
+
+ case TYPE_UINT64: (void)va_arg( ap, PRUint64 ); break;
+
+ case TYPE_STRING: (void)va_arg( ap, char* ); break;
+
+ case TYPE_INTSTR: (void)va_arg( ap, PRIntn* ); break;
+
+ case TYPE_DOUBLE: (void)va_arg( ap, double ); break;
+
+ default:
+ if( nas != nasArray )
+ PR_DELETE( nas );
+ *rv = -1;
+ return NULL;
+ }
+
+ cn++;
+ }
+
+
+ return nas;
+}
+
+/*
+** The workhorse sprintf code.
+*/
+static int dosprintf(SprintfState *ss, const char *fmt, va_list ap)
+{
+ char c;
+ int flags, width, prec, radix, type;
+ union {
+ char ch;
+ int i;
+ long l;
+ PRInt64 ll;
+ double d;
+ const char *s;
+ int *ip;
+ } u;
+ const char *fmt0;
+ static char *hex = "0123456789abcdef";
+ static char *HEX = "0123456789ABCDEF";
+ char *hexp;
+ int rv, i;
+ struct NumArgState* nas = NULL;
+ struct NumArgState nasArray[ NAS_DEFAULT_NUM ];
+ char pattern[20];
+ const char* dolPt = NULL; /* in "%4$.2f", dolPt will poiont to . */
+
+
+ /*
+ ** build an argument array, IF the fmt is numbered argument
+ ** list style, to contain the Numbered Argument list pointers
+ */
+
+ nas = BuildArgArray( fmt, ap, &rv, nasArray );
+ if( rv < 0 ){
+ /* the fmt contains error Numbered Argument format, jliu@netscape.com */
+ PR_ASSERT(0);
+ return rv;
+ }
+
+ while ((c = *fmt++) != 0) {
+ if (c != '%') {
+ rv = (*ss->stuff)(ss, fmt - 1, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ continue;
+ }
+ fmt0 = fmt - 1;
+
+ /*
+ ** Gobble up the % format string. Hopefully we have handled all
+ ** of the strange cases!
+ */
+ flags = 0;
+ c = *fmt++;
+ if (c == '%') {
+ /* quoting a % with %% */
+ rv = (*ss->stuff)(ss, fmt - 1, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ continue;
+ }
+
+ if( nas != NULL ){
+ /* the fmt contains the Numbered Arguments feature */
+ i = 0;
+ while( c && c != '$' ){ /* should imporve error check later */
+ i = ( i * 10 ) + ( c - '0' );
+ c = *fmt++;
+ }
+
+ if( nas[i-1].type == TYPE_UNKNOWN ){
+ if( l10n_debug )
+ printf( "numbered argument type unknown\n" );
+ if( nas && ( nas != nasArray ) )
+ PR_DELETE( nas );
+ return -1;
+ }
+
+ ap = nas[i-1].ap;
+ dolPt = fmt;
+ c = *fmt++;
+ }
+
+ /*
+ * Examine optional flags. Note that we do not implement the
+ * '#' flag of sprintf(). The ANSI C spec. of the '#' flag is
+ * somewhat ambiguous and not ideal, which is perhaps why
+ * the various sprintf() implementations are inconsistent
+ * on this feature.
+ */
+ while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
+ if (c == '-') flags |= _LEFT;
+ if (c == '+') flags |= _SIGNED;
+ if (c == ' ') flags |= _SPACED;
+ if (c == '0') flags |= _ZEROS;
+ c = *fmt++;
+ }
+ if (flags & _SIGNED) flags &= ~_SPACED;
+ if (flags & _LEFT) flags &= ~_ZEROS;
+
+ /* width */
+ if (c == '*') {
+ c = *fmt++;
+ width = va_arg(ap, int);
+ } else {
+ width = 0;
+ while ((c >= '0') && (c <= '9')) {
+ width = (width * 10) + (c - '0');
+ c = *fmt++;
+ }
+ }
+
+ /* precision */
+ prec = -1;
+ if (c == '.') {
+ c = *fmt++;
+ if (c == '*') {
+ c = *fmt++;
+ prec = va_arg(ap, int);
+ } else {
+ prec = 0;
+ while ((c >= '0') && (c <= '9')) {
+ prec = (prec * 10) + (c - '0');
+ c = *fmt++;
+ }
+ }
+ }
+
+ /* size */
+ type = TYPE_INTN;
+ if (c == 'h') {
+ type = TYPE_INT16;
+ c = *fmt++;
+ } else if (c == 'L') {
+ /* XXX not quite sure here */
+ type = TYPE_INT64;
+ c = *fmt++;
+ } else if (c == 'l') {
+ type = TYPE_INT32;
+ c = *fmt++;
+ if (c == 'l') {
+ type = TYPE_INT64;
+ c = *fmt++;
+ }
+ }
+
+ /* format */
+ hexp = hex;
+ switch (c) {
+ case 'd': case 'i': /* decimal/integer */
+ radix = 10;
+ goto fetch_and_convert;
+
+ case 'o': /* octal */
+ radix = 8;
+ type |= 1;
+ goto fetch_and_convert;
+
+ case 'u': /* unsigned decimal */
+ radix = 10;
+ type |= 1;
+ goto fetch_and_convert;
+
+ case 'x': /* unsigned hex */
+ radix = 16;
+ type |= 1;
+ goto fetch_and_convert;
+
+ case 'X': /* unsigned HEX */
+ radix = 16;
+ hexp = HEX;
+ type |= 1;
+ goto fetch_and_convert;
+
+ fetch_and_convert:
+ switch (type) {
+ case TYPE_INT16:
+ u.l = va_arg(ap, int);
+ if (u.l < 0) {
+ u.l = -u.l;
+ flags |= _NEG;
+ }
+ goto do_long;
+ case TYPE_UINT16:
+ u.l = va_arg(ap, int) & 0xffff;
+ goto do_long;
+ case TYPE_INTN:
+ u.l = va_arg(ap, int);
+ if (u.l < 0) {
+ u.l = -u.l;
+ flags |= _NEG;
+ }
+ goto do_long;
+ case TYPE_UINTN:
+ u.l = (long)va_arg(ap, unsigned int);
+ goto do_long;
+
+ case TYPE_INT32:
+ u.l = va_arg(ap, PRInt32);
+ if (u.l < 0) {
+ u.l = -u.l;
+ flags |= _NEG;
+ }
+ goto do_long;
+ case TYPE_UINT32:
+ u.l = (long)va_arg(ap, PRUint32);
+ do_long:
+ rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp);
+ if (rv < 0) {
+ return rv;
+ }
+ break;
+
+ case TYPE_INT64:
+ u.ll = va_arg(ap, PRInt64);
+ if (!LL_GE_ZERO(u.ll)) {
+ LL_NEG(u.ll, u.ll);
+ flags |= _NEG;
+ }
+ goto do_longlong;
+ case TYPE_UINT64:
+ u.ll = va_arg(ap, PRUint64);
+ do_longlong:
+ rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp);
+ if (rv < 0) {
+ return rv;
+ }
+ break;
+ }
+ break;
+
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'g':
+ u.d = va_arg(ap, double);
+ if( nas != NULL ){
+ i = fmt - dolPt;
+ if( i < sizeof( pattern ) ){
+ pattern[0] = '%';
+ memcpy( &pattern[1], dolPt, i );
+ rv = cvt_f(ss, u.d, pattern, &pattern[i+1] );
+ }
+ } else
+ rv = cvt_f(ss, u.d, fmt0, fmt);
+
+ if (rv < 0) {
+ return rv;
+ }
+ break;
+
+ case 'c':
+ u.ch = va_arg(ap, int);
+ if ((flags & _LEFT) == 0) {
+ while (width-- > 1) {
+ rv = (*ss->stuff)(ss, " ", 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ }
+ rv = (*ss->stuff)(ss, &u.ch, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ if (flags & _LEFT) {
+ while (width-- > 1) {
+ rv = (*ss->stuff)(ss, " ", 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ }
+ break;
+
+ case 'p':
+ if (sizeof(void *) == sizeof(PRInt32)) {
+ type = TYPE_UINT32;
+ } else if (sizeof(void *) == sizeof(PRInt64)) {
+ type = TYPE_UINT64;
+ } else if (sizeof(void *) == sizeof(int)) {
+ type = TYPE_UINTN;
+ } else {
+ PR_ASSERT(0);
+ break;
+ }
+ radix = 16;
+ goto fetch_and_convert;
+
+#if 0
+ case 'C':
+ case 'S':
+ case 'E':
+ case 'G':
+ /* XXX not supported I suppose */
+ PR_ASSERT(0);
+ break;
+#endif
+
+ case 's':
+ u.s = va_arg(ap, const char*);
+ rv = cvt_s(ss, u.s, width, prec, flags);
+ if (rv < 0) {
+ return rv;
+ }
+ break;
+
+ case 'n':
+ u.ip = va_arg(ap, int*);
+ if (u.ip) {
+ *u.ip = ss->cur - ss->base;
+ }
+ break;
+
+ default:
+ /* Not a % token after all... skip it */
+#if 0
+ PR_ASSERT(0);
+#endif
+ rv = (*ss->stuff)(ss, "%", 1);
+ if (rv < 0) {
+ return rv;
+ }
+ rv = (*ss->stuff)(ss, fmt - 1, 1);
+ if (rv < 0) {
+ return rv;
+ }
+ }
+ }
+
+ /* Stuff trailing NUL */
+ rv = (*ss->stuff)(ss, "\0", 1);
+
+ if( nas && ( nas != nasArray ) ){
+ PR_DELETE( nas );
+ }
+
+ return rv;
+}
+
+/************************************************************************/
+
+static int FuncStuff(SprintfState *ss, const char *sp, PRUint32 len)
+{
+ int rv;
+
+ rv = (*ss->func)(ss->arg, sp, len);
+ if (rv < 0) {
+ return rv;
+ }
+ ss->maxlen += len;
+ return 0;
+}
+
+PR_IMPLEMENT(PRUint32) PR_sxprintf(PRStuffFunc func, void *arg,
+ const char *fmt, ...)
+{
+ va_list ap;
+ int rv;
+
+ va_start(ap, fmt);
+ rv = PR_vsxprintf(func, arg, fmt, ap);
+ va_end(ap);
+ return rv;
+}
+
+PR_IMPLEMENT(PRUint32) PR_vsxprintf(PRStuffFunc func, void *arg,
+ const char *fmt, va_list ap)
+{
+ SprintfState ss;
+ int rv;
+
+ ss.stuff = FuncStuff;
+ ss.func = func;
+ ss.arg = arg;
+ ss.maxlen = 0;
+ rv = dosprintf(&ss, fmt, ap);
+ return (rv < 0) ? (PRUint32)-1 : ss.maxlen;
+}
+
+/*
+** Stuff routine that automatically grows the malloc'd output buffer
+** before it overflows.
+*/
+static int GrowStuff(SprintfState *ss, const char *sp, PRUint32 len)
+{
+ ptrdiff_t off;
+ char *newbase;
+ PRUint32 newlen;
+
+ off = ss->cur - ss->base;
+ if (off + len >= ss->maxlen) {
+ /* Grow the buffer */
+ newlen = ss->maxlen + ((len > 32) ? len : 32);
+ if (ss->base) {
+ newbase = (char*) PR_REALLOC(ss->base, newlen);
+ } else {
+ newbase = (char*) PR_MALLOC(newlen);
+ }
+ if (!newbase) {
+ /* Ran out of memory */
+ return -1;
+ }
+ ss->base = newbase;
+ ss->maxlen = newlen;
+ ss->cur = ss->base + off;
+ }
+
+ /* Copy data */
+ while (len) {
+ --len;
+ *ss->cur++ = *sp++;
+ }
+ PR_ASSERT((PRUint32)(ss->cur - ss->base) <= ss->maxlen);
+ return 0;
+}
+
+/*
+** sprintf into a malloc'd buffer
+*/
+PR_IMPLEMENT(char *) PR_smprintf(const char *fmt, ...)
+{
+ va_list ap;
+ char *rv;
+
+ va_start(ap, fmt);
+ rv = PR_vsmprintf(fmt, ap);
+ va_end(ap);
+ return rv;
+}
+
+/*
+** Free memory allocated, for the caller, by PR_smprintf
+*/
+PR_IMPLEMENT(void) PR_smprintf_free(char *mem)
+{
+ PR_DELETE(mem);
+}
+
+PR_IMPLEMENT(char *) PR_vsmprintf(const char *fmt, va_list ap)
+{
+ SprintfState ss;
+ int rv;
+
+ ss.stuff = GrowStuff;
+ ss.base = 0;
+ ss.cur = 0;
+ ss.maxlen = 0;
+ rv = dosprintf(&ss, fmt, ap);
+ if (rv < 0) {
+ if (ss.base) {
+ PR_DELETE(ss.base);
+ }
+ return 0;
+ }
+ return ss.base;
+}
+
+/*
+** Stuff routine that discards overflow data
+*/
+static int LimitStuff(SprintfState *ss, const char *sp, PRUint32 len)
+{
+ PRUint32 limit = ss->maxlen - (ss->cur - ss->base);
+
+ if (len > limit) {
+ len = limit;
+ }
+ while (len) {
+ --len;
+ *ss->cur++ = *sp++;
+ }
+ return 0;
+}
+
+/*
+** sprintf into a fixed size buffer. Make sure there is a NUL at the end
+** when finished.
+*/
+PR_IMPLEMENT(PRUint32) PR_snprintf(char *out, PRUint32 outlen, const char *fmt, ...)
+{
+ va_list ap;
+ int rv;
+
+ PR_ASSERT((PRInt32)outlen > 0);
+ if ((PRInt32)outlen <= 0) {
+ return 0;
+ }
+
+ va_start(ap, fmt);
+ rv = PR_vsnprintf(out, outlen, fmt, ap);
+ va_end(ap);
+ return rv;
+}
+
+PR_IMPLEMENT(PRUint32) PR_vsnprintf(char *out, PRUint32 outlen,const char *fmt,
+ va_list ap)
+{
+ SprintfState ss;
+ PRUint32 n;
+
+ PR_ASSERT((PRInt32)outlen > 0);
+ if ((PRInt32)outlen <= 0) {
+ return 0;
+ }
+
+ ss.stuff = LimitStuff;
+ ss.base = out;
+ ss.cur = out;
+ ss.maxlen = outlen;
+ (void) dosprintf(&ss, fmt, ap);
+
+ /* If we added chars, and we didn't append a null, do it now. */
+ if( (ss.cur != ss.base) && (*(ss.cur - 1) != '\0') )
+ *(--ss.cur) = '\0';
+
+ n = ss.cur - ss.base;
+ return n ? n - 1 : n;
+}
+
+PR_IMPLEMENT(char *) PR_sprintf_append(char *last, const char *fmt, ...)
+{
+ va_list ap;
+ char *rv;
+
+ va_start(ap, fmt);
+ rv = PR_vsprintf_append(last, fmt, ap);
+ va_end(ap);
+ return rv;
+}
+
+PR_IMPLEMENT(char *) PR_vsprintf_append(char *last, const char *fmt, va_list ap)
+{
+ SprintfState ss;
+ int rv;
+
+ ss.stuff = GrowStuff;
+ if (last) {
+ int lastlen = strlen(last);
+ ss.base = last;
+ ss.cur = last + lastlen;
+ ss.maxlen = lastlen;
+ } else {
+ ss.base = 0;
+ ss.cur = 0;
+ ss.maxlen = 0;
+ }
+ rv = dosprintf(&ss, fmt, ap);
+ if (rv < 0) {
+ if (ss.base) {
+ PR_DELETE(ss.base);
+ }
+ return 0;
+ }
+ return ss.base;
+}
+
diff --git a/pr/src/io/prscanf.c b/pr/src/io/prscanf.c
new file mode 100644
index 00000000..4a23543f
--- /dev/null
+++ b/pr/src/io/prscanf.c
@@ -0,0 +1,637 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * Scan functions for NSPR types
+ *
+ * Author: Wan-Teh Chang
+ *
+ * Acknowledgment: The implementation is inspired by the source code
+ * in P.J. Plauger's "The Standard C Library," Prentice-Hall, 1992.
+ */
+
+#include <limits.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include "prprf.h"
+#include "prdtoa.h"
+#include "prlog.h"
+#include "prerror.h"
+
+/*
+ * A function that reads a character from 'stream'.
+ * Returns the character read, or EOF if end of stream is reached.
+ */
+typedef int (*_PRGetCharFN)(void *stream);
+
+/*
+ * A function that pushes the character 'ch' back to 'stream'.
+ */
+typedef void (*_PRUngetCharFN)(void *stream, int ch);
+
+/*
+ * The size specifier for the integer and floating point number
+ * conversions in format control strings.
+ */
+typedef enum {
+ _PR_size_none, /* No size specifier is given */
+ _PR_size_h, /* The 'h' specifier, suggesting "short" */
+ _PR_size_l, /* The 'l' specifier, suggesting "long" */
+ _PR_size_L, /* The 'L' specifier, meaning a 'long double' */
+ _PR_size_ll /* The 'll' specifier, suggesting "long long" */
+} _PRSizeSpec;
+
+/*
+ * The collection of data that is passed between the scan function
+ * and its subordinate functions. The fields of this structure
+ * serve as the input or output arguments for these functions.
+ */
+typedef struct {
+ _PRGetCharFN get; /* get a character from input stream */
+ _PRUngetCharFN unget; /* unget (push back) a character */
+ void *stream; /* argument for get and unget */
+ va_list ap; /* the variable argument list */
+ int nChar; /* number of characters read from 'stream' */
+
+ PRBool assign; /* assign, or suppress assignment? */
+ int width; /* field width */
+ _PRSizeSpec sizeSpec; /* 'h', 'l', 'L', or 'll' */
+
+ PRBool converted; /* is the value actually converted? */
+} ScanfState;
+
+#define GET(state) ((state)->nChar++, (state)->get((state)->stream))
+#define UNGET(state, ch) \
+ ((state)->nChar--, (state)->unget((state)->stream, ch))
+
+/*
+ * The following two macros, GET_IF_WITHIN_WIDTH and WITHIN_WIDTH,
+ * are always used together.
+ *
+ * GET_IF_WITHIN_WIDTH calls the GET macro and assigns its return
+ * value to 'ch' only if we have not exceeded the field width of
+ * 'state'. Therefore, after GET_IF_WITHIN_WIDTH, the value of
+ * 'ch' is valid only if the macro WITHIN_WIDTH evaluates to true.
+ */
+
+#define GET_IF_WITHIN_WIDTH(state, ch) \
+ if (--(state)->width >= 0) { \
+ (ch) = GET(state); \
+ }
+#define WITHIN_WIDTH(state) ((state)->width >= 0)
+
+/*
+ * _pr_strtoull:
+ * Convert a string to an unsigned 64-bit integer. The string
+ * 'str' is assumed to be a representation of the integer in
+ * base 'base'.
+ *
+ * Warning:
+ * - Only handle base 8, 10, and 16.
+ * - No overflow checking.
+ */
+
+static PRUint64
+_pr_strtoull(const char *str, char **endptr, int base)
+{
+ static const int BASE_MAX = 16;
+ static const char digits[] = "0123456789abcdef";
+ char *digitPtr;
+ PRUint64 x; /* return value */
+ PRInt64 base64;
+ const char *cPtr;
+ PRBool negative;
+ const char *digitStart;
+
+ PR_ASSERT(base == 0 || base == 8 || base == 10 || base == 16);
+ if (base < 0 || base == 1 || base > BASE_MAX) {
+ if (endptr) {
+ *endptr = (char *) str;
+ return LL_ZERO;
+ }
+ }
+
+ cPtr = str;
+ while (isspace(*cPtr)) {
+ ++cPtr;
+ }
+
+ negative = PR_FALSE;
+ if (*cPtr == '-') {
+ negative = PR_TRUE;
+ cPtr++;
+ } else if (*cPtr == '+') {
+ cPtr++;
+ }
+
+ if (base == 16) {
+ if (*cPtr == '0' && (cPtr[1] == 'x' || cPtr[1] == 'X')) {
+ cPtr += 2;
+ }
+ } else if (base == 0) {
+ if (*cPtr != '0') {
+ base = 10;
+ } else if (cPtr[1] == 'x' || cPtr[1] == 'X') {
+ base = 16;
+ cPtr += 2;
+ } else {
+ base = 8;
+ }
+ }
+ PR_ASSERT(base != 0);
+ LL_I2L(base64, base);
+ digitStart = cPtr;
+
+ /* Skip leading zeros */
+ while (*cPtr == '0') {
+ cPtr++;
+ }
+
+ LL_I2L(x, 0);
+ while ((digitPtr = (char*)memchr(digits, tolower(*cPtr), base)) != NULL) {
+ PRUint64 d;
+
+ LL_I2L(d, (digitPtr - digits));
+ LL_MUL(x, x, base64);
+ LL_ADD(x, x, d);
+ cPtr++;
+ }
+
+ if (cPtr == digitStart) {
+ if (endptr) {
+ *endptr = (char *) str;
+ }
+ return LL_ZERO;
+ }
+
+ if (negative) {
+ LL_NEG(x, x);
+ }
+
+ if (endptr) {
+ *endptr = (char *) cPtr;
+ }
+ return x;
+}
+
+/*
+ * The maximum field width (in number of characters) that is enough
+ * (may be more than necessary) to represent a 64-bit integer or
+ * floating point number.
+ */
+#define FMAX 31
+#define DECIMAL_POINT '.'
+
+static PRStatus
+GetInt(ScanfState *state, int code)
+{
+ char buf[FMAX + 1], *p;
+ int ch;
+ static const char digits[] = "0123456789abcdefABCDEF";
+ PRBool seenDigit = PR_FALSE;
+ int base;
+ int dlen;
+
+ switch (code) {
+ case 'd': case 'u':
+ base = 10;
+ break;
+ case 'i':
+ base = 0;
+ break;
+ case 'x': case 'X': case 'p':
+ base = 16;
+ break;
+ case 'o':
+ base = 8;
+ break;
+ default:
+ return PR_FAILURE;
+ }
+ if (state->width == 0 || state->width > FMAX) {
+ state->width = FMAX;
+ }
+ p = buf;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) {
+ *p++ = ch;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ }
+ if (WITHIN_WIDTH(state) && ch == '0') {
+ seenDigit = PR_TRUE;
+ *p++ = ch;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ if (WITHIN_WIDTH(state)
+ && (ch == 'x' || ch == 'X')
+ && (base == 0 || base == 16)) {
+ base = 16;
+ *p++ = ch;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ } else if (base == 0) {
+ base = 8;
+ }
+ }
+ if (base == 0 || base == 10) {
+ dlen = 10;
+ } else if (base == 8) {
+ dlen = 8;
+ } else {
+ PR_ASSERT(base == 16);
+ dlen = 16 + 6; /* 16 digits, plus 6 in uppercase */
+ }
+ while (WITHIN_WIDTH(state) && memchr(digits, ch, dlen)) {
+ *p++ = ch;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ seenDigit = PR_TRUE;
+ }
+ if (WITHIN_WIDTH(state)) {
+ UNGET(state, ch);
+ }
+ if (!seenDigit) {
+ return PR_FAILURE;
+ }
+ *p = '\0';
+ if (state->assign) {
+ if (code == 'd' || code == 'i') {
+ if (state->sizeSpec == _PR_size_ll) {
+ PRInt64 llval = _pr_strtoull(buf, NULL, base);
+ *va_arg(state->ap, PRInt64 *) = llval;
+ } else {
+ long lval = strtol(buf, NULL, base);
+
+ if (state->sizeSpec == _PR_size_none) {
+ *va_arg(state->ap, PRIntn *) = lval;
+ } else if (state->sizeSpec == _PR_size_h) {
+ *va_arg(state->ap, PRInt16 *) = (PRInt16)lval;
+ } else if (state->sizeSpec == _PR_size_l) {
+ *va_arg(state->ap, PRInt32 *) = lval;
+ } else {
+ return PR_FAILURE;
+ }
+ }
+ } else {
+ if (state->sizeSpec == _PR_size_ll) {
+ PRUint64 llval = _pr_strtoull(buf, NULL, base);
+ *va_arg(state->ap, PRUint64 *) = llval;
+ } else {
+ unsigned long lval = strtoul(buf, NULL, base);
+
+ if (state->sizeSpec == _PR_size_none) {
+ *va_arg(state->ap, PRUintn *) = lval;
+ } else if (state->sizeSpec == _PR_size_h) {
+ *va_arg(state->ap, PRUint16 *) = (PRUint16)lval;
+ } else if (state->sizeSpec == _PR_size_l) {
+ *va_arg(state->ap, PRUint32 *) = lval;
+ } else {
+ return PR_FAILURE;
+ }
+ }
+ }
+ state->converted = PR_TRUE;
+ }
+ return PR_SUCCESS;
+}
+
+static PRStatus
+GetFloat(ScanfState *state)
+{
+ char buf[FMAX + 1], *p;
+ int ch;
+ PRBool seenDigit = PR_FALSE;
+
+ if (state->width == 0 || state->width > FMAX) {
+ state->width = FMAX;
+ }
+ p = buf;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) {
+ *p++ = ch;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ }
+ while (WITHIN_WIDTH(state) && isdigit(ch)) {
+ *p++ = ch;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ seenDigit = PR_TRUE;
+ }
+ if (WITHIN_WIDTH(state) && ch == DECIMAL_POINT) {
+ *p++ = ch;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ while (WITHIN_WIDTH(state) && isdigit(ch)) {
+ *p++ = ch;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ seenDigit = PR_TRUE;
+ }
+ }
+
+ /*
+ * This is not robust. For example, "1.2e+" would confuse
+ * the code below to read 'e' and '+', only to realize that
+ * it should have stopped at "1.2". But we can't push back
+ * more than one character, so there is nothing I can do.
+ */
+
+ /* Parse exponent */
+ if (WITHIN_WIDTH(state) && (ch == 'e' || ch == 'E') && seenDigit) {
+ *p++ = ch;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) {
+ *p++ = ch;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ }
+ while (WITHIN_WIDTH(state) && isdigit(ch)) {
+ *p++ = ch;
+ GET_IF_WITHIN_WIDTH(state, ch);
+ }
+ }
+ if (WITHIN_WIDTH(state)) {
+ UNGET(state, ch);
+ }
+ if (!seenDigit) {
+ return PR_FAILURE;
+ }
+ *p = '\0';
+ if (state->assign) {
+ PRFloat64 dval = PR_strtod(buf, NULL);
+
+ state->converted = PR_TRUE;
+ if (state->sizeSpec == _PR_size_l) {
+ *va_arg(state->ap, PRFloat64 *) = dval;
+ } else if (state->sizeSpec == _PR_size_L) {
+#if defined(OSF1) || defined(IRIX)
+ *va_arg(state->ap, double *) = dval;
+#else
+ *va_arg(state->ap, long double *) = dval;
+#endif
+ } else {
+ *va_arg(state->ap, float *) = dval;
+ }
+ }
+ return PR_SUCCESS;
+}
+
+/*
+ * Convert, and return the end of the conversion spec.
+ */
+
+static const char *
+Convert(ScanfState *state, const char *fmt)
+{
+ const char *cPtr;
+ int ch;
+ char *cArg;
+
+ state->converted = PR_FALSE;
+ cPtr = fmt;
+ if (*cPtr != 'c' && *cPtr != 'n' && *cPtr != '[') {
+ do {
+ ch = GET(state);
+ } while (isspace(ch));
+ UNGET(state, ch);
+ }
+ switch (*cPtr) {
+ case 'c':
+ if (state->assign) {
+ cArg = va_arg(state->ap, char *);
+ }
+ if (state->width == 0) {
+ state->width = 1;
+ }
+ for (; state->width > 0; state->width--) {
+ ch = GET(state);
+ if (ch == EOF) {
+ return NULL;
+ } else if (state->assign) {
+ *cArg++ = ch;
+ }
+ }
+ if (state->assign) {
+ state->converted = PR_TRUE;
+ }
+ break;
+ case 'p':
+ case 'd': case 'i': case 'o':
+ case 'u': case 'x': case 'X':
+ if (GetInt(state, *cPtr) == PR_FAILURE) {
+ return NULL;
+ }
+ break;
+ case 'e': case 'E': case 'f':
+ case 'g': case 'G':
+ if (GetFloat(state) == PR_FAILURE) {
+ return NULL;
+ }
+ break;
+ case 'n':
+ /* do not consume any input */
+ if (state->assign) {
+ switch (state->sizeSpec) {
+ case _PR_size_none:
+ *va_arg(state->ap, PRIntn *) = state->nChar;
+ break;
+ case _PR_size_h:
+ *va_arg(state->ap, PRInt16 *) = state->nChar;
+ break;
+ case _PR_size_l:
+ *va_arg(state->ap, PRInt32 *) = state->nChar;
+ break;
+ case _PR_size_ll:
+ LL_I2L(*va_arg(state->ap, PRInt64 *), state->nChar);
+ break;
+ default:
+ PR_ASSERT(0);
+ }
+ }
+ break;
+ case 's':
+ if (state->width == 0) {
+ state->width = INT_MAX;
+ }
+ if (state->assign) {
+ cArg = va_arg(state->ap, char *);
+ }
+ for (; state->width > 0; state->width--) {
+ ch = GET(state);
+ if ((ch == EOF) || isspace(ch)) {
+ UNGET(state, ch);
+ break;
+ }
+ if (state->assign) {
+ *cArg++ = ch;
+ }
+ }
+ if (state->assign) {
+ *cArg = '\0';
+ state->converted = PR_TRUE;
+ }
+ break;
+ case '%':
+ ch = GET(state);
+ if (ch != '%') {
+ UNGET(state, ch);
+ return NULL;
+ }
+ break;
+ case '[':
+ {
+ PRBool complement = PR_FALSE;
+ const char *closeBracket;
+ size_t n;
+
+ if (*++cPtr == '^') {
+ complement = PR_TRUE;
+ cPtr++;
+ }
+ closeBracket = strchr(*cPtr == ']' ? cPtr + 1 : cPtr, ']');
+ if (closeBracket == NULL) {
+ return NULL;
+ }
+ n = closeBracket - cPtr;
+ if (state->width == 0) {
+ state->width = INT_MAX;
+ }
+ if (state->assign) {
+ cArg = va_arg(state->ap, char *);
+ }
+ for (; state->width > 0; state->width--) {
+ ch = GET(state);
+ if ((ch == EOF)
+ || (!complement && !memchr(cPtr, ch, n))
+ || (complement && memchr(cPtr, ch, n))) {
+ UNGET(state, ch);
+ break;
+ }
+ if (state->assign) {
+ *cArg++ = ch;
+ }
+ }
+ if (state->assign) {
+ *cArg = '\0';
+ state->converted = PR_TRUE;
+ }
+ cPtr = closeBracket;
+ }
+ break;
+ default:
+ return NULL;
+ }
+ return cPtr;
+}
+
+static PRInt32
+DoScanf(ScanfState *state, const char *fmt)
+{
+ PRInt32 nConverted = 0;
+ const char *cPtr;
+ int ch;
+
+ state->nChar = 0;
+ cPtr = fmt;
+ while (1) {
+ if (isspace(*cPtr)) {
+ do {
+ cPtr++;
+ } while (isspace(*cPtr));
+ do {
+ ch = GET(state);
+ } while (isspace(ch));
+ UNGET(state, ch);
+ } else if (*cPtr != '%') {
+ if (*cPtr == '\0') {
+ return nConverted;
+ }
+ ch = GET(state);
+ if (ch != *cPtr) {
+ UNGET(state, ch);
+ return nConverted;
+ }
+ cPtr++;
+ } else {
+ cPtr++;
+ state->assign = PR_TRUE;
+ if (*cPtr == '*') {
+ state->assign = PR_FALSE;
+ }
+ for (state->width = 0; isdigit(*cPtr); cPtr++) {
+ state->width = state->width * 10 + *cPtr - '0';
+ }
+ state->sizeSpec = _PR_size_none;
+ if (*cPtr == 'h') {
+ cPtr++;
+ state->sizeSpec = _PR_size_h;
+ } else if (*cPtr == 'l') {
+ cPtr++;
+ if (*cPtr == 'l') {
+ cPtr++;
+ state->sizeSpec = _PR_size_ll;
+ } else {
+ state->sizeSpec = _PR_size_l;
+ }
+ } else if (*cPtr == 'L') {
+ cPtr++;
+ state->sizeSpec = _PR_size_L;
+ }
+ cPtr = Convert(state, cPtr);
+ if (cPtr == NULL) {
+ return (nConverted > 0 ? nConverted : EOF);
+ }
+ if (state->converted) {
+ nConverted++;
+ }
+ cPtr++;
+ }
+ }
+}
+
+static int
+StringGetChar(void *stream)
+{
+ char *cPtr = *((char **) stream);
+
+ if (*cPtr == '\0') {
+ return EOF;
+ } else {
+ *((char **) stream) = cPtr + 1;
+ return *cPtr;
+ }
+}
+
+static void
+StringUngetChar(void *stream, int ch)
+{
+ char *cPtr = *((char **) stream);
+
+ if (ch != EOF) {
+ *((char **) stream) = cPtr - 1;
+ }
+}
+
+PR_IMPLEMENT(PRInt32)
+PR_sscanf(const char *buf, const char *fmt, ...)
+{
+ PRInt32 rv;
+ ScanfState state;
+
+ state.get = &StringGetChar;
+ state.unget = &StringUngetChar;
+ state.stream = (void *) &buf;
+ va_start(state.ap, fmt);
+ rv = DoScanf(&state, fmt);
+ va_end(state.ap);
+ return rv;
+}
diff --git a/pr/src/io/prsocket.c b/pr/src/io/prsocket.c
new file mode 100644
index 00000000..53746ecd
--- /dev/null
+++ b/pr/src/io/prsocket.c
@@ -0,0 +1,1519 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+#ifdef XP_UNIX
+#include <fcntl.h>
+#endif
+#include <string.h>
+
+#if defined(SVR4) || defined(SUNOS4)
+/* To pick up FIONREAD */
+#include <sys/filio.h>
+#endif
+
+/************************************************************************/
+
+static PRInt32 PR_CALLBACK SocketWritev(PRFileDesc *fd, PRIOVec *iov, PRInt32 iov_size,
+PRIntervalTime timeout)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ int w = 0;
+ PRIOVec *tmp_iov = NULL;
+ int tmp_out;
+ int index, iov_cnt;
+ int count=0, sz = 0; /* 'count' is the return value. */
+#if defined(XP_UNIX)
+ struct timeval tv, *tvp;
+ fd_set wd;
+
+ FD_ZERO(&wd);
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ tvp = NULL;
+ else if (timeout != PR_INTERVAL_NO_WAIT) {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+#endif
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return -1;
+ }
+
+ tmp_iov = (PRIOVec *)PR_CALLOC(iov_size * sizeof(PRIOVec));
+ if (!tmp_iov) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+
+ for (index=0; index<iov_size; index++) {
+ sz += iov[index].iov_len;
+ tmp_iov[index].iov_base = iov[index].iov_base;
+ tmp_iov[index].iov_len = iov[index].iov_len;
+ }
+ iov_cnt = iov_size;
+
+ while (sz > 0) {
+
+ w = _PR_MD_WRITEV(fd, tmp_iov, iov_cnt, timeout);
+ if (w < 0) {
+ count = -1;
+ break;
+ }
+ count += w;
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ sz -= w;
+
+ if (sz > 0) {
+ /* find the next unwritten vector */
+ for ( index = 0, tmp_out = count;
+ tmp_out >= iov[index].iov_len;
+ tmp_out -= iov[index].iov_len, index++){;} /* nothing to execute */
+
+
+ /* fill in the first partial read */
+ tmp_iov[0].iov_base = &(((char *)iov[index].iov_base)[tmp_out]);
+ tmp_iov[0].iov_len = iov[index].iov_len - tmp_out;
+ index++;
+
+ /* copy the remaining vectors */
+ for (iov_cnt=1; index<iov_size; iov_cnt++, index++) {
+ tmp_iov[iov_cnt].iov_base = iov[index].iov_base;
+ tmp_iov[iov_cnt].iov_len = iov[index].iov_len;
+ }
+ }
+ }
+
+ if (tmp_iov)
+ PR_DELETE(tmp_iov);
+ return count;
+}
+
+/************************************************************************/
+
+PR_IMPLEMENT(PRFileDesc *) PR_ImportTCPSocket(PRInt32 osfd)
+{
+PRFileDesc *fd;
+
+ fd = PR_AllocFileDesc(osfd, PR_GetTCPMethods());
+ if (fd != NULL)
+ _PR_MD_MAKE_NONBLOCK(fd);
+ else
+ _PR_MD_CLOSE_SOCKET(osfd);
+ return(fd);
+}
+
+PR_IMPLEMENT(PRFileDesc *) PR_ImportUDPSocket(PRInt32 osfd)
+{
+PRFileDesc *fd;
+
+ fd = PR_AllocFileDesc(osfd, PR_GetUDPMethods());
+ if (fd != NULL)
+ _PR_MD_MAKE_NONBLOCK(fd);
+ else
+ _PR_MD_CLOSE_SOCKET(osfd);
+ return(fd);
+}
+
+static PRStatus PR_CALLBACK SocketConnect(
+ PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PRInt32 rv; /* Return value of _PR_MD_CONNECT */
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ rv = _PR_MD_CONNECT(fd, addr, PR_NETADDR_SIZE(addr), timeout);
+ PR_LOG(_pr_io_lm, PR_LOG_MAX, ("connect -> %d", rv));
+ if (rv == 0)
+ return PR_SUCCESS;
+ else
+ return PR_FAILURE;
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetConnectStatus(const PRPollDesc *pd)
+{
+ PRInt32 osfd;
+ PRFileDesc *bottom = pd->fd;
+ int err, len;
+
+ if (pd->out_flags & PR_POLL_NVAL) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+ return PR_FAILURE;
+ }
+ if ((pd->out_flags & (PR_POLL_WRITE | PR_POLL_EXCEPT | PR_POLL_ERR)) == 0) {
+ PR_ASSERT(pd->out_flags == 0);
+ PR_SetError(PR_IN_PROGRESS_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ osfd = bottom->secret->md.osfd;
+
+#if defined(XP_UNIX)
+
+ err = _MD_unix_get_nonblocking_connect_error(osfd);
+ if (err != 0) {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+
+#elif defined(WIN32) || defined(WIN16)
+
+ if (pd->out_flags & PR_POLL_EXCEPT) {
+#if defined(WIN32)
+/* Note: There is a bug in Win32 WinSock. The sleep circumvents the
+** bug. See wtc. /s lth.
+*/
+ Sleep(0);
+#endif /* WIN32 */
+ len = sizeof(err);
+ if (getsockopt(osfd, (int)SOL_SOCKET, SO_ERROR, (char *) &err, &len)
+ == SOCKET_ERROR) {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+ if (err != 0) {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ } else {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ }
+ return PR_FAILURE;
+ }
+
+ PR_ASSERT(pd->out_flags & PR_POLL_WRITE);
+ return PR_SUCCESS;
+
+#elif defined(XP_OS2)
+
+ if (pd->out_flags & PR_POLL_EXCEPT) {
+ len = sizeof(err);
+ if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, (char *) &err, &len)
+ < 0) {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(sock_errno());
+ return PR_FAILURE;
+ }
+ if (err != 0) {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ } else {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ }
+ return PR_FAILURE;
+ }
+
+ PR_ASSERT(pd->out_flags & PR_POLL_WRITE);
+ return PR_SUCCESS;
+
+#elif defined(XP_MAC)
+
+ err = _MD_mac_get_nonblocking_connect_error(osfd);
+ if (err == -1)
+ return PR_FAILURE;
+ else
+ return PR_SUCCESS;
+
+#else
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+#endif
+}
+
+static PRFileDesc* PR_CALLBACK SocketAccept(PRFileDesc *fd, PRNetAddr *addr,
+PRIntervalTime timeout)
+{
+ PRInt32 osfd;
+ PRFileDesc *fd2;
+ PRUint32 al;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+#ifdef WINNT
+ PRNetAddr addrCopy;
+#endif
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return 0;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return 0;
+ }
+
+#ifdef WINNT
+ if (addr == NULL) {
+ addr = &addrCopy;
+ }
+#endif
+ al = sizeof(PRNetAddr);
+ osfd = _PR_MD_ACCEPT(fd, addr, &al, timeout);
+ if (osfd == -1)
+ return 0;
+
+ fd2 = PR_AllocFileDesc(osfd, PR_GetTCPMethods());
+ if (!fd2) {
+ _PR_MD_CLOSE_SOCKET(osfd);
+ return NULL;
+ }
+
+ fd2->secret->nonblocking = fd->secret->nonblocking;
+#ifdef WINNT
+ fd2->secret->md.io_model_committed = PR_TRUE;
+ PR_ASSERT(al == PR_NETADDR_SIZE(addr));
+ fd2->secret->md.accepted_socket = PR_TRUE;
+ memcpy(&fd2->secret->md.peer_addr, addr, al);
+#endif
+
+ /*
+ * On some platforms, the new socket created by accept()
+ * inherits the nonblocking (or overlapped io) attribute
+ * of the listening socket. As an optimization, these
+ * platforms can skip the following _PR_MD_MAKE_NONBLOCK
+ * call.
+ */
+#if !defined(SOLARIS) && !defined(IRIX) && !defined(WINNT)
+ _PR_MD_MAKE_NONBLOCK(fd2);
+#endif
+
+ PR_ASSERT((NULL == addr) || (PR_NETADDR_SIZE(addr) == al));
+#if defined(_PR_INET6)
+ PR_ASSERT((NULL == addr) || (addr->raw.family == AF_INET)
+ || (addr->raw.family == AF_INET6));
+#else
+ PR_ASSERT((NULL == addr) || (addr->raw.family == AF_INET));
+#endif
+
+ return fd2;
+}
+
+#ifdef WINNT
+PR_IMPLEMENT(PRFileDesc*) PR_NTFast_Accept(PRFileDesc *fd, PRNetAddr *addr,
+PRIntervalTime timeout)
+{
+ PRInt32 osfd;
+ PRFileDesc *fd2;
+ PRIntn al;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRNetAddr addrCopy;
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return 0;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return 0;
+ }
+
+ if (addr == NULL) {
+ addr = &addrCopy;
+ }
+ al = PR_NETADDR_SIZE(addr);
+ osfd = _PR_MD_FAST_ACCEPT(fd, addr, &al, timeout, PR_TRUE, NULL, NULL);
+ if (osfd == -1) {
+ return 0;
+ }
+
+ fd2 = PR_AllocFileDesc(osfd, PR_GetTCPMethods());
+ if (!fd2) {
+ _PR_MD_CLOSE_SOCKET(osfd);
+ } else {
+ fd2->secret->nonblocking = fd->secret->nonblocking;
+ fd2->secret->md.io_model_committed = PR_TRUE;
+ PR_ASSERT(al == PR_NETADDR_SIZE(addr));
+ fd2->secret->md.accepted_socket = PR_TRUE;
+ memcpy(&fd2->secret->md.peer_addr, addr, al);
+ }
+ return fd2;
+}
+#endif /* WINNT */
+
+
+static PRStatus PR_CALLBACK SocketBind(PRFileDesc *fd, const PRNetAddr *addr)
+{
+ PRInt32 result;
+ int one = 1;
+
+#if defined(_PR_INET6)
+ PR_ASSERT(addr->raw.family == AF_INET || addr->raw.family == AF_INET6);
+#else
+ PR_ASSERT(addr->raw.family == AF_INET);
+#endif
+
+#ifdef HAVE_SOCKET_REUSEADDR
+ if ( setsockopt (fd->secret->md.osfd, (int)SOL_SOCKET, SO_REUSEADDR,
+ (const void *)&one, sizeof(one) ) < 0) {
+ return PR_FAILURE;
+ }
+#endif
+
+ result = _PR_MD_BIND(fd, addr, PR_NETADDR_SIZE(addr));
+ if (result < 0) {
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+static PRStatus PR_CALLBACK SocketListen(PRFileDesc *fd, PRIntn backlog)
+{
+ PRInt32 result;
+
+ result = _PR_MD_LISTEN(fd, backlog);
+ if (result < 0) {
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+static PRStatus PR_CALLBACK SocketShutdown(PRFileDesc *fd, PRIntn how)
+{
+ PRInt32 result;
+
+ result = _PR_MD_SHUTDOWN(fd, how);
+ if (result < 0) {
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+static PRInt32 PR_CALLBACK SocketRecv(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+PRIntervalTime timeout)
+{
+ PRInt32 rv;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return -1;
+ }
+
+ PR_LOG(_pr_io_lm, PR_LOG_MAX, ("recv: fd=%p osfd=%d buf=%p amount=%d",
+ fd, fd->secret->md.osfd, buf, amount));
+ rv = _PR_MD_RECV(fd, buf, amount, flags, timeout);
+ PR_LOG(_pr_io_lm, PR_LOG_MAX, ("recv -> %d, error = %d, os error = %d",
+ rv, PR_GetError(), PR_GetOSError()));
+ return rv;
+}
+
+static PRInt32 PR_CALLBACK SocketRead(PRFileDesc *fd, void *buf, PRInt32 amount)
+{
+ return SocketRecv(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT);
+}
+
+static PRInt32 PR_CALLBACK SocketSend(PRFileDesc *fd, const void *buf, PRInt32 amount,
+PRIntn flags, PRIntervalTime timeout)
+{
+ PRInt32 temp, count;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return -1;
+ }
+
+ count = 0;
+ while (amount > 0) {
+ PR_LOG(_pr_io_lm, PR_LOG_MAX,
+ ("send: fd=%p osfd=%d buf=%p amount=%d",
+ fd, fd->secret->md.osfd, buf, amount));
+ temp = _PR_MD_SEND(fd, buf, amount, flags, timeout);
+ if (temp < 0) {
+ count = -1;
+ break;
+ }
+
+ count += temp;
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ buf = (const void*) ((const char*)buf + temp);
+
+ amount -= temp;
+ }
+ PR_LOG(_pr_io_lm, PR_LOG_MAX, ("send -> %d", count));
+ return count;
+}
+
+static PRInt32 PR_CALLBACK SocketWrite(PRFileDesc *fd, const void *buf, PRInt32 amount)
+{
+ return SocketSend(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT);
+}
+
+static PRStatus PR_CALLBACK SocketClose(PRFileDesc *fd)
+{
+ PRInt32 rv;
+
+ if (!fd || fd->secret->state != _PR_FILEDESC_OPEN) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ fd->secret->state = _PR_FILEDESC_CLOSED;
+
+ rv = _PR_MD_CLOSE_SOCKET(fd->secret->md.osfd);
+ PR_FreeFileDesc(fd);
+ if (rv < 0) {
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+static PRInt32 PR_CALLBACK SocketAvailable(PRFileDesc *fd)
+{
+ PRInt32 rv;
+ rv = _PR_MD_SOCKETAVAILABLE(fd);
+ return rv;
+}
+
+static PRInt64 PR_CALLBACK SocketAvailable64(PRFileDesc *fd)
+{
+ PRInt64 rv;
+ LL_I2L(rv, _PR_MD_SOCKETAVAILABLE(fd));
+ return rv;
+}
+
+static PRStatus PR_CALLBACK SocketSync(PRFileDesc *fd)
+{
+#if defined(XP_MAC)
+#pragma unused (fd)
+#endif
+
+ return PR_SUCCESS;
+}
+
+static PRInt32 PR_CALLBACK SocketSendTo(
+ PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PRInt32 temp, count;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return -1;
+ }
+
+#if defined(_PR_INET6)
+ PR_ASSERT(addr->raw.family == AF_INET || addr->raw.family == AF_INET6);
+#else
+ PR_ASSERT(addr->raw.family == AF_INET);
+#endif
+
+ count = 0;
+ while (amount > 0) {
+ temp = _PR_MD_SENDTO(fd, buf, amount, flags,
+ addr, PR_NETADDR_SIZE(addr), timeout);
+ if (temp < 0) {
+ count = -1;
+ break;
+ }
+ count += temp;
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ buf = (const void*) ((const char*)buf + temp);
+ amount -= temp;
+ }
+ return count;
+}
+
+static PRInt32 PR_CALLBACK SocketRecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount,
+PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PRInt32 rv;
+ PRUint32 al;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return -1;
+ }
+
+ al = sizeof(PRNetAddr);
+ rv = _PR_MD_RECVFROM(fd, buf, amount, flags, addr, &al, timeout);
+ return rv;
+}
+
+static PRInt32 PR_CALLBACK SocketAcceptRead(PRFileDesc *sd, PRFileDesc **nd,
+PRNetAddr **raddr, void *buf, PRInt32 amount,
+PRIntervalTime timeout)
+{
+ PRInt32 rv;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return -1;
+ }
+ *nd = NULL;
+
+#if defined(WINNT)
+ {
+ PRInt32 newSock;
+ PRNetAddr *raddrCopy;
+
+ if (raddr == NULL) {
+ raddr = &raddrCopy;
+ }
+ rv = _PR_MD_ACCEPT_READ(sd, &newSock, raddr, buf, amount, timeout);
+ if (rv < 0) {
+ rv = -1;
+ } else {
+ /* Successfully accepted and read; create the new PRFileDesc */
+ *nd = PR_AllocFileDesc(newSock, PR_GetTCPMethods());
+ if (*nd == 0) {
+ _PR_MD_CLOSE_SOCKET(newSock);
+ /* PR_AllocFileDesc() has invoked PR_SetError(). */
+ rv = -1;
+ } else {
+ (*nd)->secret->md.io_model_committed = PR_TRUE;
+ (*nd)->secret->md.accepted_socket = PR_TRUE;
+ memcpy(&(*nd)->secret->md.peer_addr, *raddr,
+ PR_NETADDR_SIZE(*raddr));
+ }
+ }
+ }
+#else
+ rv = _PR_EmulateAcceptRead(sd, nd, raddr, buf, amount, timeout);
+#endif
+ return rv;
+}
+
+#ifdef WINNT
+PR_IMPLEMENT(PRInt32) PR_NTFast_AcceptRead(PRFileDesc *sd, PRFileDesc **nd,
+PRNetAddr **raddr, void *buf, PRInt32 amount,
+PRIntervalTime timeout)
+{
+ PRInt32 rv;
+ PRInt32 newSock;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRNetAddr *raddrCopy;
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return -1;
+ }
+ *nd = NULL;
+
+ if (raddr == NULL) {
+ raddr = &raddrCopy;
+ }
+ rv = _PR_MD_FAST_ACCEPT_READ(sd, &newSock, raddr, buf, amount,
+ timeout, PR_TRUE, NULL, NULL);
+ if (rv < 0) {
+ rv = -1;
+ } else {
+ /* Successfully accepted and read; create the new PRFileDesc */
+ *nd = PR_AllocFileDesc(newSock, PR_GetTCPMethods());
+ if (*nd == 0) {
+ _PR_MD_CLOSE_SOCKET(newSock);
+ /* PR_AllocFileDesc() has invoked PR_SetError(). */
+ rv = -1;
+ } else {
+ (*nd)->secret->md.io_model_committed = PR_TRUE;
+ (*nd)->secret->md.accepted_socket = PR_TRUE;
+ memcpy(&(*nd)->secret->md.peer_addr, *raddr,
+ PR_NETADDR_SIZE(*raddr));
+ }
+ }
+ return rv;
+}
+
+PR_IMPLEMENT(PRInt32) PR_NTFast_AcceptRead_WithTimeoutCallback(
+PRFileDesc *sd, PRFileDesc **nd,
+PRNetAddr **raddr, void *buf, PRInt32 amount,
+PRIntervalTime timeout,
+_PR_AcceptTimeoutCallback callback,
+void *callbackArg)
+{
+ PRInt32 rv;
+ PRInt32 newSock;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRNetAddr *raddrCopy;
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return -1;
+ }
+ *nd = NULL;
+
+ if (raddr == NULL) {
+ raddr = &raddrCopy;
+ }
+ rv = _PR_MD_FAST_ACCEPT_READ(sd, &newSock, raddr, buf, amount,
+ timeout, PR_TRUE, callback, callbackArg);
+ if (rv < 0) {
+ rv = -1;
+ } else {
+ /* Successfully accepted and read; create the new PRFileDesc */
+ *nd = PR_AllocFileDesc(newSock, PR_GetTCPMethods());
+ if (*nd == 0) {
+ _PR_MD_CLOSE_SOCKET(newSock);
+ /* PR_AllocFileDesc() has invoked PR_SetError(). */
+ rv = -1;
+ } else {
+ (*nd)->secret->md.io_model_committed = PR_TRUE;
+ (*nd)->secret->md.accepted_socket = PR_TRUE;
+ memcpy(&(*nd)->secret->md.peer_addr, *raddr,
+ PR_NETADDR_SIZE(*raddr));
+ }
+ }
+ return rv;
+}
+#endif /* WINNT */
+
+#ifdef WINNT
+PR_IMPLEMENT(void)
+PR_NTFast_UpdateAcceptContext(PRFileDesc *socket, PRFileDesc *acceptSocket)
+{
+ _PR_MD_UPDATE_ACCEPT_CONTEXT(
+ socket->secret->md.osfd, acceptSocket->secret->md.osfd);
+}
+#endif /* WINNT */
+
+static PRInt32 PR_CALLBACK SocketTransmitFile(PRFileDesc *sd, PRFileDesc *fd,
+const void *headers, PRInt32 hlen, PRTransmitFileFlags flags,
+PRIntervalTime timeout)
+{
+ PRInt32 rv;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ if (_PR_IO_PENDING(me)) {
+ PR_SetError(PR_IO_PENDING_ERROR, 0);
+ return -1;
+ }
+#if defined(WINNT)
+ rv = _PR_MD_TRANSMITFILE(
+ sd, fd,
+ headers, hlen, flags, timeout);
+ if (rv < 0) {
+ rv = -1;
+ }
+ if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) {
+ /*
+ * This should be kept the same as SocketClose, except
+ * that _PR_MD_CLOSE_SOCKET(sd->secret->md.osfd) should
+ * not be called because the socket will be recycled.
+ */
+ sd->secret->state = _PR_FILEDESC_CLOSED;
+ PR_FreeFileDesc(sd);
+ }
+#else
+#if defined(XP_UNIX)
+ /*
+ * On HPUX11, we could call _PR_HPUXTransmitFile(), but that
+ * would require that we not override the malloc() functions.
+ */
+ rv = _PR_UnixTransmitFile(sd, fd, headers, hlen, flags, timeout);
+#else /* XP_UNIX */
+ rv = _PR_EmulateTransmitFile(sd, fd, headers, hlen, flags,
+ timeout);
+#endif /* XP_UNIX */
+#endif /* WINNT */
+
+ return rv;
+}
+
+static PRStatus PR_CALLBACK SocketGetName(PRFileDesc *fd, PRNetAddr *addr)
+{
+ PRInt32 result;
+ PRUint32 addrlen;
+
+ addrlen = sizeof(PRNetAddr);
+ result = _PR_MD_GETSOCKNAME(fd, addr, &addrlen);
+ if (result < 0) {
+ return PR_FAILURE;
+ }
+ PR_ASSERT(addrlen == PR_NETADDR_SIZE(addr));
+#if defined(_PR_INET6)
+ PR_ASSERT(addr->raw.family == AF_INET || addr->raw.family == AF_INET6);
+#else
+ PR_ASSERT(addr->raw.family == AF_INET);
+#endif
+ return PR_SUCCESS;
+}
+
+static PRStatus PR_CALLBACK SocketGetPeerName(PRFileDesc *fd, PRNetAddr *addr)
+{
+ PRInt32 result;
+ PRUint32 addrlen;
+
+ addrlen = sizeof(PRNetAddr);
+ result = _PR_MD_GETPEERNAME(fd, addr, &addrlen);
+ if (result < 0) {
+ return PR_FAILURE;
+ }
+ PR_ASSERT(addrlen == PR_NETADDR_SIZE(addr));
+#if defined(_PR_INET6)
+ PR_ASSERT(addr->raw.family == AF_INET || addr->raw.family == AF_INET6);
+#else
+ PR_ASSERT(addr->raw.family == AF_INET);
+#endif
+ return PR_SUCCESS;
+}
+
+static PRStatus PR_CALLBACK SocketGetSockOpt(
+ PRFileDesc *fd, PRSockOption optname, void* optval, PRInt32* optlen)
+{
+ PRInt32 level, name;
+ PRStatus rv;
+
+ /*
+ * PR_SockOpt_Nonblocking is a special case that does not
+ * translate to a getsockopt() call
+ */
+ if (PR_SockOpt_Nonblocking == optname)
+ {
+ PR_ASSERT(sizeof(PRIntn) <= *optlen);
+ *((PRIntn *) optval) = (PRIntn) fd->secret->nonblocking;
+ *optlen = sizeof(PRIntn);
+ return PR_SUCCESS;
+ }
+
+ rv = _PR_MapOptionName(optname, &level, &name);
+ if (PR_SUCCESS == rv)
+ {
+ if (PR_SockOpt_Linger == optname)
+ {
+ struct linger linger;
+ PRInt32 len = sizeof(linger);
+ rv = _PR_MD_GETSOCKOPT(
+ fd, level, name, (char *) &linger, &len);
+ if (PR_SUCCESS == rv)
+ {
+ ((PRLinger*)(optval))->polarity = linger.l_onoff
+ ? PR_TRUE : PR_FALSE;
+ ((PRLinger*)(optval))->linger = PR_SecondsToInterval(
+ linger.l_linger);
+ *optlen = sizeof(PRLinger);
+ }
+ }
+ else
+ {
+ rv = _PR_MD_GETSOCKOPT(
+ fd, level, name, optval, optlen);
+ }
+ }
+ return rv;
+}
+
+static PRStatus PR_CALLBACK SocketSetSockOpt(
+ PRFileDesc *fd, PRSockOption optname, const void* optval, PRInt32 optlen)
+{
+ PRInt32 level, name;
+ PRStatus rv;
+
+ /*
+ * PR_SockOpt_Nonblocking is a special case that does not
+ * translate to a setsockopt call.
+ */
+ if (PR_SockOpt_Nonblocking == optname)
+ {
+ PRBool fNonblocking = *((PRIntn *) optval) ? PR_TRUE : PR_FALSE;
+ PR_ASSERT(sizeof(PRIntn) == optlen);
+#ifdef WINNT
+ PR_ASSERT((fd->secret->md.io_model_committed == PR_FALSE)
+ || (fd->secret->nonblocking == fNonblocking));
+ if (fd->secret->md.io_model_committed
+ && (fd->secret->nonblocking != fNonblocking))
+ {
+ /*
+ * On NT, once we have associated a socket with the io
+ * completion port, we can't disassociate it. So we
+ * can't change the nonblocking option of the socket
+ * afterwards.
+ */
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+#endif
+ fd->secret->nonblocking = fNonblocking;
+ return PR_SUCCESS;
+ }
+
+ rv = _PR_MapOptionName(optname, &level, &name);
+ if (PR_SUCCESS == rv)
+ {
+ if (PR_SockOpt_Linger == optname)
+ {
+ struct linger linger;
+ linger.l_onoff = ((PRLinger*)(optval))->polarity ? 1 : 0;
+ linger.l_linger = PR_IntervalToSeconds(
+ ((PRLinger*)(optval))->linger);
+ rv = _PR_MD_SETSOCKOPT(
+ fd, level, name, (char *) &linger, sizeof(linger));
+ }
+ else
+ {
+ rv = _PR_MD_SETSOCKOPT(
+ fd, level, name, optval, optlen);
+ }
+ }
+ return rv;
+}
+
+static PRIOMethods tcpMethods = {
+ PR_DESC_SOCKET_TCP,
+ SocketClose,
+ SocketRead,
+ SocketWrite,
+ SocketAvailable,
+ SocketAvailable64,
+ SocketSync,
+ (PRSeekFN)_PR_InvalidInt,
+ (PRSeek64FN)_PR_InvalidInt64,
+ (PRFileInfoFN)_PR_InvalidStatus,
+ (PRFileInfo64FN)_PR_InvalidStatus,
+ SocketWritev,
+ SocketConnect,
+ SocketAccept,
+ SocketBind,
+ SocketListen,
+ SocketShutdown,
+ SocketRecv,
+ SocketSend,
+ (PRRecvfromFN)_PR_InvalidInt,
+ (PRSendtoFN)_PR_InvalidInt,
+ (PRPollFN)0,
+ SocketAcceptRead,
+ SocketTransmitFile,
+ SocketGetName,
+ SocketGetPeerName,
+ SocketGetSockOpt,
+ SocketSetSockOpt,
+ _PR_SocketGetSocketOption,
+ _PR_SocketSetSocketOption
+};
+
+static PRIOMethods udpMethods = {
+ PR_DESC_SOCKET_UDP,
+ SocketClose,
+ SocketRead,
+ SocketWrite,
+ SocketAvailable,
+ SocketAvailable64,
+ SocketSync,
+ (PRSeekFN)_PR_InvalidInt,
+ (PRSeek64FN)_PR_InvalidInt64,
+ (PRFileInfoFN)_PR_InvalidStatus,
+ (PRFileInfo64FN)_PR_InvalidStatus,
+ SocketWritev,
+ SocketConnect,
+ (PRAcceptFN)_PR_InvalidDesc,
+ SocketBind,
+ SocketListen,
+ SocketShutdown,
+ SocketRecv,
+ SocketSend,
+ SocketRecvFrom,
+ SocketSendTo,
+ (PRPollFN)0,
+ (PRAcceptreadFN)_PR_InvalidInt,
+ (PRTransmitfileFN)_PR_InvalidInt,
+ SocketGetName,
+ SocketGetPeerName,
+ SocketGetSockOpt,
+ SocketSetSockOpt,
+ _PR_SocketGetSocketOption,
+ _PR_SocketSetSocketOption
+};
+
+PR_IMPLEMENT(PRIOMethods*) PR_GetTCPMethods()
+{
+ return &tcpMethods;
+}
+
+PR_IMPLEMENT(PRIOMethods*) PR_GetUDPMethods()
+{
+ return &udpMethods;
+}
+
+PR_IMPLEMENT(PRFileDesc*) PR_Socket(PRInt32 domain, PRInt32 type, PRInt32 proto)
+{
+ PRInt32 osfd;
+ int one = 1;
+ PRFileDesc *fd;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+ if (AF_INET != domain
+#if defined(_PR_INET6)
+ && AF_INET6 != domain
+#endif
+ ) {
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0);
+ return NULL;
+ }
+ osfd = _PR_MD_SOCKET(domain, type, proto);
+ if (osfd == -1) {
+ return 0;
+ }
+#ifdef HAVE_SOCKET_KEEPALIVE
+ /* "Keep-alive" packets are specific to TCP. */
+ if (domain == AF_INET && type == SOCK_STREAM) {
+ if (setsockopt(osfd, (int)SOL_SOCKET, SO_KEEPALIVE,
+ (const void *) &one, sizeof(one) ) < 0) {
+ _PR_MD_CLOSE_SOCKET(osfd);
+ return 0;
+ }
+ }
+#endif
+ if (type == SOCK_STREAM)
+ fd = PR_AllocFileDesc(osfd, PR_GetTCPMethods());
+ else
+ fd = PR_AllocFileDesc(osfd, PR_GetUDPMethods());
+ /*
+ * Make the sockets non-blocking
+ */
+ if (fd != NULL)
+ _PR_MD_MAKE_NONBLOCK(fd);
+ else
+ _PR_MD_CLOSE_SOCKET(osfd);
+ return fd;
+}
+
+PR_IMPLEMENT(PRFileDesc *) PR_NewTCPSocket(void)
+{
+ PRInt32 domain = AF_INET;
+
+#if defined(_PR_INET6)
+ if (_pr_ipv6_enabled) {
+ domain = AF_INET6;
+ }
+#endif
+ return PR_Socket(domain, SOCK_STREAM, 0);
+}
+
+PR_IMPLEMENT(PRFileDesc*) PR_NewUDPSocket(void)
+{
+ PRInt32 domain = AF_INET;
+
+#if defined(_PR_INET6)
+ if (_pr_ipv6_enabled) {
+ domain = AF_INET6;
+ }
+#endif
+ return PR_Socket(domain, SOCK_DGRAM, 0);
+}
+
+PR_IMPLEMENT(PRStatus) PR_NewTCPSocketPair(PRFileDesc *f[])
+{
+#ifdef XP_UNIX
+ PRInt32 rv, osfd[2];
+
+ rv = _PR_MD_SOCKETPAIR(AF_UNIX, SOCK_STREAM, 0, osfd);
+ if (rv == -1) {
+ return PR_FAILURE;
+ }
+
+ f[0] = PR_AllocFileDesc(osfd[0], PR_GetTCPMethods());
+ if (!f[0]) {
+ _PR_MD_CLOSE_SOCKET(osfd[0]);
+ _PR_MD_CLOSE_SOCKET(osfd[1]);
+ /* PR_AllocFileDesc() has invoked PR_SetError(). */
+ return PR_FAILURE;
+ }
+ f[1] = PR_AllocFileDesc(osfd[1], PR_GetTCPMethods());
+ if (!f[1]) {
+ PR_Close(f[0]);
+ _PR_MD_CLOSE_SOCKET(osfd[1]);
+ /* PR_AllocFileDesc() has invoked PR_SetError(). */
+ return PR_FAILURE;
+ }
+ _PR_MD_MAKE_NONBLOCK(f[0]);
+ _PR_MD_MAKE_NONBLOCK(f[1]);
+ return PR_SUCCESS;
+#endif
+
+ /* XXX: this needs to be implemented for MAC and NT */
+#ifdef XP_MAC
+#pragma unused (f)
+
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr);
+ return PR_FAILURE;
+#endif
+
+#ifdef XP_PC
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+#endif
+}
+
+PR_IMPLEMENT(PRInt32)
+PR_FileDesc2NativeHandle(PRFileDesc *fd)
+{
+ if (fd) {
+ /*
+ * The fd may be layered. Chase the links to the
+ * bottom layer to get the osfd.
+ */
+ PRFileDesc *bottom = fd;
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ return bottom->secret->md.osfd;
+ } else {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+}
+
+PR_IMPLEMENT(void)
+PR_ChangeFileDescNativeHandle(PRFileDesc *fd, PRInt32 handle)
+{
+ if (fd)
+ fd->secret->md.osfd = handle;
+}
+
+/*
+ * _PR_EmulateTransmitFile
+ *
+ * Send file fd across socket sd. If headers is non-NULL, 'hlen'
+ * bytes of headers is sent before sending the file.
+ *
+ * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file
+ *
+ * return number of bytes sent or -1 on error
+ *
+ */
+
+PRInt32 _PR_EmulateTransmitFile(PRFileDesc *sd, PRFileDesc *fd,
+const void *headers, PRInt32 hlen, PRTransmitFileFlags flags,
+PRIntervalTime timeout)
+{
+ PRInt32 rv, count = 0;
+ PRInt32 rlen;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ char *buf = NULL;
+#define _TRANSMITFILE_BUFSIZE (16 * 1024)
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+
+ buf = PR_MALLOC(_TRANSMITFILE_BUFSIZE);
+ if (buf == NULL) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+
+ /*
+ * send headers, first
+ */
+ while (hlen) {
+ rv = PR_Send(sd, headers, hlen, 0, timeout);
+ if (rv < 0) {
+ /* PR_Send() has invoked PR_SetError(). */
+ rv = -1;
+ goto done;
+ } else {
+ count += rv;
+ headers = (const void*) ((const char*)headers + rv);
+ hlen -= rv;
+ }
+ }
+ /*
+ * send file, next
+ */
+ while ((rlen = PR_Read(fd, buf, _TRANSMITFILE_BUFSIZE)) > 0) {
+ while (rlen) {
+ char *bufptr = buf;
+
+ rv = PR_Send(sd, bufptr, rlen,0,PR_INTERVAL_NO_TIMEOUT);
+ if (rv < 0) {
+ /* PR_Send() has invoked PR_SetError(). */
+ rv = -1;
+ goto done;
+ } else {
+ count += rv;
+ bufptr = ((char*)bufptr + rv);
+ rlen -= rv;
+ }
+ }
+ }
+ if (rlen == 0) {
+ /*
+ * end-of-file
+ */
+ if (flags & PR_TRANSMITFILE_CLOSE_SOCKET)
+ PR_Close(sd);
+ rv = count;
+ } else {
+ PR_ASSERT(rlen < 0);
+ /* PR_Read() has invoked PR_SetError(). */
+ rv = -1;
+ }
+
+done:
+ if (buf)
+ PR_DELETE(buf);
+ return rv;
+}
+
+/*
+ * _PR_EmulateAcceptRead
+ *
+ * Accept an incoming connection on sd, set *nd to point to the
+ * newly accepted socket, read 'amount' bytes from the accepted
+ * socket.
+ *
+ * buf is a buffer of length = (amount + sizeof(PRNetAddr))
+ * *raddr points to the PRNetAddr of the accepted connection upon
+ * return
+ *
+ * return number of bytes read or -1 on error
+ *
+ */
+PRInt32 _PR_EmulateAcceptRead(PRFileDesc *sd, PRFileDesc **nd,
+PRNetAddr **raddr, void *buf, PRInt32 amount, PRIntervalTime timeout)
+{
+ PRInt32 rv;
+ PRFileDesc *newsockfd;
+ PRNetAddr remote;
+ PRIntervalTime start, elapsed;
+
+ if (PR_INTERVAL_NO_TIMEOUT != timeout) {
+ start = PR_IntervalNow();
+ }
+ if ((newsockfd = PR_Accept(sd, &remote, timeout)) == NULL) {
+ return -1;
+ }
+
+ if (PR_INTERVAL_NO_TIMEOUT != timeout) {
+ elapsed = (PRIntervalTime) (PR_IntervalNow() - start);
+ if (elapsed > timeout) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ goto failed;
+ } else {
+ timeout = timeout - elapsed;
+ }
+ }
+
+ rv = PR_Recv(newsockfd, buf, amount, 0, timeout);
+ if (rv >= 0) {
+ *nd = newsockfd;
+ *raddr = (PRNetAddr *)((char *) buf + amount);
+ memcpy(*raddr, &remote, PR_NETADDR_SIZE(&remote));
+ return rv;
+ }
+
+failed:
+ PR_Close(newsockfd);
+ return -1;
+}
+
+/*
+** Select compatibility
+**
+*/
+
+PR_IMPLEMENT(void) PR_FD_ZERO(PR_fd_set *set)
+{
+ memset(set, 0, sizeof(PR_fd_set));
+}
+
+PR_IMPLEMENT(void) PR_FD_SET(PRFileDesc *fh, PR_fd_set *set)
+{
+ PR_ASSERT( set->hsize < PR_MAX_SELECT_DESC );
+
+ set->harray[set->hsize++] = fh;
+}
+
+PR_IMPLEMENT(void) PR_FD_CLR(PRFileDesc *fh, PR_fd_set *set)
+{
+ PRUint32 index, index2;
+
+ for (index = 0; index<set->hsize; index++)
+ if (set->harray[index] == fh) {
+ for (index2=index; index2 < (set->hsize-1); index2++) {
+ set->harray[index2] = set->harray[index2+1];
+ }
+ set->hsize--;
+ break;
+ }
+}
+
+PR_IMPLEMENT(PRInt32) PR_FD_ISSET(PRFileDesc *fh, PR_fd_set *set)
+{
+ PRUint32 index;
+ for (index = 0; index<set->hsize; index++)
+ if (set->harray[index] == fh) {
+ return 1;
+ }
+ return 0;
+}
+
+PR_IMPLEMENT(void) PR_FD_NSET(PRInt32 fd, PR_fd_set *set)
+{
+ PR_ASSERT( set->nsize < PR_MAX_SELECT_DESC );
+
+ set->narray[set->nsize++] = fd;
+}
+
+PR_IMPLEMENT(void) PR_FD_NCLR(PRInt32 fd, PR_fd_set *set)
+{
+ PRUint32 index, index2;
+
+ for (index = 0; index<set->nsize; index++)
+ if (set->narray[index] == fd) {
+ for (index2=index; index2 < (set->nsize-1); index2++) {
+ set->narray[index2] = set->narray[index2+1];
+ }
+ set->nsize--;
+ break;
+ }
+}
+
+PR_IMPLEMENT(PRInt32) PR_FD_NISSET(PRInt32 fd, PR_fd_set *set)
+{
+ PRUint32 index;
+ for (index = 0; index<set->nsize; index++)
+ if (set->narray[index] == fd) {
+ return 1;
+ }
+ return 0;
+}
+
+
+#if !defined(NEED_SELECT)
+#if !defined(XP_MAC)
+#include "obsolete/probslet.h"
+#else
+#include "probslet.h"
+#endif
+
+#define PD_INCR 20
+
+static PRPollDesc *_pr_setfd(
+ PR_fd_set *set, PRInt16 flags, PRPollDesc *polldesc)
+{
+ PRUintn fsidx, pdidx;
+ PRPollDesc *poll = polldesc;
+
+ if (NULL == set) return poll;
+
+ /* First set the pr file handle osfds */
+ for (fsidx = 0; fsidx < set->hsize; fsidx++)
+ {
+ for (pdidx = 0; 1; pdidx++)
+ {
+ if ((PRFileDesc*)-1 == poll[pdidx].fd)
+ {
+ /* our vector is full - extend and condition it */
+ poll = PR_Realloc(
+ poll, (pdidx + 1 + PD_INCR) * sizeof(PRPollDesc));
+ if (NULL == poll) goto out_of_memory;
+ memset(
+ poll + pdidx * sizeof(PRPollDesc),
+ 0, PD_INCR * sizeof(PRPollDesc));
+ poll[pdidx + PD_INCR].fd = (PRFileDesc*)-1;
+ }
+ if ((NULL == poll[pdidx].fd)
+ || (poll[pdidx].fd == set->harray[fsidx]))
+ {
+ /* PR_ASSERT(0 == (poll[pdidx].in_flags & flags)); */
+ /* either empty or prevously defined */
+ poll[pdidx].fd = set->harray[fsidx]; /* possibly redundant */
+ poll[pdidx].in_flags |= flags; /* possibly redundant */
+ break;
+ }
+ }
+ }
+
+#if 0
+ /* Second set the native osfds */
+ for (fsidx = 0; fsidx < set->nsize; fsidx++)
+ {
+ for (pdidx = 0; ((PRFileDesc*)-1 != poll[pdidx].fd); pdidx++)
+ {
+ if ((PRFileDesc*)-1 == poll[pdidx].fd)
+ {
+ /* our vector is full - extend and condition it */
+ poll = PR_Realloc(
+ poll, (pdidx + PD_INCR) * sizeof(PRPollDesc));
+ if (NULL == poll) goto out_of_memory;
+ memset(
+ poll + pdidx * sizeof(PRPollDesc),
+ 0, PD_INCR * sizeof(PRPollDesc));
+ poll[(pdidx + PD_INCR)].fd = (PRFileDesc*)-1;
+ }
+ if ((NULL == poll[pdidx].fd)
+ || (poll[pdidx].fd == set->narray[fsidx]))
+ {
+ /* either empty or prevously defined */
+ poll[pdidx].fd = set->narray[fsidx];
+ PR_ASSERT(0 == (poll[pdidx].in_flags & flags));
+ poll[pdidx].in_flags |= flags;
+ break;
+ }
+ }
+ }
+#endif /* 0 */
+
+ return poll;
+
+out_of_memory:
+ if (NULL != polldesc) PR_DELETE(polldesc);
+ return NULL;
+} /* _pr_setfd */
+
+#endif /* !defined(NEED_SELECT) */
+
+PR_IMPLEMENT(PRInt32) PR_Select(
+ PRInt32 unused, PR_fd_set *pr_rd, PR_fd_set *pr_wr,
+ PR_fd_set *pr_ex, PRIntervalTime timeout)
+{
+
+#if !defined(NEED_SELECT)
+ PRInt32 npds = 0;
+ /*
+ ** Find out how many fds are represented in the three lists.
+ ** Then allocate a polling descriptor for the logical union
+ ** (there can't be any overlapping) and call PR_Poll().
+ */
+
+ PRPollDesc *copy, *poll;
+
+ static PRBool warning = PR_TRUE;
+ if (warning) warning = _PR_Obsolete( "PR_Select()", "PR_Poll()");
+
+ /* try to get an initial guesss at how much space we need */
+ npds = 0;
+ if ((NULL != pr_rd) && ((pr_rd->hsize + pr_rd->nsize - npds) > 0))
+ npds = pr_rd->hsize + pr_rd->nsize;
+ if ((NULL != pr_wr) && ((pr_wr->hsize + pr_wr->nsize - npds) > 0))
+ npds = pr_wr->hsize + pr_wr->nsize;
+ if ((NULL != pr_ex) && ((pr_ex->hsize + pr_ex->nsize - npds) > 0))
+ npds = pr_ex->hsize + pr_ex->nsize;
+
+ if (0 == npds)
+ {
+ PR_Sleep(timeout);
+ return 0;
+ }
+
+ copy = poll = PR_Calloc(npds + PD_INCR, sizeof(PRPollDesc));
+ if (NULL == poll) goto out_of_memory;
+ poll[npds + PD_INCR - 1].fd = (PRFileDesc*)-1;
+
+ poll = _pr_setfd(pr_rd, PR_POLL_READ, poll);
+ if (NULL == poll) goto out_of_memory;
+ poll = _pr_setfd(pr_wr, PR_POLL_WRITE, poll);
+ if (NULL == poll) goto out_of_memory;
+ poll = _pr_setfd(pr_ex, PR_POLL_EXCEPT, poll);
+ if (NULL == poll) goto out_of_memory;
+ unused = 0;
+ while (NULL != poll[unused].fd && (PRFileDesc*)-1 != poll[unused].fd)
+ {
+ ++unused;
+ }
+
+ PR_ASSERT(unused > 0);
+ npds = PR_Poll(poll, unused, timeout);
+
+ if (npds > 0)
+ {
+ /* Copy the results back into the fd sets */
+ if (NULL != pr_rd) pr_rd->nsize = pr_rd->hsize = 0;
+ if (NULL != pr_wr) pr_wr->nsize = pr_wr->hsize = 0;
+ if (NULL != pr_ex) pr_ex->nsize = pr_ex->hsize = 0;
+ for (copy = &poll[unused - 1]; copy >= poll; --copy)
+ {
+ if (copy->out_flags & PR_POLL_NVAL)
+ {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+ npds = -1;
+ break;
+ }
+ if (copy->out_flags & PR_POLL_READ)
+ if (NULL != pr_rd) pr_rd->harray[pr_rd->hsize++] = copy->fd;
+ if (copy->out_flags & PR_POLL_WRITE)
+ if (NULL != pr_wr) pr_wr->harray[pr_wr->hsize++] = copy->fd;
+ if (copy->out_flags & PR_POLL_EXCEPT)
+ if (NULL != pr_ex) pr_ex->harray[pr_ex->hsize++] = copy->fd;
+ }
+ }
+ PR_DELETE(poll);
+
+ return npds;
+out_of_memory:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+
+#endif /* !defined(NEED_SELECT) */
+
+}
diff --git a/pr/src/io/prstdio.c b/pr/src/io/prstdio.c
new file mode 100644
index 00000000..7c856bef
--- /dev/null
+++ b/pr/src/io/prstdio.c
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+#include <string.h>
+
+/*
+** fprintf to a PRFileDesc
+*/
+PR_IMPLEMENT(PRUint32) PR_fprintf(PRFileDesc* fd, const char *fmt, ...)
+{
+ va_list ap;
+ PRUint32 rv;
+
+ va_start(ap, fmt);
+ rv = PR_vfprintf(fd, fmt, ap);
+ va_end(ap);
+ return rv;
+}
+
+PR_IMPLEMENT(PRUint32) PR_vfprintf(PRFileDesc* fd, const char *fmt, va_list ap)
+{
+ /* XXX this could be better */
+ PRUint32 rv, len;
+ char* msg = PR_vsmprintf(fmt, ap);
+ len = strlen(msg);
+ rv = PR_Write(fd, msg, len);
+ PR_DELETE(msg);
+ return rv;
+}
diff --git a/pr/src/linking/Makefile b/pr/src/linking/Makefile
new file mode 100644
index 00000000..eaef143b
--- /dev/null
+++ b/pr/src/linking/Makefile
@@ -0,0 +1,43 @@
+#
+# The contents of this file are subject to the Netscape Public License
+# Version 1.0 (the "NPL"); you may not use this file except in
+# compliance with the NPL. You may obtain a copy of the NPL at
+# http://www.mozilla.org/NPL/
+#
+# Software distributed under the NPL is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+# for the specific language governing rights and limitations under the
+# NPL.
+#
+# The Initial Developer of this code under the NPL is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1998 Netscape Communications Corporation. All Rights
+# Reserved.
+#
+
+#! gmake
+
+MOD_DEPTH = ../../..
+
+include $(MOD_DEPTH)/config/config.mk
+
+# Disable optimization of the nspr on SunOS4.1.3
+ifeq ($(OS_ARCH),SunOS)
+ifeq ($(OS_RELEASE),4.1.3_U1)
+OPTIMIZER =
+endif
+endif
+
+CSRCS = \
+ prlink.c \
+ $(NULL)
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(DIST)/include/private -I$(DIST)/include
+
+include $(MOD_DEPTH)/config/rules.mk
+
+export:: $(TARGETS)
+
+install:: export
diff --git a/pr/src/linking/prlink.c b/pr/src/linking/prlink.c
new file mode 100644
index 00000000..aacc7715
--- /dev/null
+++ b/pr/src/linking/prlink.c
@@ -0,0 +1,950 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+#include <string.h>
+
+#ifdef XP_MAC
+#include <CodeFragments.h>
+#include <TextUtils.h>
+#include <Types.h>
+#include <Strings.h>
+#endif
+
+#ifdef XP_UNIX
+#ifdef USE_DLFCN
+#include <dlfcn.h>
+#elif defined(USE_HPSHL)
+#include <dl.h>
+#endif
+
+/* Define this on systems which don't have it (AIX) */
+#ifndef RTLD_LAZY
+#define RTLD_LAZY 1
+#endif
+#endif /* XP_UNIX */
+
+#ifdef XP_PC
+typedef PRStaticLinkTable *NODL_PROC(void);
+#endif
+
+/************************************************************************/
+
+struct PRLibrary {
+ char* name; /* Our own copy of the name string */
+ PRLibrary* next;
+ int refCount;
+ const PRStaticLinkTable* staticTable;
+
+#ifdef XP_PC
+ HINSTANCE dlh;
+#endif
+#ifdef XP_MAC
+ CFragConnectionID dlh;
+#endif
+
+#ifdef XP_UNIX
+#if defined(USE_HPSHL)
+ shl_t dlh;
+#else
+ void* dlh;
+#endif
+#endif
+};
+
+static PRLibrary *pr_loadmap;
+static PRLibrary *pr_exe_loadmap;
+static PRMonitor *pr_linker_lock;
+static char* _pr_currentLibPath = NULL;
+
+/************************************************************************/
+
+#if ( !defined(USE_DLFCN) && !defined(HAVE_STRERROR) ) || defined(HPUX) || defined(BSDI) || defined(LINUX)
+static char* errStrBuf = NULL;
+#define ERR_STR_BUF_LENGTH 20
+static char* errno_string(PRIntn oserr)
+{
+ if (errStrBuf == NULL)
+ errStrBuf = PR_MALLOC(ERR_STR_BUF_LENGTH);
+ PR_snprintf(errStrBuf, ERR_STR_BUF_LENGTH, "error %ld", oserr);
+ return errStrBuf;
+}
+#endif
+
+static void DLLErrorInternal(PRIntn oserr)
+/*
+** This whole function, and most of the code in this file, are run
+** with a big hairy lock wrapped around it. Not the best of situations,
+** but will eventually come up with the right answer.
+*/
+{
+ const char *error = NULL;
+#ifdef HAVE_DLL
+# ifdef USE_DLFCN
+ error = dlerror(); /* $$$ That'll be wrong some of the time - AOF */
+# elif defined(USE_HPSHL)
+# ifdef HAVE_STRERROR
+ error = strerror(oserr); /* This should be okay */
+# else
+ error = errno_string(oserr);
+# endif /* HAVE_STRERROR */
+# else
+ error = errno_string(oserr);
+# endif
+#else
+ error = errno_string(oserr);
+#endif /* HAVE_DLL */
+ if (NULL != error)
+ PR_SetErrorText(strlen(error), error);
+} /* DLLErrorInternal */
+
+void _PR_InitLinker(void)
+{
+#ifndef XP_MAC
+ PRLibrary *lm;
+#endif
+#if defined(XP_UNIX)
+ void *h;
+#endif
+
+ if (!pr_linker_lock) {
+ pr_linker_lock = PR_NewNamedMonitor("linker-lock");
+ }
+ PR_EnterMonitor(pr_linker_lock);
+
+#if defined(XP_PC)
+ lm = PR_NEWZAP(PRLibrary);
+ lm->name = strdup("Executable");
+ /*
+ ** In WIN32, GetProcAddress(...) expects a module handle in order to
+ ** get exported symbols from the executable...
+ **
+ ** However, in WIN16 this is accomplished by passing NULL to
+ ** GetProcAddress(...)
+ */
+#if defined(_WIN32)
+ lm->dlh = GetModuleHandle(NULL);
+#else
+ lm->dlh = (HINSTANCE)NULL;
+#endif /* ! _WIN32 */
+
+ lm->refCount = 1;
+ lm->staticTable = NULL;
+ pr_exe_loadmap = lm;
+ pr_loadmap = lm;
+
+#elif defined(XP_UNIX)
+#ifdef HAVE_DLL
+#ifdef USE_DLFCN
+ h = dlopen(0, RTLD_LAZY);
+ if (!h) {
+ char *error;
+
+ DLLErrorInternal(_MD_ERRNO());
+ error = (char*)PR_MALLOC(PR_GetErrorTextLength());
+ (void) PR_GetErrorText(error);
+ fprintf(stderr, "failed to initialize shared libraries [%s]\n",
+ error);
+ PR_DELETE(error);
+ abort();/* XXX */
+ }
+#elif defined(USE_HPSHL)
+ h = NULL;
+ /* don't abort with this NULL */
+#else
+#error no dll strategy
+#endif /* USE_DLFCN */
+
+ lm = PR_NEWZAP(PRLibrary);
+ if (lm) {
+ lm->name = strdup("a.out");
+ lm->refCount = 1;
+ lm->dlh = h;
+ lm->staticTable = NULL;
+ }
+ pr_exe_loadmap = lm;
+ pr_loadmap = lm;
+#endif /* HAVE_DLL */
+#endif /* XP_UNIX */
+
+#ifndef XP_MAC
+ PR_LOG(_pr_linker_lm, PR_LOG_MIN, ("Loaded library %s (init)", lm?lm->name:"NULL"));
+#endif
+
+ PR_ExitMonitor(pr_linker_lock);
+}
+
+#if defined(WIN16)
+void _PR_ShutdownLinker(void)
+{
+ PR_EnterMonitor(pr_linker_lock);
+
+ while (pr_loadmap) {
+ if (pr_loadmap->refCount > 1) {
+#ifdef DEBUG
+ fprintf(stderr, "# Forcing library to unload: %s (%d outstanding references)\n",
+ pr_loadmap->name, pr_loadmap->refCount);
+#endif
+ pr_loadmap->refCount = 1;
+ }
+ PR_UnloadLibrary(pr_loadmap);
+ }
+
+ PR_ExitMonitor(pr_linker_lock);
+
+ PR_DestroyMonitor(pr_linker_lock);
+ pr_linker_lock = NULL;
+}
+#endif
+
+/******************************************************************************/
+
+PR_IMPLEMENT(PRStatus) PR_SetLibraryPath(const char *path)
+{
+ PRStatus rv = PR_SUCCESS;
+
+ PR_EnterMonitor(pr_linker_lock);
+ PR_FREEIF(_pr_currentLibPath);
+ if (path) {
+ _pr_currentLibPath = strdup(path);
+ if (!_pr_currentLibPath) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ rv = PR_FAILURE;
+ }
+ } else {
+ _pr_currentLibPath = 0;
+ }
+ PR_ExitMonitor(pr_linker_lock);
+ return rv;
+}
+
+/*
+** Return the library path for finding shared libraries.
+*/
+PR_IMPLEMENT(char *)
+PR_GetLibraryPath()
+{
+ char *ev;
+ char *copy = NULL; /* a copy of _pr_currentLibPath */
+
+ PR_EnterMonitor(pr_linker_lock);
+ if (_pr_currentLibPath != NULL) {
+ goto exit;
+ }
+
+ /* initialize pr_currentLibPath */
+
+#ifdef XP_PC
+ ev = getenv("LD_LIBRARY_PATH");
+ if (!ev) {
+ ev = ".;\\lib";
+ }
+ ev = strdup(ev);
+#endif
+
+#ifdef XP_MAC
+ {
+ char *p;
+ int len;
+
+ ev = getenv("LD_LIBRARY_PATH");
+
+ /* if we couldn't find something make up a default */
+ if (!ev)
+ ev = "/usr/local/netscape;/usr/local/netscape/java/bin"; /* do we put the classes in here too? */
+
+ len = strlen(ev) + 1; /* +1 for the null */
+ p = (char*) PR_MALLOC(len);
+ if (p) {
+ strcpy(p, ev);
+ }
+ ev = p;
+ }
+#endif
+
+#ifdef XP_UNIX
+#if defined USE_DLFCN
+ {
+ char *home;
+ char *local;
+ char *p;
+ int len;
+
+ ev = getenv("LD_LIBRARY_PATH");
+ if (!ev) {
+ ev = "/usr/lib:/lib";
+ }
+ home = getenv("HOME");
+
+ /*
+ ** Augment the path automatically by adding in ~/.netscape and
+ ** /usr/local/netscape
+ */
+ len = strlen(ev) + 1; /* +1 for the null */
+ if (home && home[0]) {
+ len += strlen(home) + 1; /* +1 for the colon */
+ }
+
+ local = ":/usr/local/netscape/lib/" PR_LINKER_ARCH;
+ len += strlen(local); /* already got the : */
+ p = (char*) PR_MALLOC(len+50);
+ if (p) {
+ strcpy(p, ev);
+ if (home) {
+ strcat(p, ":");
+ strcat(p, home);
+ }
+ strcat(p, local);
+ }
+ ev = p;
+ PR_LOG(_pr_io_lm, PR_LOG_NOTICE, ("linker path '%s'", ev));
+ }
+#else
+ /* AFAIK there isn't a library path with the HP SHL interface --Rob */
+ ev = strdup("");
+#endif
+#endif
+
+ /*
+ * If ev is NULL, we have run out of memory
+ */
+ _pr_currentLibPath = ev;
+
+ exit:
+ if (_pr_currentLibPath) {
+ copy = strdup(_pr_currentLibPath);
+ }
+ PR_ExitMonitor(pr_linker_lock);
+ if (!copy) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ }
+ return copy;
+}
+
+/*
+** Build library name from path, lib and extensions
+*/
+PR_IMPLEMENT(char*)
+PR_GetLibraryName(const char *path, const char *lib)
+{
+ char *fullname;
+
+#ifdef XP_PC
+ if (strstr(lib, PR_DLL_SUFFIX) == NULL)
+ {
+ fullname = PR_smprintf("%s\\%s%s", path, lib, PR_DLL_SUFFIX);
+ } else {
+ fullname = PR_smprintf("%s\\%s", path, lib);
+ }
+#endif /* XP_PC */
+#ifdef XP_MAC
+ fullname = PR_smprintf("%s%s", path, lib);
+#endif
+#ifdef XP_UNIX
+ if (strstr(lib, PR_DLL_SUFFIX) == NULL)
+ {
+ fullname = PR_smprintf("%s/lib%s%s", path, lib, PR_DLL_SUFFIX);
+ } else {
+ fullname = PR_smprintf("%s/%s", path, lib);
+ }
+#endif /* XP_UNIX */
+ return fullname;
+}
+
+/*
+** Free the memory allocated, for the caller, by PR_GetLibraryName
+*/
+PR_IMPLEMENT(void)
+PR_FreeLibraryName(char *mem)
+{
+ PR_smprintf_free(mem);
+}
+
+static PRLibrary*
+pr_UnlockedFindLibrary(const char *name)
+{
+ PRLibrary* lm = pr_loadmap;
+ const char* np = strrchr(name, PR_DIRECTORY_SEPARATOR);
+ np = np ? np + 1 : name;
+ while (lm) {
+ const char* cp = strrchr(lm->name, PR_DIRECTORY_SEPARATOR);
+ cp = cp ? cp + 1 : lm->name;
+#ifdef XP_PC
+ /* Windows DLL names are case insensitive... */
+ if (strcmpi(np, cp) == 0)
+#else
+ if (strcmp(np, cp) == 0)
+#endif
+ {
+ /* found */
+ lm->refCount++;
+ PR_LOG(_pr_linker_lm, PR_LOG_MIN,
+ ("%s incr => %d (find lib)",
+ lm->name, lm->refCount));
+ return lm;
+ }
+ lm = lm->next;
+ }
+ return NULL;
+}
+
+/*
+** Dynamically load a library. Only load libraries once, so scan the load
+** map first.
+*/
+PR_IMPLEMENT(PRLibrary*)
+PR_LoadLibrary(const char *name)
+{
+ PRLibrary *lm;
+ PRLibrary* result;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ /* See if library is already loaded */
+ PR_EnterMonitor(pr_linker_lock);
+
+ result = pr_UnlockedFindLibrary(name);
+ if (result != NULL) goto unlock;
+
+ lm = PR_NEWZAP(PRLibrary);
+ if (lm == NULL) goto unlock;
+ lm->staticTable = NULL;
+
+#ifdef XP_OS2 /* Why isn't all this stuff in MD code?! */
+ {
+ NODL_PROC *pfn;
+ HMODULE h;
+ UCHAR pszError[_MAX_PATH];
+ ULONG ulRc = NO_ERROR;
+ int first_try = 1;
+
+ retry:
+ ulRc = DosLoadModule(pszError, _MAX_PATH, (PSZ) name, &h);
+ if (ulRc != NO_ERROR) {
+ PR_DELETE(lm);
+ goto unlock;
+ }
+ lm->name = strdup(name);
+ lm->dlh = h;
+ lm->next = pr_loadmap;
+ pr_loadmap = lm;
+ }
+#endif /* XP_OS2 */
+
+#if defined(WIN32) || defined(WIN16)
+ {
+ HINSTANCE h;
+ NODL_PROC *pfn;
+
+ h = LoadLibrary(name);
+ if (h < (HINSTANCE)HINSTANCE_ERROR) {
+ PR_DELETE(lm);
+ goto unlock;
+ }
+ lm->name = strdup(name);
+ lm->dlh = h;
+ lm->next = pr_loadmap;
+ pr_loadmap = lm;
+
+ /*
+ ** Try to load a table of "static functions" provided by the DLL
+ */
+
+ pfn = (NODL_PROC *)GetProcAddress(h, "NODL_TABLE");
+ if (pfn != NULL) {
+ lm->staticTable = (*pfn)();
+ }
+ }
+#endif /* WIN32 || WIN16 */
+
+#if defined(XP_MAC) && GENERATINGCFM
+ {
+ OSErr err;
+ Ptr main;
+ CFragConnectionID connectionID;
+ Str255 errName;
+ Str255 pName;
+ char cName[64];
+ const char* libName;
+
+ /*
+ * Algorithm: The "name" passed in could be either a shared
+ * library name that we should look for in the normal library
+ * search paths, or a full path name to a specific library on
+ * disk. Since the full path will always contain a ":"
+ * (shortest possible path is "Volume:File"), and since a
+ * library name can not contain a ":", we can test for the
+ * presence of a ":" to see which type of library we should load.
+ * or its a full UNIX path which we for now assume is Java
+ * enumerating all the paths (see below)
+ */
+ if (strchr(name, PR_PATH_SEPARATOR) == NULL)
+ {
+ if (strchr(name, PR_DIRECTORY_SEPARATOR) == NULL)
+ {
+ /*
+ * The name did not contain a ":", so it must be a
+ * library name. Convert the name to a Pascal string
+ * and try to find the library.
+ */
+ }
+ else
+ {
+ /* name contained a "/" which means we need to suck off the last part */
+ /* of the path and pass that on the NSGetSharedLibrary */
+ /* this may not be what we really want to do .. because Java could */
+ /* be iterating through the whole LD path, and we'll find it if it's */
+ /* anywhere on that path -- it appears that's what UNIX and the PC do */
+ /* too...so we'll emulate but it could be wrong. */
+ name = strrchr(name, PR_DIRECTORY_SEPARATOR) + 1;
+ }
+
+ PStrFromCStr(name, pName);
+
+ err = NSGetSharedLibrary(pName, &connectionID, &main);
+ if (err != noErr)
+ goto unlock;
+
+ libName = name;
+ }
+ else
+ {
+ /*
+ * The name did contain a ":", so it must be a full path name.
+ * Now we have to do a lot of work to convert the path name to
+ * an FSSpec (silly, since we were probably just called from the
+ * MacFE plug-in code that already knew the FSSpec and converted
+ * it to a full path just to pass to us). First we copy out the
+ * volume name (the text leading up to the first ":"); then we
+ * separate the file name (the text following the last ":") from
+ * rest of the path. After converting the strings to Pascal
+ * format we can call GetCatInfo to get the parent directory ID
+ * of the file, and then (finally) make an FSSpec and call
+ * GetDiskFragment.
+ */
+ char* cMacPath = NULL;
+ char* cFileName = NULL;
+ char* position = NULL;
+ CInfoPBRec pb;
+ FSSpec fileSpec;
+ PRUint32 index;
+
+ /* Copy the name: we'll change it */
+ cMacPath = strdup(name);
+ if (cMacPath == NULL)
+ goto unlock;
+
+ /* First, get the vRefNum */
+ position = strchr(cMacPath, PR_PATH_SEPARATOR);
+ if ((position == cMacPath) || (position == NULL))
+ fileSpec.vRefNum = 0; /* Use application relative searching */
+ else
+ {
+ char cVolName[32];
+ memset(cVolName, 0, sizeof(cVolName));
+ strncpy(cVolName, cMacPath, position-cMacPath);
+ fileSpec.vRefNum = GetVolumeRefNumFromName(cVolName);
+ }
+
+ /* Next, break the path and file name apart */
+ index = 0;
+ while (cMacPath[index] != 0)
+ index++;
+ while (cMacPath[index] != PR_PATH_SEPARATOR && index > 0)
+ index--;
+ if (index == 0 || index == strlen(cMacPath))
+ {
+ PR_DELETE(cMacPath);
+ goto unlock;
+ }
+ cMacPath[index] = 0;
+ cFileName = &(cMacPath[index + 1]);
+
+ /* Convert the path and name into Pascal strings */
+ strcpy((char*) &pName, cMacPath);
+ c2pstr((char*) &pName);
+ strcpy((char*) &fileSpec.name, cFileName);
+ c2pstr((char*) &fileSpec.name);
+ strcpy(cName, cFileName);
+ PR_DELETE(cMacPath);
+ cMacPath = NULL;
+
+ /* Now we can look up the path on the volume */
+ pb.dirInfo.ioNamePtr = pName;
+ pb.dirInfo.ioVRefNum = fileSpec.vRefNum;
+ pb.dirInfo.ioDrDirID = 0;
+ pb.dirInfo.ioFDirIndex = 0;
+ err = PBGetCatInfoSync(&pb);
+ if (err != noErr)
+ goto unlock;
+ fileSpec.parID = pb.dirInfo.ioDrDirID;
+
+ /* Finally, try to load the library */
+ err = GetDiskFragment(&fileSpec, 0, kCFragGoesToEOF, fileSpec.name,
+ kLoadCFrag, &connectionID, &main, errName);
+
+ libName = cName;
+ if (err != noErr)
+ goto unlock;
+ }
+
+ lm->name = strdup(libName);
+ lm->dlh = connectionID;
+ lm->next = pr_loadmap;
+ pr_loadmap = lm;
+ }
+#elif defined(XP_MAC) && !GENERATINGCFM
+ {
+
+ }
+#endif
+
+#ifdef XP_UNIX
+#ifdef HAVE_DLL
+ {
+#if defined(USE_DLFCN)
+ void *h = dlopen(name, RTLD_LAZY);
+#elif defined(USE_HPSHL)
+ shl_t h = shl_load(name, BIND_DEFERRED | DYNAMIC_PATH, 0L);
+#else
+#error Configuration error
+#endif
+ if (!h) {
+ PR_DELETE(lm);
+ goto unlock;
+ }
+ lm->name = strdup(name);
+ lm->dlh = h;
+ lm->next = pr_loadmap;
+ pr_loadmap = lm;
+ }
+#endif /* HAVE_DLL */
+#endif /* XP_UNIX */
+
+ lm->refCount = 1;
+ result = lm; /* success */
+ PR_LOG(_pr_linker_lm, PR_LOG_MIN, ("Loaded library %s (load lib)", lm->name));
+
+ unlock:
+ if (result == NULL) {
+ PR_SetError(PR_LOAD_LIBRARY_ERROR, _MD_ERRNO());
+ DLLErrorInternal(_MD_ERRNO()); /* sets error text */
+ }
+ PR_ExitMonitor(pr_linker_lock);
+ return result;
+}
+
+PR_IMPLEMENT(PRLibrary*)
+PR_FindLibrary(const char *name)
+{
+ PRLibrary* result;
+
+ PR_EnterMonitor(pr_linker_lock);
+ result = pr_UnlockedFindLibrary(name);
+ PR_ExitMonitor(pr_linker_lock);
+ return result;
+}
+
+/*
+** Unload a shared library which was loaded via PR_LoadLibrary
+*/
+PR_IMPLEMENT(PRStatus)
+PR_UnloadLibrary(PRLibrary *lib)
+{
+ int result = 0;
+ PRStatus status = PR_SUCCESS;
+
+ if ((lib == 0) || (lib->refCount <= 0)) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ PR_EnterMonitor(pr_linker_lock);
+ if (--lib->refCount > 0) {
+ PR_LOG(_pr_linker_lm, PR_LOG_MIN,
+ ("%s decr => %d",
+ lib->name, lib->refCount));
+ goto done;
+ }
+#ifdef XP_UNIX
+#ifdef HAVE_DLL
+#ifdef USE_DLFCN
+ result = dlclose(lib->dlh);
+#elif defined(USE_HPSHL)
+ result = shl_unload(lib->dlh);
+#else
+#error Configuration error
+#endif
+#endif /* HAVE_DLL */
+#endif /* XP_UNIX */
+#ifdef XP_PC
+ if (lib->dlh) {
+ FreeLibrary((HINSTANCE)(lib->dlh));
+ lib->dlh = (HINSTANCE)NULL;
+ }
+#endif /* XP_PC */
+
+#if defined(XP_MAC) && GENERATINGCFM
+ /* Close the connection */
+ CloseConnection(&(lib->dlh));
+#endif
+
+ /* unlink from library search list */
+ if (pr_loadmap == lib)
+ pr_loadmap = pr_loadmap->next;
+ else if (pr_loadmap != NULL) {
+ PRLibrary* prev = pr_loadmap;
+ PRLibrary* next = pr_loadmap->next;
+ while (next != NULL) {
+ if (next == lib) {
+ prev->next = next->next;
+ goto freeLib;
+ }
+ prev = next;
+ next = next->next;
+ }
+ /*
+ * fail (the library is not on the _pr_loadmap list),
+ * but don't wipe out an error from dlclose/shl_unload.
+ */
+ PR_ASSERT(!"_pr_loadmap and lib->refCount inconsistent");
+ if (result == 0) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ status = PR_FAILURE;
+ }
+ }
+ /*
+ * We free the PRLibrary structure whether dlclose/shl_unload
+ * succeeds or not.
+ */
+
+ freeLib:
+ PR_LOG(_pr_linker_lm, PR_LOG_MIN, ("Unloaded library %s", lib->name));
+ PR_DELETE(lib->name);
+ PR_DELETE(lib);
+ if (result == -1) {
+ PR_SetError(PR_UNLOAD_LIBRARY_ERROR, _MD_ERRNO());
+ DLLErrorInternal(_MD_ERRNO());
+ status = PR_FAILURE;
+ }
+
+done:
+ PR_ExitMonitor(pr_linker_lock);
+ return status;
+}
+
+static void*
+pr_FindSymbolInLib(PRLibrary *lm, const char *name)
+{
+ void *f = NULL;
+
+ if (lm->staticTable != NULL) {
+ const PRStaticLinkTable* tp;
+ for (tp = lm->staticTable; tp->name; tp++) {
+ if (strcmp(name, tp->name) == 0) {
+ return (void*) tp->fp;
+ }
+ }
+ /*
+ ** If the symbol was not found in the static table then check if
+ ** the symbol was exported in the DLL... Win16 only!!
+ */
+#if !defined(WIN16)
+ PR_SetError(PR_FIND_SYMBOL_ERROR, 0);
+ return (void*)NULL;
+#endif
+ }
+
+#ifdef XP_OS2
+ DosQueryProcAddr(lm->dlh, 0, name, (PFN *) &f);
+#endif /* XP_OS2 */
+
+#if defined(WIN32) || defined(WIN16)
+ f = GetProcAddress(lm->dlh, name);
+#endif /* WIN32 || WIN16 */
+
+#ifdef XP_MAC
+ {
+ Ptr symAddr;
+ CFragSymbolClass symClass;
+ Str255 pName;
+
+ PStrFromCStr(name, pName);
+
+ f = (NSFindSymbol(lm->dlh, pName, &symAddr, &symClass) == noErr) ? symAddr : NULL;
+ }
+#endif /* XP_MAC */
+
+#ifdef XP_UNIX
+#ifdef HAVE_DLL
+#ifdef USE_DLFCN
+ f = dlsym(lm->dlh, name);
+#elif defined(USE_HPSHL)
+ if (shl_findsym(&lm->dlh, name, TYPE_PROCEDURE, &f) == -1)
+ f = NULL;
+#endif
+#endif /* HAVE_DLL */
+#endif /* XP_UNIX */
+ if (f == NULL) {
+ PR_SetError(PR_FIND_SYMBOL_ERROR, _MD_ERRNO());
+ DLLErrorInternal(_MD_ERRNO());
+ }
+ return f;
+}
+
+/*
+** Called by class loader to resolve missing native's
+*/
+PR_IMPLEMENT(void*)
+PR_FindSymbol(PRLibrary *lib, const char *raw_name)
+{
+ void *f = NULL;
+#if defined(SUNOS4) || defined(WIN16)
+ char *name;
+#else
+ const char *name;
+#endif
+ /*
+ ** Mangle the raw symbol name in any way that is platform specific.
+ */
+#if defined(SUNOS4)
+ /* Need a leading _ */
+ name = PR_smprintf("_%s", raw_name);
+#elif defined(AIX)
+ /*
+ ** AIX with the normal linker put's a "." in front of the symbol
+ ** name. When use "svcc" and "svld" then the "." disappears. Go
+ ** figure.
+ */
+ name = raw_name;
+#elif defined(WIN16)
+ /*
+ ** Win16. symbols have a leading '_'
+ */
+ name = PR_smprintf("_%s", raw_name);
+#else
+ name = raw_name;
+#endif
+
+ PR_EnterMonitor(pr_linker_lock);
+ PR_ASSERT(lib != NULL);
+ f = pr_FindSymbolInLib(lib, name);
+
+#if defined(SUNOS4) || defined(WIN16)
+ PR_smprintf_free(name);
+#endif
+
+ PR_ExitMonitor(pr_linker_lock);
+ return f;
+}
+
+PR_IMPLEMENT(void*)
+PR_FindSymbolAndLibrary(const char *raw_name, PRLibrary* *lib)
+{
+ void *f = NULL;
+#if defined(SUNOS4) || defined(WIN16)
+ char *name;
+#else
+ const char *name;
+#endif
+ PRLibrary* lm;
+
+ /*
+ ** Mangle the raw symbol name in any way that is platform specific.
+ */
+#if defined(SUNOS4)
+ /* Need a leading _ */
+ name = PR_smprintf("_%s", raw_name);
+#elif defined(AIX)
+ /*
+ ** AIX with the normal linker put's a "." in front of the symbol
+ ** name. When use "svcc" and "svld" then the "." disappears. Go
+ ** figure.
+ */
+ name = raw_name;
+#elif defined(WIN16)
+ /*
+ ** Win16. symbols have a leading '_'
+ */
+ name = PR_smprintf("_%s", raw_name);
+#else
+ name = raw_name;
+#endif
+
+ PR_EnterMonitor(pr_linker_lock);
+
+ /* search all libraries */
+ for (lm = pr_loadmap; lm != NULL; lm = lm->next) {
+ f = pr_FindSymbolInLib(lm, name);
+ if (f != NULL) {
+ *lib = lm;
+ lm->refCount++;
+ PR_LOG(_pr_linker_lm, PR_LOG_MIN,
+ ("%s incr => %d (for %s)",
+ lm->name, lm->refCount, name));
+ break;
+ }
+ }
+#if defined(SUNOS4) || defined(WIN16)
+ PR_smprintf_free(name);
+#endif
+
+ PR_ExitMonitor(pr_linker_lock);
+ return f;
+}
+
+/*
+** Add a static library to the list of loaded libraries. If LoadLibrary
+** is called with the name then we will pretend it was already loaded
+*/
+PR_IMPLEMENT(PRLibrary*)
+PR_LoadStaticLibrary(const char *name, const PRStaticLinkTable *slt)
+{
+ PRLibrary *lm;
+ PRLibrary* result = NULL;
+
+ /* See if library is already loaded */
+ PR_EnterMonitor(pr_linker_lock);
+
+ /* If the lbrary is already loaded, then add the static table information... */
+ result = pr_UnlockedFindLibrary(name);
+ if (result != NULL) {
+ PR_ASSERT( (result->staticTable == NULL) || (result->staticTable == slt) );
+ result->staticTable = slt;
+ goto unlock;
+ }
+
+ /* Add library to list...Mark it static */
+ lm = PR_NEWZAP(PRLibrary);
+ if (lm == NULL) goto unlock;
+
+ lm->name = strdup(name);
+ lm->refCount = 1;
+ lm->dlh = pr_exe_loadmap ? pr_exe_loadmap->dlh : 0;
+ lm->staticTable = slt;
+ lm->next = pr_loadmap;
+ pr_loadmap = lm;
+
+ result = lm; /* success */
+ PR_ASSERT(lm->refCount == 1);
+ unlock:
+ PR_LOG(_pr_linker_lm, PR_LOG_MIN, ("Loaded library %s (static lib)", lm->name));
+ PR_ExitMonitor(pr_linker_lock);
+ return result;
+}
diff --git a/pr/src/malloc/Makefile b/pr/src/malloc/Makefile
new file mode 100644
index 00000000..62a2e17f
--- /dev/null
+++ b/pr/src/malloc/Makefile
@@ -0,0 +1,41 @@
+#
+# The contents of this file are subject to the Netscape Public License
+# Version 1.0 (the "NPL"); you may not use this file except in
+# compliance with the NPL. You may obtain a copy of the NPL at
+# http://www.mozilla.org/NPL/
+#
+# Software distributed under the NPL is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+# for the specific language governing rights and limitations under the
+# NPL.
+#
+# The Initial Developer of this code under the NPL is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1998 Netscape Communications Corporation. All Rights
+# Reserved.
+#
+
+#! gmake
+
+MOD_DEPTH = ../../..
+
+include $(MOD_DEPTH)/config/config.mk
+
+# Disable optimization of the nspr on SunOS4.1.3
+ifeq ($(OS_ARCH),SunOS)
+ifeq ($(OS_RELEASE),4.1.3_U1)
+OPTIMIZER =
+endif
+endif
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(DIST)/include/private -I$(DIST)/include
+
+CSRCS = prmalloc.c prmem.c
+
+include $(MOD_DEPTH)/config/rules.mk
+
+export:: $(TARGETS)
+
+install:: export
diff --git a/pr/src/malloc/prmalloc.c b/pr/src/malloc/prmalloc.c
new file mode 100644
index 00000000..b7357462
--- /dev/null
+++ b/pr/src/malloc/prmalloc.c
@@ -0,0 +1,1156 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+/*
+** We override malloc etc. on any platform which has preemption +
+** nspr20 user level threads. When we're debugging, we can make our
+** version of malloc fail occasionally.
+*/
+#ifdef _PR_OVERRIDE_MALLOC
+
+/*
+** Thread safe version of malloc, calloc, realloc, free
+*/
+#include <stdarg.h>
+
+#ifdef DEBUG
+#define SANITY
+#define EXTRA_SANITY
+#else
+#undef SANITY
+#undef EXTRA_SANITY
+#endif
+
+/* Forward decls */
+void *_PR_UnlockedMalloc(size_t size);
+void _PR_UnlockedFree(void *ptr);
+void *_PR_UnlockedRealloc(void *ptr, size_t size);
+void *_PR_UnlockedCalloc(size_t n, size_t elsize);
+
+/************************************************************************/
+
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ */
+
+/*
+ * Defining SANITY will enable some checks which will tell you if the users
+ * program did botch something
+ */
+
+/*
+ * Defining EXTRA_SANITY will enable some checks which are mostly related
+ * to internal conditions in malloc.c
+ */
+
+/*
+ * Very verbose progress on stdout...
+ */
+#if 0
+# define TRACE(foo) printf foo
+static int malloc_event;
+#else
+# define TRACE(foo)
+#endif
+
+/* XXX Pick a number, any number */
+# define malloc_pagesize 4096UL
+# define malloc_pageshift 12UL
+
+#ifdef XP_UNIX
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <memory.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#endif
+
+/*
+ * This structure describes a page's worth of chunks.
+ */
+
+struct pginfo {
+ struct pginfo *next; /* next on the free list */
+ char *page; /* Pointer to the page */
+ u_short size; /* size of this page's chunks */
+ u_short shift; /* How far to shift for this size chunks */
+ u_short free; /* How many free chunks */
+ u_short total; /* How many chunk */
+ u_long bits[1]; /* Which chunks are free */
+};
+
+struct pgfree {
+ struct pgfree *next; /* next run of free pages */
+ struct pgfree *prev; /* prev run of free pages */
+ char *page; /* pointer to free pages */
+ char *end; /* pointer to end of free pages */
+ u_long size; /* number of bytes free */
+};
+
+/*
+ * How many bits per u_long in the bitmap.
+ * Change only if not 8 bits/byte
+ */
+#define MALLOC_BITS (8*sizeof(u_long))
+
+/*
+ * Magic values to put in the page_directory
+ */
+#define MALLOC_NOT_MINE ((struct pginfo*) 0)
+#define MALLOC_FREE ((struct pginfo*) 1)
+#define MALLOC_FIRST ((struct pginfo*) 2)
+#define MALLOC_FOLLOW ((struct pginfo*) 3)
+#define MALLOC_MAGIC ((struct pginfo*) 4)
+
+/*
+ * Set to one when malloc_init has been called
+ */
+static unsigned initialized;
+
+/*
+ * The size of a page.
+ * Must be a integral multiplum of the granularity of mmap(2).
+ * Your toes will curl if it isn't a power of two
+ */
+#define malloc_pagemask ((malloc_pagesize)-1)
+
+/*
+ * The size of the largest chunk.
+ * Half a page.
+ */
+#define malloc_maxsize ((malloc_pagesize)>>1)
+
+/*
+ * malloc_pagesize == 1 << malloc_pageshift
+ */
+#ifndef malloc_pageshift
+static unsigned malloc_pageshift;
+#endif /* malloc_pageshift */
+
+/*
+ * The smallest allocation we bother about.
+ * Must be power of two
+ */
+#ifndef malloc_minsize
+static unsigned malloc_minsize;
+#endif /* malloc_minsize */
+
+/*
+ * The largest chunk we care about.
+ * Must be smaller than pagesize
+ * Must be power of two
+ */
+#ifndef malloc_maxsize
+static unsigned malloc_maxsize;
+#endif /* malloc_maxsize */
+
+#ifndef malloc_cache
+static unsigned malloc_cache;
+#endif /* malloc_cache */
+
+/*
+ * The offset from pagenumber to index into the page directory
+ */
+static u_long malloc_origo;
+
+/*
+ * The last index in the page directory we care about
+ */
+static u_long last_index;
+
+/*
+ * Pointer to page directory.
+ * Allocated "as if with" malloc
+ */
+static struct pginfo **page_dir;
+
+/*
+ * How many slots in the page directory
+ */
+static unsigned malloc_ninfo;
+
+/*
+ * Free pages line up here
+ */
+static struct pgfree free_list;
+
+/*
+ * Abort() if we fail to get VM ?
+ */
+static int malloc_abort;
+
+#ifdef SANITY
+/*
+ * Are we trying to die ?
+ */
+static int suicide;
+#endif
+
+/*
+ * dump statistics
+ */
+static int malloc_stats;
+
+/*
+ * always realloc ?
+ */
+static int malloc_realloc;
+
+/*
+ * my last break.
+ */
+static void *malloc_brk;
+
+/*
+ * one location cache for free-list holders
+ */
+static struct pgfree *px;
+
+static int set_pgdir(void *ptr, struct pginfo *info);
+static int extend_page_directory(u_long index);
+
+#ifdef SANITY
+void
+malloc_dump(FILE *fd)
+{
+ struct pginfo **pd;
+ struct pgfree *pf;
+ int j;
+
+ pd = page_dir;
+
+ /* print out all the pages */
+ for(j=0;j<=last_index;j++) {
+ fprintf(fd,"%08lx %5d ",(j+malloc_origo) << malloc_pageshift,j);
+ if (pd[j] == MALLOC_NOT_MINE) {
+ for(j++;j<=last_index && pd[j] == MALLOC_NOT_MINE;j++)
+ ;
+ j--;
+ fprintf(fd,".. %5d not mine\n", j);
+ } else if (pd[j] == MALLOC_FREE) {
+ for(j++;j<=last_index && pd[j] == MALLOC_FREE;j++)
+ ;
+ j--;
+ fprintf(fd,".. %5d free\n", j);
+ } else if (pd[j] == MALLOC_FIRST) {
+ for(j++;j<=last_index && pd[j] == MALLOC_FOLLOW;j++)
+ ;
+ j--;
+ fprintf(fd,".. %5d in use\n", j);
+ } else if (pd[j] < MALLOC_MAGIC) {
+ fprintf(fd,"(%p)\n", pd[j]);
+ } else {
+ fprintf(fd,"%p %d (of %d) x %d @ %p --> %p\n",
+ pd[j],pd[j]->free, pd[j]->total,
+ pd[j]->size, pd[j]->page, pd[j]->next);
+ }
+ }
+
+ for(pf=free_list.next; pf; pf=pf->next) {
+ fprintf(fd,"Free: @%p [%p...%p[ %ld ->%p <-%p\n",
+ pf,pf->page,pf->end,pf->size,pf->prev,pf->next);
+ if (pf == pf->next) {
+ fprintf(fd,"Free_list loops.\n");
+ break;
+ }
+ }
+
+ /* print out various info */
+ fprintf(fd,"Minsize\t%d\n",malloc_minsize);
+ fprintf(fd,"Maxsize\t%d\n",malloc_maxsize);
+ fprintf(fd,"Pagesize\t%d\n",malloc_pagesize);
+ fprintf(fd,"Pageshift\t%d\n",malloc_pageshift);
+ fprintf(fd,"FirstPage\t%ld\n",malloc_origo);
+ fprintf(fd,"LastPage\t%ld %lx\n",last_index+malloc_pageshift,
+ (last_index + malloc_pageshift) << malloc_pageshift);
+ fprintf(fd,"Break\t%ld\n",(u_long)sbrk(0) >> malloc_pageshift);
+}
+
+static void wrterror(char *fmt, ...)
+{
+ char *q = "malloc() error: ";
+ char buf[100];
+ va_list ap;
+
+ suicide = 1;
+
+ va_start(ap, fmt);
+ PR_vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ fputs(q, stderr);
+ fputs(buf, stderr);
+
+ malloc_dump(stderr);
+ PR_Abort();
+}
+
+static void wrtwarning(char *fmt, ...)
+{
+ char *q = "malloc() warning: ";
+ char buf[100];
+ va_list ap;
+
+ va_start(ap, fmt);
+ PR_vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ fputs(q, stderr);
+ fputs(buf, stderr);
+}
+#endif /* SANITY */
+
+
+/*
+ * Allocate a number of pages from the OS
+ */
+static caddr_t
+map_pages(int pages, int update)
+{
+ caddr_t result,tail;
+
+ result = ((caddr_t)sbrk(0)) + malloc_pagemask - 1;
+ result = (caddr_t) ((u_long)result & ~malloc_pagemask);
+ tail = result + (pages << malloc_pageshift);
+ if (!brk(tail)) {
+ last_index = ((u_long)tail >> malloc_pageshift) - malloc_origo -1;
+ malloc_brk = tail;
+ TRACE(("%6d S %p .. %p\n",malloc_event++, result, tail));
+ if (!update || last_index < malloc_ninfo ||
+ extend_page_directory(last_index))
+ return result;
+ }
+ TRACE(("%6d s %d %p %d\n",malloc_event++,pages,sbrk(0),errno));
+#ifdef EXTRA_SANITY
+ wrterror("map_pages fails\n");
+#endif
+ return 0;
+}
+
+#define set_bit(_pi,_bit) \
+ (_pi)->bits[(_bit)/MALLOC_BITS] |= 1L<<((_bit)%MALLOC_BITS)
+
+#define clr_bit(_pi,_bit) \
+ (_pi)->bits[(_bit)/MALLOC_BITS] &= ~(1L<<((_bit)%MALLOC_BITS));
+
+#define tst_bit(_pi,_bit) \
+ ((_pi)->bits[(_bit)/MALLOC_BITS] & (1L<<((_bit)%MALLOC_BITS)))
+
+/*
+ * Extend page directory
+ */
+static int
+extend_page_directory(u_long index)
+{
+ struct pginfo **new,**old;
+ int i;
+
+ TRACE(("%6d E %lu\n",malloc_event++,index));
+
+ /* Make it this many pages */
+ i = index * sizeof *page_dir;
+ i /= malloc_pagesize;
+ i += 2;
+
+ /* Get new pages, if you used this much mem you don't care :-) */
+ new = (struct pginfo**) map_pages(i,0);
+ if (!new)
+ return 0;
+
+ /* Copy the old stuff */
+ memset(new, 0, i * malloc_pagesize);
+ memcpy(new, page_dir,
+ malloc_ninfo * sizeof *page_dir);
+
+ /* register the new size */
+ malloc_ninfo = i * malloc_pagesize / sizeof *page_dir;
+
+ /* swap the pointers */
+ old = page_dir;
+ page_dir = new;
+
+ /* Mark the pages */
+ index = ((u_long)new >> malloc_pageshift) - malloc_origo;
+ page_dir[index] = MALLOC_FIRST;
+ while (--i) {
+ page_dir[++index] = MALLOC_FOLLOW;
+ }
+
+ /* Now free the old stuff */
+ _PR_UnlockedFree(old);
+ return 1;
+}
+
+/*
+ * Set entry in page directory.
+ * Extend page directory if need be.
+ */
+static int
+set_pgdir(void *ptr, struct pginfo *info)
+{
+ u_long index = ((u_long)ptr >> malloc_pageshift) - malloc_origo;
+
+ if (index >= malloc_ninfo && !extend_page_directory(index))
+ return 0;
+ page_dir[index] = info;
+ return 1;
+}
+
+/*
+ * Initialize the world
+ */
+static void
+malloc_init (void)
+{
+ int i;
+ char *p;
+
+ TRACE(("%6d I\n",malloc_event++));
+#ifdef DEBUG
+ for (p=getenv("MALLOC_OPTIONS"); p && *p; p++) {
+ switch (*p) {
+ case 'a': malloc_abort = 0; break;
+ case 'A': malloc_abort = 1; break;
+ case 'd': malloc_stats = 0; break;
+ case 'D': malloc_stats = 1; break;
+ case 'r': malloc_realloc = 0; break;
+ case 'R': malloc_realloc = 1; break;
+ default:
+ wrtwarning("Unknown chars in MALLOC_OPTIONS\n");
+ break;
+ }
+ }
+#endif
+
+#ifndef malloc_pagesize
+ /* determine our pagesize */
+ malloc_pagesize = getpagesize();
+#endif /* malloc_pagesize */
+
+#ifndef malloc_pageshift
+ /* determine how much we shift by to get there */
+ for (i = malloc_pagesize; i > 1; i >>= 1)
+ malloc_pageshift++;
+#endif /* malloc_pageshift */
+
+#ifndef malloc_cache
+ malloc_cache = 50 << malloc_pageshift;
+#endif /* malloc_cache */
+
+#ifndef malloc_minsize
+ /*
+ * find the smallest size allocation we will bother about.
+ * this is determined as the smallest allocation that can hold
+ * it's own pginfo;
+ */
+ i = 2;
+ for(;;) {
+ int j;
+
+ /* Figure out the size of the bits */
+ j = malloc_pagesize/i;
+ j /= 8;
+ if (j < sizeof(u_long))
+ j = sizeof (u_long);
+ if (sizeof(struct pginfo) + j - sizeof (u_long) <= i)
+ break;
+ i += i;
+ }
+ malloc_minsize = i;
+#endif /* malloc_minsize */
+
+
+ /* Allocate one page for the page directory */
+ page_dir = (struct pginfo **) map_pages(1,0);
+#ifdef SANITY
+ if (!page_dir)
+ wrterror("fatal: my first mmap failed. (check limits ?)\n");
+#endif
+
+ /*
+ * We need a maximum of malloc_pageshift buckets, steal these from the
+ * front of the page_directory;
+ */
+ malloc_origo = (u_long) page_dir >> malloc_pageshift;
+ malloc_origo -= malloc_pageshift;
+
+ /* Clear it */
+ memset(page_dir,0,malloc_pagesize);
+
+ /* Find out how much it tells us */
+ malloc_ninfo = malloc_pagesize / sizeof *page_dir;
+
+ /* Plug the page directory into itself */
+ i = set_pgdir(page_dir,MALLOC_FIRST);
+#ifdef SANITY
+ if (!i)
+ wrterror("fatal: couldn't set myself in the page directory\n");
+#endif
+
+ /* Been here, done that */
+ initialized++;
+}
+
+/*
+ * Allocate a number of complete pages
+ */
+static void *malloc_pages(size_t size)
+{
+ void *p,*delay_free = 0;
+ int i;
+ struct pgfree *pf;
+ u_long index;
+
+ /* How many pages ? */
+ size += (malloc_pagesize-1);
+ size &= ~malloc_pagemask;
+
+ p = 0;
+ /* Look for free pages before asking for more */
+ for(pf = free_list.next; pf; pf = pf->next) {
+#ifdef EXTRA_SANITY
+ if (pf->page == pf->end)
+ wrterror("zero entry on free_list\n");
+ if (pf->page > pf->end) {
+ TRACE(("%6d !s %p %p %p <%d>\n",malloc_event++,
+ pf,pf->page,pf->end,__LINE__));
+ wrterror("sick entry on free_list\n");
+ }
+ if ((void*)pf->page >= (void*)sbrk(0))
+ wrterror("entry on free_list past brk\n");
+ if (page_dir[((u_long)pf->page >> malloc_pageshift) - malloc_origo]
+ != MALLOC_FREE) {
+ TRACE(("%6d !f %p %p %p <%d>\n",malloc_event++,
+ pf,pf->page,pf->end,__LINE__));
+ wrterror("non-free first page on free-list\n");
+ }
+ if (page_dir[((u_long)pf->end >> malloc_pageshift) - 1 - malloc_origo]
+ != MALLOC_FREE)
+ wrterror("non-free last page on free-list\n");
+#endif /* EXTRA_SANITY */
+ if (pf->size < size)
+ continue;
+ else if (pf->size == size) {
+ p = pf->page;
+ if (pf->next)
+ pf->next->prev = pf->prev;
+ pf->prev->next = pf->next;
+ delay_free = pf;
+ break;
+ } else {
+ p = pf->page;
+ pf->page += size;
+ pf->size -= size;
+ break;
+ }
+ }
+#ifdef EXTRA_SANITY
+ if (p && page_dir[((u_long)p >> malloc_pageshift) - malloc_origo]
+ != MALLOC_FREE) {
+ wrterror("allocated non-free page on free-list\n");
+ }
+#endif /* EXTRA_SANITY */
+
+ size >>= malloc_pageshift;
+
+ /* Map new pages */
+ if (!p)
+ p = map_pages(size,1);
+
+ if (p) {
+ /* Mark the pages in the directory */
+ index = ((u_long)p >> malloc_pageshift) - malloc_origo;
+ page_dir[index] = MALLOC_FIRST;
+ for (i=1;i<size;i++)
+ page_dir[index+i] = MALLOC_FOLLOW;
+ }
+ if (delay_free) {
+ if (!px)
+ px = delay_free;
+ else
+ _PR_UnlockedFree(delay_free);
+ }
+ return p;
+}
+
+/*
+ * Allocate a page of fragments
+ */
+
+static int
+malloc_make_chunks(int bits)
+{
+ struct pginfo *bp;
+ void *pp;
+ int i,k,l;
+
+ /* Allocate a new bucket */
+ pp = malloc_pages(malloc_pagesize);
+ if (!pp)
+ return 0;
+ l = sizeof *bp - sizeof(u_long);
+ l += sizeof(u_long) *
+ (((malloc_pagesize >> bits)+MALLOC_BITS-1) / MALLOC_BITS);
+ if ((1<<(bits)) <= l+l) {
+ bp = (struct pginfo *)pp;
+ } else {
+ bp = (struct pginfo *)_PR_UnlockedMalloc(l);
+ }
+ if (!bp)
+ return 0;
+ bp->size = (1<<bits);
+ bp->shift = bits;
+ bp->total = bp->free = malloc_pagesize >> bits;
+ bp->next = page_dir[bits];
+ bp->page = pp;
+ i = set_pgdir(pp,bp);
+ if (!i)
+ return 0;
+
+ /* We can safely assume that there is nobody in this chain */
+ page_dir[bits] = bp;
+
+ /* set all valid bits in the bits */
+ k = bp->total;
+ i = 0;
+/*
+ for(;k-i >= MALLOC_BITS; i += MALLOC_BITS)
+ bp->bits[i / MALLOC_BITS] = ~0;
+*/
+ for(; i < k; i++)
+ set_bit(bp,i);
+
+ if (bp != pp)
+ return 1;
+
+ /* We may have used the first ones already */
+ for(i=0;l > 0;i++) {
+ clr_bit(bp,i);
+ bp->free--;
+ bp->total--;
+ l -= (1 << bits);
+ }
+ return 1;
+}
+
+/*
+ * Allocate a fragment
+ */
+static void *malloc_bytes(size_t size)
+{
+ size_t s;
+ int j;
+ struct pginfo *bp;
+ int k;
+ u_long *lp, bf;
+
+ /* Don't bother with anything less than this */
+ if (size < malloc_minsize) {
+ size = malloc_minsize;
+ }
+
+ /* Find the right bucket */
+ j = 1;
+ s = size - 1;
+ while (s >>= 1) {
+ j++;
+ }
+
+ /* If it's empty, make a page more of that size chunks */
+ if (!page_dir[j] && !malloc_make_chunks(j))
+ return 0;
+
+ /* Find first word of bitmap which isn't empty */
+ bp = page_dir[j];
+ for (lp = bp->bits; !*lp; lp++)
+ ;
+
+ /* Find that bit */
+ bf = *lp;
+ k = 0;
+ while ((bf & 1) == 0) {
+ bf >>= 1;
+ k++;
+ }
+
+ *lp ^= 1L<<k; /* clear it */
+ bp->free--;
+ if (!bp->free) {
+ page_dir[j] = bp->next;
+ bp->next = 0;
+ }
+ k += (lp - bp->bits)*MALLOC_BITS;
+ return bp->page + (k << bp->shift);
+}
+
+void *_PR_UnlockedMalloc(size_t size)
+{
+ void *result;
+
+ /* Round up to a multiple of 8 bytes */
+ if (size & 7) {
+ size = size + 8 - (size & 7);
+ }
+
+ if (!initialized)
+ malloc_init();
+
+#ifdef SANITY
+ if (suicide)
+ PR_Abort();
+#endif
+
+ if (size <= malloc_maxsize)
+ result = malloc_bytes(size);
+ else
+ result = malloc_pages(size);
+#ifdef SANITY
+ if (malloc_abort && !result)
+ wrterror("malloc() returns NULL\n");
+#endif
+ TRACE(("%6d M %p %d\n",malloc_event++,result,size));
+
+ return result;
+}
+
+void *_PR_UnlockedMemalign(size_t alignment, size_t size)
+{
+ void *result;
+
+ /*
+ * alignment has to be a power of 2
+ */
+
+ if ((size <= alignment) && (alignment <= malloc_maxsize))
+ size = alignment;
+ else
+ size += alignment - 1;
+
+ /* Round up to a multiple of 8 bytes */
+ if (size & 7) {
+ size = size + 8 - (size & 7);
+ }
+
+ if (!initialized)
+ malloc_init();
+
+#ifdef SANITY
+ if (suicide)
+ abort();
+#endif
+
+ if (size <= malloc_maxsize)
+ result = malloc_bytes(size);
+ else
+ result = malloc_pages(size);
+#ifdef SANITY
+ if (malloc_abort && !result)
+ wrterror("malloc() returns NULL\n");
+#endif
+ TRACE(("%6d A %p %d\n",malloc_event++,result,size));
+
+ if ((u_long)result & (alignment - 1))
+ return ((void *)(((u_long)result + alignment) & ~(alignment - 1)));
+ else
+ return result;
+}
+
+void *_PR_UnlockedCalloc(size_t n, size_t nelem)
+{
+ void *p;
+
+ /* Compute total size and then round up to a double word amount */
+ n *= nelem;
+ if (n & 7) {
+ n = n + 8 - (n & 7);
+ }
+
+ /* Get the memory */
+ p = _PR_UnlockedMalloc(n);
+ if (p) {
+ /* Zero it */
+ memset(p, 0, n);
+ }
+ return p;
+}
+
+/*
+ * Change an allocation's size
+ */
+void *_PR_UnlockedRealloc(void *ptr, size_t size)
+{
+ void *p;
+ u_long osize,page,index,tmp_index;
+ struct pginfo **mp;
+
+ if (!initialized)
+ malloc_init();
+
+#ifdef SANITY
+ if (suicide)
+ PR_Abort();
+#endif
+
+ /* used as free() */
+ TRACE(("%6d R %p %d\n",malloc_event++, ptr, size));
+ if (ptr && !size) {
+ _PR_UnlockedFree(ptr);
+ return _PR_UnlockedMalloc (1);
+ }
+
+ /* used as malloc() */
+ if (!ptr) {
+ p = _PR_UnlockedMalloc(size);
+ return p;
+ }
+
+ /* Find the page directory entry for the page in question */
+ page = (u_long)ptr >> malloc_pageshift;
+ index = page - malloc_origo;
+
+ /*
+ * check if memory was allocated by memalign
+ */
+ tmp_index = index;
+ while (page_dir[tmp_index] == MALLOC_FOLLOW)
+ tmp_index--;
+ if (tmp_index != index) {
+ /*
+ * memalign-allocated memory
+ */
+ index = tmp_index;
+ page = index + malloc_origo;
+ ptr = (void *) (page << malloc_pageshift);
+ }
+ TRACE(("%6d R2 %p %d\n",malloc_event++, ptr, size));
+
+ /* make sure it makes sense in some fashion */
+ if (index < malloc_pageshift || index > last_index) {
+#ifdef SANITY
+ wrtwarning("junk pointer passed to realloc()\n");
+#endif
+ return 0;
+ }
+
+ /* find the size of that allocation, and see if we need to relocate */
+ mp = &page_dir[index];
+ if (*mp == MALLOC_FIRST) {
+ osize = malloc_pagesize;
+ while (mp[1] == MALLOC_FOLLOW) {
+ osize += malloc_pagesize;
+ mp++;
+ }
+ if (!malloc_realloc &&
+ size < osize &&
+ size > malloc_maxsize &&
+ size > (osize - malloc_pagesize)) {
+ return ptr;
+ }
+ } else if (*mp >= MALLOC_MAGIC) {
+ osize = (*mp)->size;
+ if (!malloc_realloc &&
+ size < osize &&
+ (size > (*mp)->size/2 || (*mp)->size == malloc_minsize)) {
+ return ptr;
+ }
+ } else {
+#ifdef SANITY
+ wrterror("realloc() of wrong page.\n");
+#endif
+ }
+
+ /* try to reallocate */
+ p = _PR_UnlockedMalloc(size);
+
+ if (p) {
+ /* copy the lesser of the two sizes */
+ if (osize < size)
+ memcpy(p,ptr,osize);
+ else
+ memcpy(p,ptr,size);
+ _PR_UnlockedFree(ptr);
+ }
+#ifdef DEBUG
+ else if (malloc_abort)
+ wrterror("realloc() returns NULL\n");
+#endif
+
+ return p;
+}
+
+/*
+ * Free a sequence of pages
+ */
+
+static void
+free_pages(char *ptr, u_long page, int index, struct pginfo *info)
+{
+ int i;
+ struct pgfree *pf,*pt;
+ u_long l;
+ char *tail;
+
+ TRACE(("%6d FP %p %d\n",malloc_event++, ptr, page));
+ /* Is it free already ? */
+ if (info == MALLOC_FREE) {
+#ifdef SANITY
+ wrtwarning("freeing free page at %p.\n", ptr);
+#endif
+ return;
+ }
+
+#ifdef SANITY
+ /* Is it not the right place to begin ? */
+ if (info != MALLOC_FIRST)
+ wrterror("freeing wrong page.\n");
+
+ /* Is this really a pointer to a page ? */
+ if ((u_long)ptr & malloc_pagemask)
+ wrterror("freeing messed up page pointer.\n");
+#endif
+
+ /* Count how many pages it is anyway */
+ page_dir[index] = MALLOC_FREE;
+ for (i = 1; page_dir[index+i] == MALLOC_FOLLOW; i++)
+ page_dir[index + i] = MALLOC_FREE;
+
+ l = i << malloc_pageshift;
+
+ tail = ptr+l;
+
+ /* add to free-list */
+ if (!px)
+ px = _PR_UnlockedMalloc(sizeof *pt);
+ /* XXX check success */
+ px->page = ptr;
+ px->end = tail;
+ px->size = l;
+ if (!free_list.next) {
+ px->next = free_list.next;
+ px->prev = &free_list;
+ free_list.next = px;
+ pf = px;
+ px = 0;
+ } else {
+ tail = ptr+l;
+ for(pf = free_list.next; pf->next && pf->end < ptr; pf = pf->next)
+ ;
+ for(; pf; pf = pf->next) {
+ if (pf->end == ptr ) {
+ /* append to entry */
+ pf->end += l;
+ pf->size += l;
+ if (pf->next && pf->end == pf->next->page ) {
+ pt = pf->next;
+ pf->end = pt->end;
+ pf->size += pt->size;
+ pf->next = pt->next;
+ if (pf->next)
+ pf->next->prev = pf;
+ _PR_UnlockedFree(pt);
+ }
+ } else if (pf->page == tail) {
+ /* prepend to entry */
+ pf->size += l;
+ pf->page = ptr;
+ } else if (pf->page > ptr) {
+ px->next = pf;
+ px->prev = pf->prev;
+ pf->prev = px;
+ px->prev->next = px;
+ pf = px;
+ px = 0;
+ } else if (!pf->next) {
+ px->next = 0;
+ px->prev = pf;
+ pf->next = px;
+ pf = px;
+ px = 0;
+ } else {
+ continue;
+ }
+ break;
+ }
+ }
+ if (!pf->next &&
+ pf->size > malloc_cache &&
+ pf->end == malloc_brk &&
+ malloc_brk == (void*)sbrk(0)) {
+ pf->end = pf->page + malloc_cache;
+ pf->size = malloc_cache;
+ TRACE(("%6d U %p %d\n",malloc_event++,pf->end,pf->end - pf->page));
+ brk(pf->end);
+ malloc_brk = pf->end;
+ /* Find the page directory entry for the page in question */
+ page = (u_long)pf->end >> malloc_pageshift;
+ index = page - malloc_origo;
+ /* Now update the directory */
+ for(i=index;i <= last_index;)
+ page_dir[i++] = MALLOC_NOT_MINE;
+ last_index = index - 1;
+ }
+}
+
+/*
+ * Free a chunk, and possibly the page it's on, if the page becomes empty.
+ */
+
+static void
+free_bytes(void *ptr, u_long page, int index, struct pginfo *info)
+{
+ int i;
+ struct pginfo **mp;
+ void *vp;
+
+ /* Make sure that pointer is multiplum of chunk-size */
+#ifdef SANITY
+ if ((u_long)ptr & (info->size - 1))
+ wrterror("freeing messed up chunk pointer\n");
+#endif
+
+ /* Find the chunk number on the page */
+ i = ((u_long)ptr & malloc_pagemask) >> info->shift;
+
+ /* See if it's free already */
+ if (tst_bit(info,i)) {
+#ifdef SANITY
+ wrtwarning("freeing free chunk at %p\n", ptr);
+#endif
+ return;
+ }
+
+ /* Mark it free */
+ set_bit(info,i);
+ info->free++;
+
+ /* If the page was full before, we need to put it on the queue now */
+ if (info->free == 1) {
+ mp = page_dir + info->shift;
+ while (*mp && (*mp)->next && (*mp)->next->page < info->page)
+ mp = &(*mp)->next;
+ info->next = *mp;
+ *mp = info;
+ return;
+ }
+
+ /* If this page isn't empty, don't do anything. */
+ if (info->free != info->total)
+ return;
+
+ /* We may want to keep at least one page of each size chunks around. */
+ mp = page_dir + info->shift;
+ if (0 && (*mp == info) && !info->next)
+ return;
+
+ /* Find & remove this page in the queue */
+ while (*mp != info) {
+ mp = &((*mp)->next);
+#ifdef EXTRA_SANITY
+ if (!*mp) {
+ TRACE(("%6d !q %p\n",malloc_event++,info));
+ wrterror("Not on queue\n");
+ }
+#endif
+ }
+ *mp = info->next;
+
+ /* Free the page & the info structure if need be */
+ set_pgdir(info->page,MALLOC_FIRST);
+ if((void*)info->page == (void*)info) {
+ _PR_UnlockedFree(info->page);
+ } else {
+ vp = info->page;
+ _PR_UnlockedFree(info);
+ _PR_UnlockedFree(vp);
+ }
+}
+
+void _PR_UnlockedFree(void *ptr)
+{
+ u_long page;
+ struct pginfo *info;
+ int index, tmp_index;
+
+ TRACE(("%6d F %p\n",malloc_event++,ptr));
+ /* This is legal */
+ if (!ptr)
+ return;
+
+#ifdef SANITY
+ /* There wouldn't be anything to free */
+ if (!initialized) {
+ wrtwarning("free() called before malloc() ever got called\n");
+ return;
+ }
+#endif
+
+#ifdef SANITY
+ if (suicide)
+ PR_Abort();
+#endif
+
+ /* Find the page directory entry for the page in question */
+ page = (u_long)ptr >> malloc_pageshift;
+ index = page - malloc_origo;
+
+ /*
+ * check if memory was allocated by memalign
+ */
+ tmp_index = index;
+ while (page_dir[tmp_index] == MALLOC_FOLLOW)
+ tmp_index--;
+ if (tmp_index != index) {
+ /*
+ * memalign-allocated memory
+ */
+ index = tmp_index;
+ page = index + malloc_origo;
+ ptr = (void *) (page << malloc_pageshift);
+ }
+ /* make sure it makes sense in some fashion */
+ if (index < malloc_pageshift) {
+#ifdef SANITY
+ wrtwarning("junk pointer %p (low) passed to free()\n", ptr);
+#endif
+ return;
+ }
+ if (index > last_index) {
+#ifdef SANITY
+ wrtwarning("junk pointer %p (high) passed to free()\n", ptr);
+#endif
+ return;
+ }
+
+ /* handle as page-allocation or chunk allocation */
+ info = page_dir[index];
+ if (info < MALLOC_MAGIC)
+ free_pages(ptr,page,index,info);
+ else
+ free_bytes(ptr,page,index,info);
+ return;
+}
+#endif /* _PR_OVERRIDE_MALLOC */
diff --git a/pr/src/malloc/prmem.c b/pr/src/malloc/prmem.c
new file mode 100644
index 00000000..c9319216
--- /dev/null
+++ b/pr/src/malloc/prmem.c
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+** Thread safe versions of malloc, free, realloc, calloc and cfree.
+*/
+
+#include "primpl.h"
+
+/*
+** The PR_Malloc, PR_Calloc, PR_Realloc, and PR_Free functions simply
+** call their libc equivalents now. This may seem redundant, but it
+** ensures that we are calling into the same runtime library. On
+** Win32, it is possible to have multiple runtime libraries (e.g.,
+** objects compiled with /MD and /MDd) in the same process, and
+** they maintain separate heaps, which cannot be mixed.
+*/
+PR_IMPLEMENT(void *) PR_Malloc(PRUint32 size)
+{
+#if defined (WIN16)
+ return PR_MD_malloc( (size_t) size);
+#else
+ return malloc(size);
+#endif
+}
+
+PR_IMPLEMENT(void *) PR_Calloc(PRUint32 nelem, PRUint32 elsize)
+{
+#if defined (WIN16)
+ return PR_MD_calloc( (size_t)nelem, (size_t)elsize );
+
+#else
+ return calloc(nelem, elsize);
+#endif
+}
+
+PR_IMPLEMENT(void *) PR_Realloc(void *ptr, PRUint32 size)
+{
+#if defined (WIN16)
+ return PR_MD_realloc( ptr, (size_t) size);
+#else
+ return realloc(ptr, size);
+#endif
+}
+
+PR_IMPLEMENT(void) PR_Free(void *ptr)
+{
+#if defined (WIN16)
+ PR_MD_free( ptr );
+#else
+ free(ptr);
+#endif
+}
+
+/*
+** Complexity alert!
+**
+** If malloc/calloc/free (etc.) were implemented to use pr lock's then
+** the entry points could block when called if some other thread had the
+** lock.
+**
+** Most of the time this isn't a problem. However, in the case that we
+** are using the thread safe malloc code after PR_Init but before
+** PR_AttachThread has been called (on a native thread that nspr has yet
+** to be told about) we could get royally screwed if the lock was busy
+** and we tried to context switch the thread away. In this scenario
+** PR_CURRENT_THREAD() == NULL
+**
+** To avoid this unfortunate case, we use the low level locking
+** facilities for malloc protection instead of the slightly higher level
+** locking. This makes malloc somewhat faster so maybe it's a good thing
+** anyway.
+*/
+#ifdef _PR_OVERRIDE_MALLOC
+
+/* Imports */
+extern void *_PR_UnlockedMalloc(size_t size);
+extern void *_PR_UnlockedMemalign(size_t alignment, size_t size);
+extern void _PR_UnlockedFree(void *ptr);
+extern void *_PR_UnlockedRealloc(void *ptr, size_t size);
+extern void *_PR_UnlockedCalloc(size_t n, size_t elsize);
+
+static PRBool _PR_malloc_initialised = PR_FALSE;
+
+#ifdef _PR_PTHREADS
+static pthread_mutex_t _PR_MD_malloc_crustylock;
+
+#define _PR_Lock_Malloc() { \
+ if(PR_TRUE == _PR_malloc_initialised) { \
+ PRStatus rv; \
+ rv = pthread_mutex_lock(&_PR_MD_malloc_crustylock); \
+ PR_ASSERT(0 == rv); \
+ }
+
+#define _PR_Unlock_Malloc() if(PR_TRUE == _PR_malloc_initialised) { \
+ PRStatus rv; \
+ rv = pthread_mutex_unlock(&_PR_MD_malloc_crustylock); \
+ PR_ASSERT(0 == rv); \
+ } \
+ }
+#else /* _PR_PTHREADS */
+static _MDLock _PR_MD_malloc_crustylock;
+
+#define _PR_Lock_Malloc() { \
+ PRIntn _is; \
+ if(PR_TRUE == _PR_malloc_initialised) { \
+ if (_PR_MD_CURRENT_THREAD() && \
+ !_PR_IS_NATIVE_THREAD( \
+ _PR_MD_CURRENT_THREAD())) \
+ _PR_INTSOFF(_is); \
+ _PR_MD_LOCK(&_PR_MD_malloc_crustylock); \
+ }
+
+#define _PR_Unlock_Malloc() if(PR_TRUE == _PR_malloc_initialised) { \
+ _PR_MD_UNLOCK(&_PR_MD_malloc_crustylock); \
+ if (_PR_MD_CURRENT_THREAD() && \
+ !_PR_IS_NATIVE_THREAD( \
+ _PR_MD_CURRENT_THREAD())) \
+ _PR_INTSON(_is); \
+ } \
+ }
+#endif /* _PR_PTHREADS */
+
+PR_IMPLEMENT(PRStatus) _PR_MallocInit(void)
+{
+ PRStatus rv = PR_SUCCESS;
+
+ if( PR_TRUE == _PR_malloc_initialised ) return PR_SUCCESS;
+
+#ifdef _PR_PTHREADS
+ {
+ int status;
+ pthread_mutexattr_t mattr;
+
+ status = PTHREAD_MUTEXATTR_INIT(&mattr);
+ PR_ASSERT(0 == status);
+ status = PTHREAD_MUTEX_INIT(_PR_MD_malloc_crustylock, mattr);
+ PR_ASSERT(0 == status);
+ status = PTHREAD_MUTEXATTR_DESTROY(&mattr);
+ PR_ASSERT(0 == status);
+ }
+#else /* _PR_PTHREADS */
+ _MD_NEW_LOCK(&_PR_MD_malloc_crustylock);
+#endif /* _PR_PTHREADS */
+
+ if( PR_SUCCESS == rv )
+ {
+ _PR_malloc_initialised = PR_TRUE;
+ }
+
+ return rv;
+}
+
+void *malloc(size_t size)
+{
+ void *p;
+ _PR_Lock_Malloc();
+ p = _PR_UnlockedMalloc(size);
+ _PR_Unlock_Malloc();
+ return p;
+}
+
+#if defined(IRIX)
+void *memalign(size_t alignment, size_t size)
+{
+ void *p;
+ _PR_Lock_Malloc();
+ p = _PR_UnlockedMemalign(alignment, size);
+ _PR_Unlock_Malloc();
+ return p;
+}
+
+void *valloc(size_t size)
+{
+ return(memalign(sysconf(_SC_PAGESIZE),size));
+}
+#endif /* IRIX */
+
+void free(void *ptr)
+{
+ _PR_Lock_Malloc();
+ _PR_UnlockedFree(ptr);
+ _PR_Unlock_Malloc();
+}
+
+void *realloc(void *ptr, size_t size)
+{
+ void *p;
+ _PR_Lock_Malloc();
+ p = _PR_UnlockedRealloc(ptr, size);
+ _PR_Unlock_Malloc();
+ return p;
+}
+
+void *calloc(size_t n, size_t elsize)
+{
+ void *p;
+ _PR_Lock_Malloc();
+ p = _PR_UnlockedCalloc(n, elsize);
+ _PR_Unlock_Malloc();
+ return p;
+}
+
+void cfree(void *p)
+{
+ _PR_Lock_Malloc();
+ _PR_UnlockedFree(p);
+ _PR_Unlock_Malloc();
+}
+
+void _PR_InitMem(void)
+{
+ PRStatus rv;
+ rv = _PR_MallocInit();
+ PR_ASSERT(PR_SUCCESS == rv);
+}
+
+#endif /* _PR_OVERRIDE_MALLOC */
diff --git a/pr/src/md/Makefile b/pr/src/md/Makefile
new file mode 100644
index 00000000..92460077
--- /dev/null
+++ b/pr/src/md/Makefile
@@ -0,0 +1,57 @@
+#
+# The contents of this file are subject to the Netscape Public License
+# Version 1.0 (the "NPL"); you may not use this file except in
+# compliance with the NPL. You may obtain a copy of the NPL at
+# http://www.mozilla.org/NPL/
+#
+# Software distributed under the NPL is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+# for the specific language governing rights and limitations under the
+# NPL.
+#
+# The Initial Developer of this code under the NPL is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1998 Netscape Communications Corporation. All Rights
+# Reserved.
+#
+
+#! gmake
+
+MOD_DEPTH = ../../..
+
+include $(MOD_DEPTH)/config/config.mk
+
+ifeq ($(OS_ARCH),MAC)
+DIRS = mac
+else
+ ifeq ($(OS_ARCH),WINNT)
+ ifeq ($(OS_TARGET),OS2)
+ DIRS = os2
+ else
+ DIRS = windows
+ endif
+ else
+ DIRS = unix
+ endif
+endif
+
+# Disable optimization of the nspr on SunOS4.1.3
+ifeq ($(OS_ARCH),SunOS)
+ifeq ($(OS_RELEASE),4.1.3_U1)
+OPTIMIZER =
+endif
+endif
+
+CSRCS = \
+ prosdep.c \
+ $(NULL)
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(DIST)/include/private -I$(DIST)/include
+
+include $(MOD_DEPTH)/config/rules.mk
+
+export:: $(TARGETS)
+
+install:: export
diff --git a/pr/src/md/mac/MacErrorHandling.h b/pr/src/md/mac/MacErrorHandling.h
new file mode 100644
index 00000000..8b9b795f
--- /dev/null
+++ b/pr/src/md/mac/MacErrorHandling.h
@@ -0,0 +1,633 @@
+
+/*********************************************************************
+
+FILENAME
+ Exceptions.h
+
+DESCRIPTION
+ A collection of routines and macros to handle assertions and
+ exceptions.
+
+COPYRIGHT
+ Copyright © Apple Computer, Inc. 1989-1991
+ All rights reserved.
+
+ROUTINES
+ EXTERNALS
+ dprintf
+ check_dprintf
+ checkpos_dprintf
+
+MACROS
+ EXTERNALS
+ check
+ ncheck
+ check_action
+ ncheck_action
+ require
+ nrequire
+ require_action
+ nrequire_action
+ resume
+
+MODIFICATION HISTORY
+ Nov 12 95 BKJ Moved to MetroWerks environment & the NSPR
+
+NOTE
+ To keep code size down, use these routines and macros with the C
+ compiler option -b2 or -b3. This will eliminate duplicate strings
+ within a procedure.
+
+*********************************************************************/
+
+#ifndef __MACERRORHANDLING__
+#define __MACERRORHANDLING__
+
+/*********************************************************************
+
+INCLUDES
+
+*********************************************************************/
+
+#include <Types.h>
+
+/*<FF>*/
+/*********************************************************************
+
+CONSTANTS AND CONTROL
+
+*********************************************************************/
+
+/*
+ These defines are used to control the amount of information
+ displayed when an assertion fails. DEBUGOFF and WARN will run
+ silently. MIN will simply break into the debugger. ON will break
+ and display the assertion that failed and the exception (for
+ require statements). FULL will also display the source file name
+ and line number. SYM does a SysBreak and is usefull when using a
+ symbolic debugger like SourceBug or SADE. They should be set into
+ DEBUGLEVEL. The default LEVEL is OFF.
+*/
+
+#define DEBUGOFF 0
+#define DEBUGWARN 1
+#define DEBUGMIN 2
+#define DEBUGON 3
+#define DEBUGFULL 4
+#define DEBUGSYM 6
+
+#ifndef DEBUGLEVEL
+#define DEBUGLEVEL DEBUGOFF
+#endif DEBUGLEVEL
+
+/*
+ resumeLabel is used to control the insertion of labels for use with
+ the resume macro. If you do not use the resume macro and you wish
+ to have multible exceptions per label then you can add the
+ following define to you source code.
+
+*/
+#define resumeLabel(exception) // Multiple exceptions per label
+// #define resumeLabel(exception) resume_ ## exception: // Single exception per label
+
+
+/*
+ traceon and debugon are used to test for options
+*/
+
+#define traceon ((DEBUGLEVEL > DEBUGWARN) && defined(TRACEON))
+#define debugon (DEBUGLEVEL > DEBUGWARN)
+
+/*
+ Add some macros for DEBUGMIN and DEBUGSYM to keep the size down.
+*/
+
+#define __DEBUGSMALL ((DEBUGLEVEL == DEBUGMIN) || \
+ (DEBUGLEVEL == DEBUGSYM))
+
+#if DEBUGLEVEL == DEBUGMIN
+#define __DebuggerBreak Debugger()
+#elif DEBUGLEVEL == DEBUGSYM
+#define __DebuggerBreak SysBreak()
+#endif
+
+
+/*<FF>*/
+/*********************************************************************
+
+MACRO
+ check(assertion)
+
+DESCRIPTION
+ If debugging is on then check will test assertion and if it fails
+ break into the debugger. Otherwise check does nothing.
+
+*********************************************************************/
+
+#if __DEBUGSMALL
+
+#define check(assertion) \
+ do { \
+ if (assertion) ; \
+ else __DebuggerBreak; \
+ } while (false)
+
+#elif DEBUGLEVEL == DEBUGON
+
+#define check(assertion) \
+ do { \
+ if (assertion) ; \
+ else { \
+ dprintf(notrace, "Assertion \"%s\" Failed", #assertion); \
+ } \
+ } while (false)
+
+#elif DEBUGLEVEL == DEBUGFULL
+
+#define check(assertion) \
+ do { \
+ if (assertion) ; \
+ else { \
+ dprintf(notrace, "Assertion \"%s\" Failed\n" \
+ "File: %s\n" \
+ "Line: %d", \
+ #assertion, __FILE__, __LINE__); \
+ } \
+ } while (false)
+
+#else
+
+#define check(assertion)
+
+#endif
+
+/*<FF>*/
+/*********************************************************************
+
+MACRO
+ ncheck(assertion)
+
+DESCRIPTION
+ If debugging is on then ncheck will test !assertion and if it fails
+ break into the debugger. Otherwise ncheck does nothing.
+
+*********************************************************************/
+
+#if __DEBUGSMALL
+
+#define ncheck(assertion) \
+ do { \
+ if (assertion) __DebuggerBreak; \
+ } while (false)
+
+#elif DEBUGLEVEL == DEBUGON
+
+#define ncheck(assertion) \
+ do { \
+ void* __privateAssertion = (void*)(assertion); \
+ \
+ if (__privateAssertion) { \
+ dprintf(notrace, "Assertion \"!(%s [= %#08X])\" Failed", \
+ #assertion, __privateAssertion); \
+ } \
+ } while (false)
+
+#elif DEBUGLEVEL == DEBUGFULL
+
+#define ncheck(assertion) \
+ do { \
+ void* __privateAssertion = (void*)(assertion); \
+ \
+ if (__privateAssertion) { \
+ dprintf(notrace, "Assertion \"!(%s [= %#08X])\" Failed\n" \
+ "File: %s\n" \
+ "Line: %d", \
+ #assertion, __privateAssertion, __FILE__, __LINE__); \
+ } \
+ } while (false)
+
+#else
+
+#define ncheck(assertion)
+
+#endif
+
+/*<FF>*/
+/*********************************************************************
+
+MACRO
+ check_action(assertion, action)
+
+DESCRIPTION
+ If debugging is on then check_action will test assertion and if it
+ fails break into the debugger then execute action. Otherwise
+ check_action does nothing.
+
+*********************************************************************/
+
+#if __DEBUGSMALL
+
+#define check_action(assertion, action) \
+ do { \
+ if (assertion) ; \
+ else { \
+ __DebuggerBreak; \
+ { action } \
+ } while (false)
+
+#elif DEBUGLEVEL == DEBUGON
+
+#define check_action(assertion, action) \
+ do { \
+ if (assertion) ; \
+ else { \
+ dprintf(notrace, "Assertion \"%s\" Failed", #assertion); \
+ { action } \
+ } \
+ } while (false)
+
+#elif DEBUGLEVEL == DEBUGFULL
+
+#define check_action(assertion, action) \
+ do { \
+ if (assertion) ; \
+ else { \
+ dprintf(notrace, "Assertion \"%s\" Failed\n" \
+ "File: %s\n" \
+ "Line: %d", \
+ #assertion, __FILE__, __LINE__); \
+ { action } \
+ } \
+ } while (false)
+
+#else
+
+#define check_action(assertion, action)
+
+#endif
+
+/*<FF>*/
+/**************************************************************************************
+
+MACRO
+ ncheck_action(assertion, action)
+
+DESCRIPTION
+ If debugging is on then ncheck_action will test !assertion and if
+ it fails break into the debugger then execute action. Otherwise
+ ncheck_action does nothing.
+
+*********************************************************************/
+
+#if __DEBUGSMALL
+
+#define ncheck_action(assertion, action) \
+ do { \
+ if (assertion) { \
+ __DebuggerBreak; \
+ { action } \
+ } \
+ } while (false)
+
+#elif DEBUGLEVEL == DEBUGON
+
+#define ncheck_action(assertion, action) \
+ do { \
+ void* __privateAssertion = (void*)(assertion); \
+ \
+ if (__privateAssertion) { \
+ dprintf(notrace, "Assertion \"!(%s [= %#08X])\" Failed", \
+ #assertion, __privateAssertion); \
+ { action } \
+ } \
+ } while (false)
+
+#elif DEBUGLEVEL == DEBUGFULL
+
+#define ncheck_action(assertion, action) \
+ do { \
+ void* __privateAssertion = (void*)(assertion); \
+ \
+ if (__privateAssertion) { \
+ dprintf(notrace, "Assertion \"!(%s [= %#08X])\" Failed\n" \
+ "File: %s\n" \
+ "Line: %d", \
+ #assertion, __privateAssertion, __FILE__, __LINE__); \
+ { action } \
+ } \
+ } while (false)
+
+#else
+
+#define ncheck_action(assertion, action)
+
+#endif
+
+/*<FF>*/
+/*********************************************************************
+
+MACRO
+ require(assertion, exception)
+
+DESCRIPTION
+ require will test assertion and if it fails:
+ break into the debugger if debugging is on.
+ goto exception.
+
+*********************************************************************/
+
+#if __DEBUGSMALL
+
+#define require(assertion, exception) \
+ do { \
+ if (assertion) ; \
+ else { \
+ __DebuggerBreak; \
+ goto exception; \
+ resumeLabel(exception); \
+ } \
+ } while (false)
+
+#elif DEBUGLEVEL == DEBUGON
+
+#define require(assertion, exception) \
+ do { \
+ if (assertion) ; \
+ else { \
+ dprintf(notrace, "Assertion \"%s\" Failed\n" \
+ "Exception \"%s\" Raised", \
+ #assertion, #exception); \
+ goto exception; \
+ resumeLabel(exception); \
+ } \
+ } while (false)
+
+#elif DEBUGLEVEL == DEBUGFULL
+
+#define require(assertion, exception) \
+ do { \
+ if (assertion) ; \
+ else { \
+ dprintf(notrace, "Assertion \"%s\" Failed\n" \
+ "Exception \"%s\" Raised\n" \
+ "File: %s\n" \
+ "Line: %d", \
+ #assertion, #exception, __FILE__, __LINE__); \
+ goto exception; \
+ resumeLabel(exception); \
+ } \
+ } while (false)
+
+#else
+
+#define require(assertion, exception) \
+ do { \
+ if (assertion) ; \
+ else { \
+ goto exception; \
+ resumeLabel(exception); \
+ } \
+ } while (false)
+
+#endif
+
+/*<FF>*/
+/*********************************************************************
+
+MACRO
+ nrequire(assertion, exception)
+
+DESCRIPTION
+ nrequire will test !assertion and if it fails:
+ break into the debugger if debugging is on.
+ goto exception.
+
+*********************************************************************/
+
+#if __DEBUGSMALL
+
+#define nrequire(assertion, exception) \
+ do { \
+ if (assertion) { \
+ DebugStr(); \
+ goto exception; \
+ resumeLabel(exception); \
+ } \
+ } while (false)
+
+#elif DEBUGLEVEL == DEBUGON
+
+#define nrequire(assertion, exception) \
+ do { \
+ void* __privateAssertion = (void*)(assertion); \
+ \
+ if (__privateAssertion) { \
+ dprintf(notrace, "Assertion \"!(%s [= %#08X])\" Failed\n" \
+ "Exception \"%s\" Raised", \
+ #assertion, __privateAssertion, #exception); \
+ goto exception; \
+ resumeLabel(exception); \
+ } \
+ } while (false)
+
+#elif DEBUGLEVEL == DEBUGFULL
+
+#define nrequire(assertion, exception) \
+ do { \
+ void* __privateAssertion = (void*)(assertion); \
+ \
+ if (__privateAssertion) { \
+ dprintf(notrace, "Assertion \"!(%s [= %#08X])\" Failed\n" \
+ "Exception \"%s\" Raised\n" \
+ "File: %s\n" \
+ "Line: %d", \
+ #assertion, __privateAssertion, #exception, __FILE__, \
+ __LINE__); \
+ goto exception; \
+ resumeLabel(exception); \
+ } \
+ } while (false)
+
+#else
+
+#define nrequire(assertion, exception) \
+ do { \
+ if (assertion) { \
+ goto exception; \
+ resumeLabel(exception); \
+ } \
+ } while (false)
+
+#endif
+
+/*<FF>*/
+/*********************************************************************
+
+MACRO
+ require_action(assertion, exception, action)
+
+DESCRIPTION
+ require_action will test assertion and if it fails:
+ break into the debugger if debugging is on.
+ execute action.
+ goto exception.
+
+*********************************************************************/
+
+#if __DEBUGSMALL
+
+#define require_action(assertion, exception, action) \
+ do { \
+ if (assertion) ; \
+ else { \
+ __DebuggerBreak; \
+ { action } \
+ goto exception; \
+ resumeLabel(exception); \
+ } \
+ } while (false)
+
+#elif DEBUGLEVEL == DEBUGON
+
+#define require_action(assertion, exception, action) \
+ do { \
+ if (assertion) ; \
+ else { \
+ dprintf(notrace, "Assertion \"%s\" Failed\n" \
+ "Exception \"%s\" Raised", \
+ #assertion, #exception); \
+ { action } \
+ goto exception; \
+ resumeLabel(exception); \
+ } \
+ } while (false)
+
+#elif DEBUGLEVEL == DEBUGFULL
+
+#define require_action(assertion, exception, action) \
+ do { \
+ if (assertion) ; \
+ else { \
+ dprintf(notrace, "Assertion \"%s\" Failed\n" \
+ "Exception \"%s\" Raised\n" \
+ "File: %s\n" \
+ "Line: %d", \
+ #assertion, #exception, __FILE__, __LINE__); \
+ { action } \
+ goto exception; \
+ resumeLabel(exception); \
+ } \
+ } while (false)
+
+#else
+
+#define require_action(assertion, exception, action) \
+ do { \
+ if (assertion) ; \
+ else { \
+ { action } \
+ goto exception; \
+ resumeLabel(exception); \
+ } \
+ } while (false)
+
+#endif
+
+/*<FF>*/
+/*********************************************************************
+
+MACRO
+ nrequire_action(assertion, exception, action)
+
+DESCRIPTION
+ nrequire_action will test !assertion and if it fails:
+ break into the debugger if debugging is on.
+ execute action.
+ goto exception.
+
+*********************************************************************/
+
+#if __DEBUGSMALL
+
+#define nrequire_action(assertion, exception, action) \
+ do { \
+ if (assertion) { \
+ __DebuggerBreak; \
+ { action } \
+ goto exception; \
+ resumeLabel(exception); \
+ } \
+ } while (false)
+
+#elif DEBUGLEVEL == DEBUGON
+
+#define nrequire_action(assertion, exception, action) \
+ do { \
+ void* __privateAssertion = (void*)(assertion); \
+ \
+ if (__privateAssertion) { \
+ dprintf(notrace, "Assertion \"!(%s [= %#08X])\" Failed\n" \
+ "Exception \"%s\" Raised", \
+ #assertion, __privateAssertion, #exception); \
+ { action } \
+ goto exception; \
+ resumeLabel(exception); \
+ } \
+ } while (false)
+
+#elif DEBUGLEVEL == DEBUGFULL
+
+#define nrequire_action(assertion, exception, action) \
+ do { \
+ void* __privateAssertion = (void*)(assertion); \
+ \
+ if (__privateAssertion) { \
+ dprintf(notrace, "Assertion \"!(%s [= %#08X])\" Failed\n" \
+ "Exception \"%s\" Raised\n" \
+ "File: %s\n" \
+ "Line: %d", \
+ #assertion, __privateAssertion, #exception, __FILE__, \
+ __LINE__); \
+ { action } \
+ goto exception; \
+ resumeLabel(exception); \
+ } \
+ } while (false)
+
+#else
+
+#define nrequire_action(assertion, exception, action) \
+ do { \
+ if (assertion) { \
+ { action } \
+ goto exception; \
+ resumeLabel(exception); \
+ } \
+ } while (false)
+
+#endif
+
+/*<FF>*/
+/*********************************************************************
+
+MACRO
+ resume(exception)
+
+DESCRIPTION
+ resume will resume execution after the n/require/_action statement
+ specified by exception. Resume lables must be on (the default) in
+ order to use resume. If an action form of require was used then the
+ action will not be re-executed.
+
+*********************************************************************/
+
+
+#define resume(exception) \
+ do { \
+ goto resume_ ## exception; \
+ } while (false)
+
+
+/*<FF>*/
+/********************************************************************/
+#endif
diff --git a/pr/src/md/mac/macdll.c b/pr/src/md/mac/macdll.c
new file mode 100644
index 00000000..1a06d59e
--- /dev/null
+++ b/pr/src/md/mac/macdll.c
@@ -0,0 +1,361 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#undef OLDROUTINENAMES
+#define OLDROUTINENAMES 1
+
+#include <Files.h>
+#include <Errors.h>
+#include <Folders.h>
+#include <CodeFragments.h>
+#include <Aliases.h>
+
+#include "MacErrorHandling.h"
+#include "primpl.h"
+#include "plstr.h"
+
+typedef struct CfrgItem CfrgItem, *CfrgItemPtr;
+struct CfrgItem
+{
+ OSType fArchType;
+ UInt32 fUpdateLevel;
+ UInt32 fCurVersion;
+ UInt32 fOldDefVersion;
+ UInt32 fAppStackSize;
+ UInt16 fAppSubFolder;
+ UInt8 fUsage;
+ UInt8 fLocation;
+ UInt32 fCodeOffset;
+ UInt32 fCodeLength;
+ UInt32 fReserved1;
+ UInt32 fReserved2;
+ UInt16 fItemSize; // %4 == 0
+ Str255 fName;
+ // Only make the final p-string as long as needed, then align to
+ // a longword boundary
+};
+
+typedef struct CfrgHeader CfrgHeader, *CfrgHeaderPtr, **CfrgHeaderHandle;
+struct CfrgHeader
+{
+ UInt32 fReserved1;
+ UInt32 fReserved2;
+ UInt32 fVersion;
+ UInt32 fReserved3;
+ UInt32 fReserved4;
+ UInt32 fFiller1;
+ UInt32 fFiller2;
+ UInt32 fItemCount;
+ CfrgItem fCfrgItemArray[1];
+};
+
+/*
+ turds used to iterate through the directories looking
+ for the desired library.
+*/
+
+struct GetSharedLibraryFilterProcData
+{
+ Boolean inRecursive;
+ StringPtr inName;
+
+ Boolean outFound;
+ CFragConnectionID outID;
+ Ptr outAddress;
+ OSErr outError;
+};
+typedef struct GetSharedLibraryFilterProcData GetSharedLibraryFilterProcData;
+
+static pascal void
+GetSharedLibraryFilterProc(const CInfoPBRec* const inCpb, Boolean* inWantQuit, void *inFilterData);
+
+
+/*
+ NSGetSharedLibrary
+
+ Unfortunately CFM doesn't support user specified loader paths,
+ so we emulate the behavior. Effectively this is a GetSharedLibrary
+ where the loader path is user defined.
+*/
+
+extern OSErr
+NSGetSharedLibrary(Str255 inLibName, CFragConnectionID* outID, Ptr* outMainAddr)
+{
+ char* curLibPath;
+ char* freeCurLibPath;
+ OSErr tempErr;
+ Boolean recursive;
+ FSSpec curFolder;
+ GetSharedLibraryFilterProcData filterData;
+ char *endCurLibPath;
+ Boolean done;
+
+ filterData.outFound = false;
+ filterData.outID = (CFragConnectionID)(-1);
+ filterData.outAddress = NULL;
+ filterData.inName = inLibName;
+
+ freeCurLibPath = curLibPath = strdup(PR_GetLibraryPath());
+
+ if (curLibPath == NULL)
+ return (fragLibNotFound);
+
+ tempErr = fragLibNotFound;
+
+ do
+ {
+ endCurLibPath = PL_strchr(curLibPath, PR_PATH_SEPARATOR);
+ done = (endCurLibPath == NULL);
+
+#if 0
+ // we overload the first character of a path if it's :
+ // then we want to recursively search that path
+ // see if path should be recursive
+ if (*curLibPath == ':')
+ {
+ // ':' is an illegal character in the name of a file
+ // if we start any path with this, we want to allow
+ // search recursively
+ curLibPath++;
+ recursive = true;
+ }
+ else
+#endif
+ {
+ recursive = false;
+ }
+
+ if (!done)
+ *endCurLibPath = '\0'; // NULL terminate the string
+
+ // convert to FSSpec
+ tempErr = ConvertUnixPathToFSSpec(curLibPath, &curFolder);
+
+ // now look in this directory
+ if (noErr == tempErr)
+ {
+ filterData.inRecursive = recursive;
+ FSpIterateDirectory(&curFolder, recursive ? 0 : 1, &GetSharedLibraryFilterProc, &filterData);
+
+ if (filterData.outFound)
+ {
+ *outID = filterData.outID;
+ *outMainAddr = filterData.outAddress;
+ tempErr = noErr;
+ break;
+ }
+ else
+ {
+ tempErr = fragLibNotFound;
+ }
+ }
+
+ curLibPath = endCurLibPath + 1; // skip to next path (past the '\0');
+ } while (!done);
+
+ free(freeCurLibPath);
+ return (tempErr);
+}
+
+
+static Boolean
+LibInPefContainer(const FSSpec* inSpec, StringPtr inName, UInt32* outCodeOffset, UInt32* outCodeLength);
+
+
+/*
+ GetSharedLibraryFilterProc
+
+ Callback to FSpIterateDirectory, finds a library with the name matching the
+ data in inFilterData (of type GetSharedLibraryFilterProcData). Forces a quit
+ when a match is found.
+*/
+
+static pascal void
+GetSharedLibraryFilterProc(const CInfoPBRec* const inCpb, Boolean* inWantQuit, void *inFilterData)
+{
+ GetSharedLibraryFilterProcData* pFilterData = (GetSharedLibraryFilterProcData*) inFilterData;
+
+ if ((inCpb->hFileInfo.ioFlAttrib & (1 << ioDirFlg)) == 0)
+ {
+ FSSpec fragSpec;
+ OSErr tempErr;
+ Str255 errName;
+ Boolean crap;
+ UInt32 codeOffset;
+ UInt32 codeLength;
+
+ // it's a file
+
+ // ¥ fix-me do we really want to allow all 'APPL's' for in which to find this library?
+ switch (inCpb->hFileInfo.ioFlFndrInfo.fdType)
+ {
+ case kCFragLibraryFileType:
+ case 'APPL':
+ tempErr = FSMakeFSSpec(inCpb->hFileInfo.ioVRefNum, inCpb->hFileInfo.ioFlParID, inCpb->hFileInfo.ioNamePtr, &fragSpec);
+
+ // this shouldn't fail
+ if (noErr != tempErr)
+ {
+ return;
+ }
+
+ // resolve an alias if this was one
+ tempErr = ResolveAliasFile(&fragSpec, true, &crap, &crap);
+
+ // if got here we have a shlb (or app-like shlb)
+ if (noErr != tempErr)
+ {
+ // probably couldn't resolve an alias
+ return;
+ }
+
+ break;
+ default:
+ return;
+ }
+
+ // see if this symbol is in this fragment
+ if (LibInPefContainer(&fragSpec, pFilterData->inName, &codeOffset, &codeLength))
+ tempErr = GetDiskFragment(&fragSpec, codeOffset, codeLength, pFilterData->inName, kLoadLib, &pFilterData->outID, &pFilterData->outAddress, errName);
+ else
+ return;
+
+ // stop if we found a library by that name
+ if (noErr == tempErr)
+ {
+ *inWantQuit = true;
+ pFilterData->outFound = true;
+ pFilterData->outError = tempErr;
+ }
+ }
+ // FSpIterateDirectory will automagically call us for subsequent sub-dirs if necessary
+}
+
+
+/*
+ LibInPefContainer
+
+ Tell whether library inName is contained it the file pointed to by inSpec.
+ Return the codeOffset and codeLength information, for a subsequent
+ call to GetDiskFragment.
+*/
+
+static Boolean
+LibInPefContainer(const FSSpec* inSpec, StringPtr inName, UInt32* outCodeOffset, UInt32* outCodeLength)
+{
+ short refNum;
+ CfrgHeaderHandle hCfrg;
+ CfrgItem* pCurItem;
+ UInt32 curLibIndex;
+ Boolean found;
+
+ // asume we didn't find it
+ found = false;
+
+ // open the resource fork, if we can't bail
+ refNum = FSpOpenResFile(inSpec, fsRdPerm);
+ require(-1 != refNum, Exit);
+
+ // grab out the alias record, if it's not there bail
+ hCfrg = (CfrgHeaderHandle) Get1Resource(kCFragResourceType, kCFragResourceID);
+ require(NULL != hCfrg, CloseResourceAndExit);
+
+ HLock((Handle)hCfrg);
+
+ // get ptr to first item
+ pCurItem = &(*hCfrg)->fCfrgItemArray[0];
+ for (curLibIndex = 0; curLibIndex < (*hCfrg)->fItemCount; curLibIndex++)
+ {
+ // is this our library?
+ if ((pCurItem->fName[0] == inName[0]) &&
+ (strncmp((char*) inName + 1, (char*) pCurItem->fName + 1, PR_MIN(pCurItem->fName[0], inName[0])) == 0))
+ {
+ *outCodeOffset = pCurItem->fCodeOffset;
+ *outCodeLength = pCurItem->fCodeLength;
+ found = true;
+ }
+
+ // skip to next one
+ pCurItem = (CfrgItem*) ((char*) pCurItem + pCurItem->fItemSize);
+ }
+
+ HUnlock((Handle)hCfrg);
+
+CloseResourceAndExit:
+ CloseResFile(refNum);
+Exit:
+ return (found);
+
+}
+
+
+/*
+ NSFindSymbol
+
+ Workaround bug in CFM FindSymbol (in at least 7.5.5) where symbols with lengths
+ greater than 63 chars cause a "paramErr". We iterate through all symbols
+ in the library to find the desired symbol.
+*/
+
+extern OSErr
+NSFindSymbol(CFragConnectionID inID, Str255 inSymName, Ptr* outMainAddr, SymClass *outSymClass)
+{
+ OSErr err;
+
+ if (inSymName[0] > 63)
+ {
+ /*
+ if there are greater than 63 characters in the
+ name, CFM FindSymbol fails, so let's iterate through all
+ of the symbols in the fragment and grab it
+ that way.
+ */
+ long symbolCount;
+ Str255 curSymName;
+ long curIndex;
+ Boolean found;
+
+ found = false;
+ err = CountSymbols(inID, &symbolCount);
+ if (noErr == err)
+ {
+ /* now iterate through all the symbols in the library */
+ /* per DTS the indices apparently go 0 to n-1 */
+ for (curIndex = 0; (curIndex <= symbolCount - 1 && !found); curIndex++)
+ {
+ err = GetIndSymbol(inID, curIndex, curSymName, outMainAddr, outSymClass);
+ if (noErr == err && curSymName[0] == inSymName[0] && !strncmp((char*)curSymName + 1, (char*)inSymName + 1, curSymName[0]))
+ {
+ /* found our symbol */
+ found = true;
+ }
+ }
+
+ /* if we didn't find it set the error code so below it won't take this symbol */
+ if (!found)
+ err = fragSymbolNotFound;
+ }
+ }
+ else
+ {
+ err = FindSymbol(inID, inSymName, outMainAddr, outSymClass);
+ }
+
+ return (err);
+}
+
diff --git a/pr/src/md/mac/macio.c b/pr/src/md/mac/macio.c
new file mode 100644
index 00000000..050d2529
--- /dev/null
+++ b/pr/src/md/mac/macio.c
@@ -0,0 +1,1897 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include <Types.h>
+#include <Files.h>
+#include <Folders.h>
+#include <Errors.h>
+
+#include <fcntl.h>
+
+#include "primpl.h"
+#include "MacErrorHandling.h"
+
+/* forward declarations */
+OSErr ConvertUnixPathToMacPath(const char *, char **);
+OSErr ConvertUnixPathToFSSpec(const char *unixPath, FSSpec *fileSpec);
+extern unsigned long gJanuaryFirst1970Seconds;
+
+extern void WaitOnThisThread(PRThread *thread, PRIntervalTime timeout);
+extern void DoneWaitingOnThisThread(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;
+
+ if (_PR_MD_GET_INTSOFF()) {
+ cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
+ thread->md.notifyPending = PR_TRUE;
+ return;
+ }
+ _PR_SET_INTSOFF(1);
+
+ thread->md.osErrCode = noErr;
+ DoneWaitingOnThisThread(thread);
+
+ _PR_SET_INTSOFF(0);
+
+}
+
+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.notifyPending) {
+ thread->md.notifyPending = PR_FALSE;
+ DoneWaitingOnThisThread(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.notifyPending) {
+ thread->md.notifyPending = PR_FALSE;
+ DoneWaitingOnThisThread(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();
+#if 0
+ /* 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 */
+ FILE* systemFP = NULL;
+ extern int errno;
+
+ switch (refNum)
+ {
+ case 0:
+ systemFP = stdin;
+ break;
+ case 1:
+ systemFP = stdout;
+ break;
+ case 2:
+ systemFP = stderr;
+ break;
+ }
+
+ if (systemFP)
+ {
+ size_t bytesDone;
+
+ if (op == READ_ASYNC)
+ bytesDone = fread(buf, 1, bytes, systemFP);
+ else
+ bytesDone = fwrite(buf, 1, bytes, systemFP);
+
+ if (errno)
+ {
+ err = errno;
+ goto ErrorExit;
+ }
+ else
+ return (bytesDone);
+ }
+ else
+#else
+ 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;
+ }
+#endif
+ {
+ /* grab the thread so we know which one to post to at completion */
+ pbAsync.thread = me;
+
+ pbAsync.pb.ioParam.ioCompletion = NewIOCompletionProc((ProcPtr)&AsyncIOCompletion);
+ 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.
+ ** Don't compute error code from async call. Bug in OS returns a garbage value.
+ */
+ me->io_pending = PR_TRUE;
+ me->io_fd = refNum;
+ me->md.osErrCode = noErr;
+ if (op == READ_ASYNC)
+ (void) PBReadAsync(&pbAsync);
+ else
+ (void) PBWriteAsync(&pbAsync);
+
+ 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;
+ else
+ 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 oflag, int mode)
+{
+// Macintosh doesn¹t really have mode bits, just drop them
+#pragma unused (mode)
+
+ OSErr err;
+ ParamBlockRec pb;
+ char *macFileName = NULL;
+ Str255 pascalName;
+ PRInt8 perm;
+ PRInt32 flags = oflag;
+
+ if (flags & PR_RDWR) {
+ oflag = O_RDWR;
+ } else if (flags & PR_WRONLY) {
+ oflag = O_WRONLY;
+ } else {
+ oflag = O_RDONLY;
+ }
+
+ if (flags & PR_CREATE_FILE) {
+ oflag |= O_CREAT ;
+ }
+
+ err = ConvertUnixPathToMacPath(path, &macFileName);
+
+ if (err != noErr)
+ goto ErrorExit;
+
+ pb.ioParam.ioCompletion = NULL;
+ PStrFromCStr(macFileName, pascalName);
+ PR_DELETE(macFileName);
+ pb.ioParam.ioNamePtr = pascalName;
+ pb.ioParam.ioVRefNum = 0;
+ pb.ioParam.ioVersNum = 0;
+
+open:
+ perm = oflag & 3;
+ if (perm == O_RDWR)
+ perm = fsRdWrPerm;
+ else if (perm == O_WRONLY)
+ perm = fsWrPerm;
+ else
+ perm = fsRdPerm;
+ pb.ioParam.ioPermssn = perm;
+
+ pb.ioParam.ioMisc = NULL;
+
+ err = PBOpenSync(&pb);
+ if (err == noErr)
+ return pb.ioParam.ioRefNum;
+ else if ((err != fnfErr) || ((oflag & O_CREAT) == 0))
+ goto ErrorExit;
+
+ err = PBCreateSync(&pb);
+ if (err == noErr)
+ goto open;
+
+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 */
+
+PRInt32 _MD_LSeek(PRFileDesc *fd, PRInt32 offset, int how)
+{
+ PRInt32 refNum = fd->secret->md.osfd;
+ OSErr err = noErr;
+ long curPos, endPos;
+
+ PR_ASSERT(offset >= 0);
+
+ /* 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 */
+ if (err == noErr) {
+ err = SetFPos(refNum, fsFromStart, endPos);
+ if (err == eofErr) {
+ err = SetEOF(refNum, 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.
+
+ char *src;
+ char *tgt;
+ OSErr err = noErr;
+
+ *macPath = malloc(strlen(unixPath) * 2); // Will be enough extra space.
+ require_action (*macPath != NULL, exit, err = memFullErr;);
+
+ strcpy(*macPath, ""); // Clear the Mac path
+
+ (const char *)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;
+}
+
+
+#include <Processes.h>
+
+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;
+ char *temp;
+ 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, "/bin", strlen("/bin")) == 0) {
+ dprintf("Unable to translate Unix file path %s to Mac path\n", unixPath);
+ err = -1;
+ goto Exit_ConvertUnixPathToMacPath;
+ }
+
+ else if (strncmp(unixPath, "/dev", strlen("/dev")) == 0) {
+ dprintf("Unable to translate Unix file path %s to Mac path\n", unixPath);
+ err = -1;
+ goto Exit_ConvertUnixPathToMacPath;
+ }
+
+ else if (strncmp(unixPath, "/etc", strlen("/etc")) == 0) {
+ dprintf("Unable to translate Unix file path %s to Mac path\n", unixPath);
+ err = -1;
+ goto Exit_ConvertUnixPathToMacPath;
+ }
+
+ 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.
+
+ char *src;
+ char *tgt;
+ OSErr err = noErr;
+
+ *macPath = malloc(strlen(unixPath) * 2); // Will be enough extra space.
+ require_action (*macPath != NULL, exit, err = memFullErr;);
+
+ strcpy(*macPath, ""); // Clear the Mac path
+
+ (const char *)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, "/bin", strlen("/bin")) == 0) {
+ dprintf("Unable to translate Unix file path %s to Mac path\n", unixPath);
+ err = -1;
+ goto Exit_ConvertUnixPathToMacPath;
+ }
+
+ else if (strncmp(unixPath, "/dev", strlen("/dev")) == 0) {
+ dprintf("Unable to translate Unix file path %s to Mac path\n", unixPath);
+ err = -1;
+ goto Exit_ConvertUnixPathToMacPath;
+ }
+
+ else if (strncmp(unixPath, "/etc", strlen("/etc")) == 0) {
+ dprintf("Unable to translate Unix file path %s to Mac path\n", unixPath);
+ err = -1;
+ goto Exit_ConvertUnixPathToMacPath;
+ }
+
+ 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_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+void * _MD_MemMap(
+ PRFileMap *fmap,
+ PRInt64 offset,
+ PRUint32 len)
+{
+#pragma unused (fmap, offset, len)
+
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+}
+
+PRStatus _MD_MemUnmap(void *addr, PRUint32 len)
+{
+#pragma unused (addr, len)
+
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRStatus _MD_CloseFileMap(PRFileMap *fmap)
+{
+#pragma unused (fmap)
+
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
diff --git a/pr/src/md/mac/macsockclient.c b/pr/src/md/mac/macsockclient.c
new file mode 100644
index 00000000..3ae9b0e5
--- /dev/null
+++ b/pr/src/md/mac/macsockclient.c
@@ -0,0 +1,558 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include <errno.h>
+
+#define OTUNIXERRORS 1 /* We want OpenTransport error codes */
+#include <OpenTransport.h>
+#include <OpenTptInternet.h> // All the internet typedefs
+
+#include "macsock.h" /* from macsock library */
+#include "primpl.h"
+
+void _MD_InitNetAccess()
+{
+}
+
+static void macsock_map_error(int err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case EADDRNOTAVAIL:
+ PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err);
+ break;
+ case EINPROGRESS:
+ PR_SetError(PR_IN_PROGRESS_ERROR, err);
+ break;
+ case EWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case EAFNOSUPPORT:
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err);
+ break;
+ case ETIMEDOUT:
+ PR_SetError(PR_IO_TIMEOUT_ERROR, err);
+ break;
+ case ECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case ENETUNREACH:
+ PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, err);
+ break;
+ case EADDRINUSE:
+ PR_SetError(PR_ADDRESS_IN_USE_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ /*
+ * UNIX domain sockets are not supported in NSPR
+ */
+ case EACCES:
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err);
+ break;
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
+ break;
+ case EINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case EIO:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ENOENT:
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err);
+ break;
+ case ENXIO:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case EPROTOTYPE:
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+// Errors returned:
+// ENETDOWN - no MacTCP driver
+// EPROTONOSUPPORT - bad socket type/protocol
+// ENOBUFS - not enough space for another socket, or failure in socket creation routine
+PRInt32 _MD_socket(int domain, int type, int protocol)
+{
+ int err;
+
+ err = macsock_socket(domain, type, protocol);
+ if (err == -1) {
+ macsock_map_error(errno);
+ return -1;
+ }
+
+ return err;
+}
+
+// Errors:
+// EBADF -- bad socket id
+// EFAULT -- bad address format
+PRInt32 _MD_bind(PRFileDesc *fd, PRNetAddr *addr, PRUint32 addrlen)
+{
+ int err;
+ int sID = fd->secret->md.osfd;
+
+ err = macsock_bind(sID, (const struct sockaddr *)addr, addrlen);
+ if (err == -1) {
+ macsock_map_error(errno);
+ return -1;
+ }
+
+ return err;
+}
+
+// Errors:
+// EBADF -- bad socket id
+// EOPNOTSUPP -- socket is already connected, and closing
+// EISCONN -- already connected
+// EINPROGRESS -- connecting right now
+PRInt32 _MD_listen(PRFileDesc *fd, PRIntn backlog)
+{
+ int err;
+ int sID = fd->secret->md.osfd;
+
+ err = macsock_listen(sID, backlog);
+ if (err == -1) {
+ macsock_map_error(errno);
+ return -1;
+ }
+
+ return err;
+}
+
+// Errors:
+// EBADF -- bad socket id
+PRInt32 _MD_getsockname(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen)
+{
+ int err;
+ int sID = fd->secret->md.osfd;
+
+ err = macsock_getsockname(sID, (struct sockaddr *)addr, (int *)addrlen);
+ if (err == -1) {
+ macsock_map_error(errno);
+ return -1;
+ }
+
+ return err;
+}
+
+// Errors:
+// EBADF - bad socket id
+// ENOPROTOOPT - The option is unknown
+PRStatus _MD_getsockopt(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen)
+{
+ int err;
+ int sID = fd->secret->md.osfd;
+
+ err = macsock_getsockopt(sID, level, optname, optval, optlen);
+ if (err == -1) {
+ macsock_map_error(errno);
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+}
+
+// Errors:
+// EBADF - bad socket id
+// ENOTCONN - socket hasnÕt been properly created
+PRStatus _MD_setsockopt(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen)
+{
+ int err;
+ int sID = fd->secret->md.osfd;
+
+ err = macsock_setsockopt(sID, level, optname, optval, optlen);
+ if (err == -1) {
+ macsock_map_error(errno);
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+}
+
+PRInt32 _MD_socketavailable(PRFileDesc *fd, size_t *bytes)
+{
+ int err;
+ int sID = fd->secret->md.osfd;
+
+ // Careful of the return value here. 0 => failure, 1 => success
+ err = macsock_socketavailable(sID, bytes);
+ if (err == 0) {
+ _PR_MD_CURRENT_THREAD()->md.osErrCode = -1;
+ return -1;
+ }
+
+ return 0;
+}
+
+PRInt32 _MD_accept(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
+{
+#pragma unused (timeout)
+
+ int err;
+ int sID = fd->secret->md.osfd;
+
+ err = macsock_accept(sID, (struct sockaddr *)addr, (int *)addrlen);
+ if (err == -1) {
+ macsock_map_error(errno);
+ return -1;
+ }
+
+ return err;
+}
+
+// Connect
+// check the arguments validity
+// issue the connect call to the stream
+// Errors:
+// EBADF -- bad socket id, bad MacTCP stream
+// EAFNOSUPPORT -- bad address format
+// EADDRINUSE -- we are listening, or duplicate socket
+// EINPROGRESS -- we are connecting right now
+// EISCONN -- already connected
+// ECONNREFUSED -- other side has closed, or open has failed
+// EALREADY -- we are connected
+// EINTR -- user interrupted
+PRInt32 _MD_connect(PRFileDesc *fd, PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
+{
+#pragma unused (timeout)
+
+ int err;
+ int sID = fd->secret->md.osfd;
+
+ err = macsock_connect(sID, (struct sockaddr *)addr, addrlen);
+ if (err == -1) {
+ macsock_map_error(errno);
+ return -1;
+ }
+
+ return err;
+}
+
+// Errors:
+// EBADF - bad socket ID
+// ENOTCONN - no such connection
+PRInt32 _MD_recv(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, PRIntervalTime timeout)
+{
+#pragma unused (timeout)
+
+ int err;
+ int sID = fd->secret->md.osfd;
+
+ err = macsock_recv(sID, buf, amount, flags);
+ if (err == -1) {
+ macsock_map_error(errno);
+ return -1;
+ }
+
+ return err;
+}
+
+PRInt32 _MD_send(PRFileDesc *fd,const void *buf, PRInt32 amount, PRIntn flags, PRIntervalTime timeout)
+{
+#pragma unused (timeout)
+
+ int err;
+ int sID = fd->secret->md.osfd;
+
+ err = macsock_send(sID, buf, amount, flags);
+ if (err == -1) {
+ macsock_map_error(errno);
+ return -1;
+ }
+
+ return err;
+}
+
+PRInt32 _MD_recvfrom(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRNetAddr *addr, PRUint32 *addrlen,
+ PRIntervalTime timeout)
+{
+#pragma unused (timeout)
+
+ int err;
+ int sID = fd->secret->md.osfd;
+
+ err = macsock_recvfrom(sID, buf, amount, flags, (struct sockaddr *)addr, (int *)addrlen);
+ if (err == -1) {
+ macsock_map_error(errno);
+ return -1;
+ }
+
+ return err;
+}
+
+
+PRInt32 _MD_sendto(PRFileDesc *fd,const void *buf, PRInt32 amount,
+ PRIntn flags, PRNetAddr *addr, PRUint32 addrlen,
+ PRIntervalTime timeout)
+{
+#pragma unused (timeout)
+
+ int err;
+ int sID = fd->secret->md.osfd;
+
+ err = macsock_sendto(sID, buf, amount, flags, (struct sockaddr *)addr, addrlen);
+ if (err == -1) {
+ macsock_map_error(errno);
+ return -1;
+ }
+
+ return err;
+}
+
+// Errors:
+// EBADF -- bad socket id
+PRInt32 _MD_closesocket(PRInt32 osfd)
+{
+ int err;
+ int sID = osfd;
+
+ err = macsock_close(sID);
+ if (err == -1) {
+ macsock_map_error(errno);
+ return -1;
+ }
+
+ return err;
+}
+
+PRInt32 _MD_writev(PRFileDesc *fd, struct PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout)
+{
+#pragma unused (fd, iov, iov_size, timeout)
+
+ PR_ASSERT(0);
+ _PR_MD_CURRENT_THREAD()->md.osErrCode = unimpErr;
+ return -1;
+}
+
+PRInt32 _MD_shutdown(PRFileDesc *fd, PRIntn how)
+{
+ int err;
+ int sID = fd->secret->md.osfd;
+
+ err = macsock_shutdown(sID, how);
+ if (err == -1) {
+ macsock_map_error(errno);
+ return -1;
+ }
+
+ return err;
+}
+
+PRStatus _MD_getpeername(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen)
+{
+ int err;
+ int sID = fd->secret->md.osfd;
+
+ err = macsock_getpeername(sID, (struct sockaddr *)addr, (int *)addrlen);
+ if (err == -1) {
+ macsock_map_error(errno);
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+}
+
+void _MD_makenonblock(PRFileDesc *fd)
+{
+ int err;
+ int sID = fd->secret->md.osfd;
+ int optval =1;
+
+ err = macsock_setsockopt(sID, SOL_SOCKET, FIONBIO, (const void *)&optval, 0);
+ if (err == -1) {
+ macsock_map_error(errno);
+ }
+}
+
+struct hostent *gethostbyname(const char * name)
+{
+ return macsock_gethostbyname((char *)name);
+}
+
+struct hostent *gethostbyaddr(const void *addr, int addrlen, int type)
+{
+ return macsock_gethostbyaddr(addr, addrlen, type);
+}
+
+PRStatus _MD_gethostname(char *name, int namelen)
+{
+ int err;
+
+ err = macsock_gethostname(name, namelen);
+ if (err != noErr) {
+ macsock_map_error(err);
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+}
+
+#define kIPName "ip"
+static struct protoent sIPProto = {kIPName, NULL, INET_IP};
+static struct protoent sTCPProto = {kTCPName, NULL, INET_TCP};
+static struct protoent sUDPProto = {kUDPName, NULL, INET_UDP};
+
+struct protoent *getprotobyname(const char * name)
+{
+ if (strcmp(name, kIPName) == 0)
+ return (&sIPProto);
+
+ if (strcmp(name, kTCPName) == 0)
+ return (&sTCPProto);
+
+ if (strcmp(name, kUDPName) == 0)
+ return (&sUDPProto);
+
+ErrorExit:
+ _PR_MD_CURRENT_THREAD()->md.osErrCode = paramErr;
+ return NULL;
+}
+
+
+struct protoent *getprotobynumber(int number)
+{
+ if (number == INET_IP)
+ return (&sIPProto);
+
+ if (number == INET_TCP)
+ return (&sTCPProto);
+
+ if (number == INET_UDP)
+ return (&sUDPProto);
+
+ErrorExit:
+ _PR_MD_CURRENT_THREAD()->md.osErrCode = paramErr;
+ return NULL;
+}
+
+PRInt32 _MD_poll(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
+{
+ PRPollDesc *pd, *epd;
+ PRInt32 n;
+
+ fd_set rd, wt, ex;
+ struct timeval tv, *tvp = NULL;
+ int maxfd = -1;
+
+ FD_ZERO(&rd);
+ FD_ZERO(&wt);
+ FD_ZERO(&ex);
+
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ PRInt32 osfd;
+ PRInt16 in_flags = pd->in_flags;
+ PRFileDesc *bottom = pd->fd;
+
+ if (NULL == bottom) {
+ continue;
+ }
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ osfd = bottom->secret->md.osfd;
+
+ if (osfd > maxfd) {
+ maxfd = osfd;
+ }
+ if (in_flags & PR_POLL_READ) {
+ FD_SET(osfd, &rd);
+ }
+ if (in_flags & PR_POLL_WRITE) {
+ FD_SET(osfd, &wt);
+ }
+ if (in_flags & PR_POLL_EXCEPT) {
+ FD_SET(osfd, &ex);
+ }
+ }
+ if (timeout != PR_INTERVAL_NO_TIMEOUT) {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(timeout) % PR_USEC_PER_SEC;
+ tvp = &tv;
+ }
+
+ n = select(maxfd + 1, &rd, &wt, &ex, tvp);
+
+ if (n > 0) {
+ n = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ PRInt32 osfd;
+ PRInt16 in_flags = pd->in_flags;
+ PRInt16 out_flags = 0;
+ PRFileDesc *bottom = pd->fd;
+
+ if (NULL == bottom) {
+ continue;
+ }
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ osfd = bottom->secret->md.osfd;
+
+ if ((in_flags & PR_POLL_READ) && FD_ISSET(osfd, &rd)) {
+ out_flags |= PR_POLL_READ;
+ }
+ if ((in_flags & PR_POLL_WRITE) && FD_ISSET(osfd, &wt)) {
+ out_flags |= PR_POLL_WRITE;
+ }
+ if ((in_flags & PR_POLL_EXCEPT) && FD_ISSET(osfd, &ex)) {
+ out_flags |= PR_POLL_EXCEPT;
+ }
+ pd->out_flags = out_flags;
+ if (out_flags) {
+ n++;
+ }
+ }
+ /*
+ Can't do this assert because MacSock returns write fds even if we did not
+ set it in the original write fd set.
+ */
+ /*PR_ASSERT(n > 0);*/
+ } else
+ PR_ASSERT(n == 0);
+
+ return n;
+}
+
+int _MD_mac_get_nonblocking_connect_error(int osfd)
+{
+ int err;
+
+ err = macsock_getconnectstatus (osfd);
+ if (err == -1) {
+ macsock_map_error(errno);
+ return -1;
+ }
+
+ return err;
+}
+
diff --git a/pr/src/md/mac/macsocket.h b/pr/src/md/mac/macsocket.h
new file mode 100644
index 00000000..4bc9ed66
--- /dev/null
+++ b/pr/src/md/mac/macsocket.h
@@ -0,0 +1,219 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#ifndef macksocket_h___
+#define macksocket_h___
+
+// macsock.h
+// Interface visible to xp code
+// C socket type definitions and routines
+// from sys/socket.h
+#include <OpenTptInternet.h> // All the internet typedefs
+#include <utime.h> // For timeval
+/*
+ * sleep and delay conflict with the same in unistd.h from Metrowerks. OT
+ * defines them as
+ *
+ * extern pascal void OTDelay(UInt32 seconds);
+ * extern pascal void OTIdle(void);
+ *
+ * #define sleep(x) OTDelay(x)
+ * #define delay(x) OTDelay(x)
+ */
+
+#undef sleep
+#undef delay
+
+#pragma once
+
+#include "prio.h"
+
+struct sockaddr {
+ unsigned char sa_len; /* total length */
+ unsigned char sa_family; /* address family */
+ char sa_data[14]; /* actually longer; address value */
+};
+
+// from netinet/in.h
+struct in_addr {
+ unsigned long s_addr;
+};
+
+struct sockaddr_in {
+ unsigned char sin_len;
+ unsigned char sin_family; // AF_INET
+ unsigned short sin_port;
+ struct in_addr sin_addr;
+ char sin_zero[8];
+};
+
+struct hostent {
+ char *h_name; /* official name of host */
+ char **h_aliases; /* alias list */
+ int h_addrtype; /* host address type */
+ int h_length; /* length of address */
+ char **h_addr_list; /* list of addresses from name server */
+#define h_addr h_addr_list[0] /* address, for backward compatiblity */
+};
+
+// Necessary network defines, found by grepping unix headers when XP code would not compile
+#define FIONBIO 1
+#define SOCK_STREAM 1
+#define SOCK_DGRAM 2
+#define IPPROTO_TCP INET_TCP // Default TCP protocol
+#define IPPROTO_UDP INET_UDP // Default UDP protocol
+#define INADDR_ANY kOTAnyInetAddress
+#define SOL_SOCKET XTI_GENERIC // Any type of socket
+#define SO_REUSEADDR IP_REUSEADDR
+#define MSG_PEEK 0x2 // Just look at a message waiting, donÕt actually read it.
+
+typedef unsigned long u_long;
+
+/* ldap.h has its own definition of fd_set */
+/* select support */
+#if !defined(FD_SET)
+#define NBBY 8
+typedef long fd_mask;
+#define NFDBITS (sizeof(fd_mask) * NBBY) /* bits per mask */
+
+#ifndef howmany
+#define howmany(x, y) (((x)+((y)-1))/(y))
+#endif
+#define FD_SETSIZE 64
+typedef struct fd_set{
+ fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)];
+} fd_set;
+
+#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
+#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
+#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
+#define FD_ZERO(p) memset (p, 0, sizeof(*(p)))
+#endif /* !FD_SET */
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern unsigned long inet_addr(const char *cp);
+extern char *inet_ntoa(struct in_addr in);
+
+inline unsigned long htonl(unsigned long hostlong) {return hostlong;}
+inline unsigned long ntohl(unsigned long netlong) {return netlong;}
+inline unsigned short ntohs(unsigned short netshort) {return netshort;}
+inline unsigned short htons(unsigned short hostshort) {return hostshort;}
+
+
+// UNIX look-alike routines
+// They make sure that the arguments passed in are valid, and then
+//
+extern struct hostent *macsock_gethostbyaddr(const void *addr, int addrlen, int type);
+
+extern int macsock_socket(int domain, int type, int protocol);
+extern int macsock_ioctl(int sID, unsigned int request, void *value);
+extern int macsock_connect(int sID, struct sockaddr *name, int namelen);
+extern int macsock_write(int sID, const void *buffer, unsigned buflen);
+extern int macsock_read(int sID, void *buf, unsigned nbyte);
+extern int macsock_close(int sID);
+
+extern int macsock_accept(int sID, struct sockaddr *addr, int *addrlen);
+extern int macsock_bind(int sID, const struct sockaddr *name, int namelen);
+extern int macsock_listen(int sID, int backlog);
+
+extern int macsock_shutdown(int sID, int how);
+extern int macsock_getpeername(int sID, struct sockaddr *name, int *namelen);
+extern int macsock_getsockname(int sID, struct sockaddr *name, int *namelen);
+extern int macsock_getsockopt(int sID, int level, int optname, void *optval,int *optlen);
+extern int macsock_setsockopt(int sID, int level, int optname, const void *optval,int optlen);
+extern int macsock_socketavailable(int sID, size_t *bytesAvailable);
+extern int macsock_dup(int sID);
+
+extern int macsock_send(int sID, const void *msg, int len, int flags);
+extern int macsock_sendto(int sID, const void *msg, int len, int flags, struct sockaddr *toAddr, int toLen);
+extern int macsock_recvfrom(int sID, void *buf, int len, int flags, struct sockaddr *from, int *fromLen);
+extern int macsock_recv(int sID, void *buf, int len, int flags);
+
+extern int select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
+
+
+#define macsock_gethostbyaddr PR_GetHostByAddr
+#define macsock_socket PR_Socket
+#define macsock_connect PR_Connect
+#define macsock_write PR_Write
+#define macsock_read PR_Read
+#define macsock_close PR_Close
+#define macsock_accept PR_Accept
+#define macsock_bind PR_Bind
+#define macsock_listen PR_Listen
+#define macsock_shutdown PR_Shutdown
+#define macsock_getpeername PR_GetPeerName
+#define macsock_getsockname PR_GetSockName
+#define macsock_getsockopt PR_GetSockOpt
+#define macsock_setsockopt PR_SetSockOpt
+#define macsock_socketavailable PR_SocketAvailable
+#define macsock_send PR_Send
+#define macsock_sendto PR_SendTo
+#define macsock_recvfrom PR_RecvFrom
+#define macsock_recv PR_Recv
+
+#ifdef __cplusplus
+}
+#endif
+//extern int errno;
+
+/*
+macsock_sendmsg
+macsock_readv
+macsock_writev
+*/
+
+/* New definitions that are not defined in macsock.h in macsock library */
+struct protoent {
+ char *p_name; /* official protocol name */
+ char **p_aliases; /* alias list */
+ int p_proto; /* protocol # */
+};
+
+extern struct protoent *getprotobyname(const char * name);
+extern struct protoent *getprotobynumber(int number);
+
+extern int gethostname (char *name, int namelen);
+extern struct hostent *gethostbyname(const char * name);
+extern struct hostent *gethostbyaddr(const void *addr, int addrlen, int type);
+
+#define INADDR_LOOPBACK 0x7F000001
+
+#define SO_KEEPALIVE TCP_KEEPALIVE
+#define SO_RCVBUF XTI_RCVBUF
+#define SO_SNDBUF XTI_SNDBUF
+#define SO_LINGER XTI_LINGER /* linger on close if data present */
+
+#define IPPROTO_IP INET_IP
+
+/* Get/Set sock opt until fixed in NSPR 2.0 */
+struct linger {
+ int l_onoff; /* option on/off */
+ int l_linger; /* linger time */
+};
+
+struct ip_mreq {
+ struct in_addr imr_multiaddr; /* IP multicast address of group */
+ struct in_addr imr_interface; /* local IP address of interface */
+};
+
+#endif /* macksocket_h___ */
diff --git a/pr/src/md/mac/macsockotpt.c b/pr/src/md/mac/macsockotpt.c
new file mode 100644
index 00000000..4c74bb8d
--- /dev/null
+++ b/pr/src/md/mac/macsockotpt.c
@@ -0,0 +1,1542 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/* This turns on UNIX style errors in OT 1.1 headers */
+#define OTUNIXERRORS 1
+
+#include <Gestalt.h>
+
+/*
+ Since Apple put out new headers without
+ putting in a way to test for them, we found some random symbol which
+ isn't defined in the "1.1" headers.
+*/
+#include <OpenTransport.h>
+#ifdef kOTInvalidStreamRef
+/* old */
+#define GESTALT_OPEN_TPT_PRESENT gestaltOpenTptPresent
+#define GESTALT_OPEN_TPT_TCP_PRESENT gestaltOpenTptTCPPresent
+#else
+/* new */
+#define GESTALT_OPEN_TPT_PRESENT gestaltOpenTptPresentMask
+#define GESTALT_OPEN_TPT_TCP_PRESENT gestaltOpenTptTCPPresentMask
+#endif
+
+#include <OpenTptInternet.h> // All the internet typedefs
+#include "macsocket.h"
+#include "primpl.h"
+
+typedef enum SndRcvOpCode {
+ kSTREAM_SEND,
+ kSTREAM_RECEIVE ,
+ kDGRAM_SEND,
+ kDGRAM_RECEIVE
+} SndRcvOpCode;
+
+
+static InetSvcRef sSvcRef;
+
+static pascal void NotifierRoutine(void * contextPtr, OTEventCode code,
+ OTResult result, void * cookie);
+
+static PRBool GetState(EndpointRef endpoint, PRBool *readReady, PRBool *writeReady, PRBool *exceptReady);
+
+extern void WaitOnThisThread(PRThread *thread, PRIntervalTime timeout);
+extern void DoneWaitingOnThisThread(PRThread *thread);
+
+void _MD_InitNetAccess()
+{
+ OSErr err;
+ OSStatus errOT;
+ PRBool hasOTTCPIP = PR_FALSE;
+ PRBool hasOT = PR_FALSE;
+ long gestaltResult;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ err = Gestalt(gestaltOpenTpt, &gestaltResult);
+ if (err == noErr)
+ if (gestaltResult & GESTALT_OPEN_TPT_PRESENT)
+ hasOT = PR_TRUE;
+
+ if (hasOT)
+ if (gestaltResult & GESTALT_OPEN_TPT_TCP_PRESENT)
+ hasOTTCPIP = PR_TRUE;
+
+ PR_ASSERT(hasOTTCPIP == PR_TRUE);
+
+ errOT = InitOpenTransport();
+ PR_ASSERT(err == kOTNoError);
+
+ sSvcRef = OTOpenInternetServices(kDefaultInternetServicesPath, NULL, &errOT);
+ if (errOT != kOTNoError) return; /* no network -- oh well */
+ PR_ASSERT((sSvcRef != NULL) && (errOT == kOTNoError));
+
+ /* Install notify function for DNR Address To String completion */
+ errOT = OTInstallNotifier(sSvcRef, NotifierRoutine, me);
+ PR_ASSERT(errOT == kOTNoError);
+
+ /* Put us into async mode */
+ errOT = OTSetAsynchronous(sSvcRef);
+ PR_ASSERT(errOT == kOTNoError);
+
+/* XXX Does not handle absence of open tpt and tcp yet! */
+}
+
+static void macsock_map_error(OSStatus err)
+{
+ _PR_MD_CURRENT_THREAD()->md.osErrCode = err;
+
+ if (IsEError(err) || (err >= EPERM && err <= ELASTERRNO)) {
+ switch (IsEError(err) ? OSStatus2E(err) : err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case EADDRNOTAVAIL:
+ PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err);
+ break;
+ case EINPROGRESS:
+ PR_SetError(PR_IN_PROGRESS_ERROR, err);
+ break;
+ case EWOULDBLOCK:
+ case EAGAIN:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case ETIMEDOUT:
+ PR_SetError(PR_IO_TIMEOUT_ERROR, err);
+ break;
+ case ECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case ENETUNREACH:
+ PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, err);
+ break;
+ case EADDRINUSE:
+ PR_SetError(PR_ADDRESS_IN_USE_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
+ break;
+ case EINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case EIO:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ENOENT:
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err);
+ break;
+ case ENXIO:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case EPROTOTYPE:
+ PR_SetError(PR_PROTOCOL_NOT_SUPPORTED_ERROR, err);
+ break;
+ case EOPNOTSUPP:
+ PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+ } else {
+ PR_ASSERT(IsXTIError(err));
+ switch (err) {
+ case kOTNoDataErr:
+ case kOTFlowErr:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ default:
+ PR_ASSERT(0);
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+ }
+}
+
+static void PrepareThreadForAsyncIO(PRThread *thread, EndpointRef endpoint, PRInt32 osfd)
+{
+ OSStatus err;
+
+ thread->io_pending = PR_TRUE;
+ thread->io_fd = osfd;
+ thread->md.osErrCode = noErr;
+
+ OTRemoveNotifier(endpoint);
+ err = OTInstallNotifier(endpoint, NotifierRoutine, thread);
+ PR_ASSERT(err == kOTNoError);
+}
+
+// Notification routine
+// Async callback routine.
+// A5 is OK. Cannot allocate memory here
+pascal void NotifierRoutine(void * contextPtr, OTEventCode code, OTResult result, void * cookie)
+{
+ PRThread * thread = (PRThread *) contextPtr;
+ _PRCPU *cpu = _PR_MD_CURRENT_CPU();
+
+ switch (code)
+ {
+// Async Completion Event
+ case T_OPENCOMPLETE:
+ case T_BINDCOMPLETE:
+ case T_UNBINDCOMPLETE:
+ case T_GETPROTADDRCOMPLETE:
+ case T_ACCEPTCOMPLETE:
+// Connect callback
+ case T_CONNECT:
+// Standard or expedited data is available
+ case T_DATA:
+ case T_EXDATA:
+// Standard or expedited data Flow control lifted
+ case T_GODATA:
+ case T_GOEXDATA:
+// Asynchronous Listen Event
+ case T_LISTEN:
+// DNR String To Address Complete Event
+ case T_DNRSTRINGTOADDRCOMPLETE:
+// Option Management Request Complete Event
+ case T_OPTMGMTCOMPLETE:
+ thread->md.osErrCode = result;
+ thread->md.cookie = cookie;
+ if (_PR_MD_GET_INTSOFF()) {
+ cpu->u.missed[cpu->where] |= _PR_MISSED_IO;
+ thread->md.notifyPending = PR_TRUE;
+ return;
+ }
+ DoneWaitingOnThisThread(thread);
+ break;
+
+// T_ORDREL orderly release is available; nothing to do
+ case T_ORDREL:
+ break;
+
+// T_PASSCON; nothing to do
+ case T_PASSCON:
+ break;
+
+// T_DISCONNECT; disconnect is available; nothing to do
+ case T_DISCONNECT:
+ break;
+
+// UDP Send error; clear the error
+ case T_UDERR:
+ (void) OTRcvUDErr((EndpointRef) cookie, NULL);
+ default:
+ PR_ASSERT(0);
+ }
+}
+
+
+static OSErr CreateSocket(int type, EndpointRef *endpoint)
+{
+ OSStatus err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ switch (type){
+ case SOCK_STREAM:
+ err = OTAsyncOpenEndpoint(OTCreateConfiguration(kTCPName), 0, NULL,
+ NotifierRoutine, me);
+ break;
+ case SOCK_DGRAM:
+ err = OTAsyncOpenEndpoint(OTCreateConfiguration(kUDPName), 0, NULL,
+ NotifierRoutine, me);
+ break;
+ }
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);
+
+ err = me->md.osErrCode;
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ *endpoint = me->md.cookie;
+ PR_ASSERT(*endpoint != NULL);
+
+ return kOTNoError;
+
+ErrorExit:
+ return err;
+}
+
+
+// Errors returned:
+// kOTXXXX - OT returned error
+// EPROTONOSUPPORT - bad socket type/protocol
+// ENOBUFS - not enough space for another socket, or failure in socket creation routine
+PRInt32 _MD_socket(int domain, int type, int protocol)
+{
+ OSStatus err;
+ EndpointRef endpoint;
+
+ // We only deal with internet domain
+ if (domain != AF_INET) {
+ err = kEPROTONOSUPPORTErr;
+ goto ErrorExit;
+ }
+
+ // We only know about tcp & udp
+ if ((type != SOCK_STREAM) && (type != SOCK_DGRAM)) {
+ err = kEPROTONOSUPPORTErr;
+ goto ErrorExit;
+ }
+
+ // Convert default types to specific types.
+ if (protocol == 0) {
+ if (type == SOCK_DGRAM)
+ protocol = IPPROTO_UDP;
+ else if (type == SOCK_STREAM)
+ protocol = IPPROTO_TCP;
+ }
+
+ // Only support default protocol for tcp
+ if ((type == SOCK_STREAM) && (protocol != IPPROTO_TCP)) {
+ err = kEPROTONOSUPPORTErr;
+ goto ErrorExit;
+ }
+
+ // Only support default protocol for udp
+ if ((type == SOCK_DGRAM) && (protocol != IPPROTO_UDP)) {
+ err = kEPROTONOSUPPORTErr;
+ goto ErrorExit;
+ }
+
+ // Create a socket, we might run out of memory
+ err = CreateSocket(type, &endpoint);
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ PR_ASSERT((PRInt32)endpoint != -1);
+
+ return ((PRInt32)endpoint);
+
+ErrorExit:
+ macsock_map_error(err);
+ return -1;
+}
+
+// Errors:
+// EBADF -- bad socket id
+// EFAULT -- bad address format
+PRInt32 _MD_bind(PRFileDesc *fd, PRNetAddr *addr, PRUint32 addrlen)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ OSStatus err;
+ EndpointRef endpoint = (EndpointRef) osfd;
+ TBind bindReq;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRUint32 retryCount = 0;
+
+ if (endpoint == NULL) {
+ err = kEBADFErr;
+ goto ErrorExit;
+ }
+
+ if (addr == NULL) {
+ err = kEFAULTErr;
+ goto ErrorExit;
+ }
+
+ // setup our request
+#if 0
+ if ((addr->inet.port == 0) || (addr->inet.ip == 0))
+ bindReq.addr.len = 0;
+ else
+#endif
+/*
+ * There seems to be a bug with OT ralted to OTBind failing with kOTNoAddressErr eventhough
+ * a proper legal address was supplied. This happens very rarely and just retrying the
+ * operation after a certain time (less than 1 sec. does not work) seems to succeed.
+ */
+
+TryAgain:
+ bindReq.addr.len = addrlen;
+
+ bindReq.addr.maxlen = addrlen;
+ bindReq.addr.buf = (UInt8*) addr;
+ bindReq.qlen = 1;
+
+ PrepareThreadForAsyncIO(me, endpoint, osfd);
+
+ err = OTBind(endpoint, &bindReq, NULL);
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);
+
+ err = me->md.osErrCode;
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ PR_ASSERT(me->md.cookie == NULL);
+
+ return kOTNoError;
+
+ErrorExit:
+ if ((err == kOTNoAddressErr) && (++retryCount <= 4)) {
+ long finalTicks;
+
+ Delay(100,&finalTicks);
+ goto TryAgain;
+ }
+ macsock_map_error(err);
+ return -1;
+}
+
+// Errors:
+// EBADF -- bad socket id
+PRInt32 _MD_listen(PRFileDesc *fd, PRIntn backlog)
+{
+#if 0
+ PRInt32 osfd = fd->secret->md.osfd;
+ OSStatus err;
+ EndpointRef endpoint = (EndpointRef) osfd;
+ TBind bindReq;
+ PRNetAddr addr;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (backlog == 0)
+ backlog = 1;
+
+ if (endpoint == NULL) {
+ err = EBADF;
+ goto ErrorExit;
+ }
+
+ addr.inet.port = addr.inet.ip = 0;
+
+ bindReq.addr.maxlen = PR_NETADDR_SIZE (&addr);
+ bindReq.addr.len = 0;
+ bindReq.addr.buf = (UInt8*) &addr;
+ bindReq.qlen = 0;
+
+ PrepareThreadForAsyncIO(me, endpoint, osfd);
+
+ err = OTGetProtAddress(endpoint, &bindReq, NULL);
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);
+
+ err = me->md.osErrCode;
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ PrepareThreadForAsyncIO(me, endpoint, osfd);
+
+ err = OTUnbind(endpoint);
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);
+
+ err = me->md.osErrCode;
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ bindReq.qlen = backlog;
+
+ PrepareThreadForAsyncIO(me, endpoint, osfd);
+
+ err = OTBind(endpoint, &bindReq, NULL);
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);
+
+ err = me->md.osErrCode;
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ PR_ASSERT(me->md.cookie == NULL);
+
+ return kOTNoError;
+
+ErrorExit:
+ macsock_map_error(err);
+ return -1;
+#endif
+
+#pragma unused (fd, backlog)
+ return kOTNoError;
+
+}
+
+// Errors:
+// EBADF -- bad socket id
+PRInt32 _MD_getsockname(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ OSStatus err;
+ EndpointRef endpoint = (EndpointRef) osfd;
+ TBind bindReq;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (endpoint == NULL) {
+ err = kEBADFErr;
+ goto ErrorExit;
+ }
+
+ if (addr == NULL) {
+ err = kEFAULTErr;
+ goto ErrorExit;
+ }
+
+#if !defined(_PR_INET6)
+ addr->inet.family = AF_INET;
+#endif
+
+ PR_ASSERT(PR_NETADDR_SIZE(addr) >= (*addrlen));
+
+ bindReq.addr.len = *addrlen;
+ bindReq.addr.maxlen = *addrlen;
+ bindReq.addr.buf = (UInt8*) addr;
+ bindReq.qlen = 0;
+
+ PrepareThreadForAsyncIO(me, endpoint, osfd);
+
+ err = OTGetProtAddress(endpoint, &bindReq, NULL);
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);
+
+ err = me->md.osErrCode;
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ PR_ASSERT(me->md.cookie == &bindReq);
+
+ return kOTNoError;
+
+ErrorExit:
+ macsock_map_error(err);
+ return -1;
+}
+
+PRStatus _MD_getsockopt(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen)
+{
+ OSStatus err;
+ PRInt32 osfd = fd->secret->md.osfd;
+ EndpointRef endpoint = (EndpointRef) osfd;
+ TOptMgmt cmd;
+ TOption *opt;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ unsigned char optionBuffer[kOTOptionHeaderSize + sizeof(PRSocketOptionData)];
+
+ if (endpoint == NULL) {
+ err = kEBADFErr;
+ goto ErrorExit;
+ }
+
+ /*
+ OT wants IPPROTO_IP for level and not XTI_GENERIC. SO_REUSEADDR and SO_KEEPALIVE
+ are equated to IP level and TCP level options respectively and hence we need to set
+ the level correctly.
+ */
+ if (level == SOL_SOCKET) {
+ if (optname == SO_REUSEADDR)
+ level = IPPROTO_IP;
+ else if (optname == SO_KEEPALIVE)
+ level = INET_TCP;
+ }
+
+ opt = (TOption *)&optionBuffer[0];
+ opt->len = sizeof(TOption);
+ opt->level = level;
+ opt->name = optname;
+ opt->status = 0;
+
+ cmd.opt.len = sizeof(TOption);
+ cmd.opt.maxlen = sizeof(optionBuffer);
+ cmd.opt.buf = (UInt8*)optionBuffer;
+ cmd.flags = T_CURRENT;
+
+ PrepareThreadForAsyncIO(me, endpoint, osfd);
+
+ err = OTOptionManagement(endpoint, &cmd, &cmd);
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);
+
+ err = me->md.osErrCode;
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ if (opt->status == T_FAILURE || opt->status == T_NOTSUPPORT){
+ err = kEOPNOTSUPPErr;
+ goto ErrorExit;
+ }
+
+ PR_ASSERT(opt->status == T_SUCCESS);
+
+ switch (optname) {
+ case SO_LINGER:
+ *((t_linger*)optval) = *((t_linger*)&opt->value);
+ *optlen = sizeof(t_linger);
+ break;
+ case SO_REUSEADDR:
+ case TCP_NODELAY:
+ case SO_KEEPALIVE:
+ case SO_RCVBUF:
+ case SO_SNDBUF:
+ *((PRIntn*)optval) = *((PRIntn*)&opt->value);
+ *optlen = sizeof(PRIntn);
+ break;
+ case IP_MULTICAST_LOOP:
+ *((PRUint8*)optval) = *((PRIntn*)&opt->value);
+ *optlen = sizeof(PRUint8);
+ break;
+ case IP_TTL:
+ *((PRUintn*)optval) = *((PRUint8*)&opt->value);
+ *optlen = sizeof(PRUintn);
+ break;
+ case IP_MULTICAST_TTL:
+ *((PRUint8*)optval) = *((PRUint8*)&opt->value);
+ *optlen = sizeof(PRUint8);
+ break;
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ {
+ /* struct ip_mreq and TIPAddMulticast are the same size and optval
+ is pointing to struct ip_mreq */
+ *((struct ip_mreq *)optval) = *((struct ip_mreq *)&opt->value);
+ *optlen = sizeof(struct ip_mreq);
+ break;
+ }
+ case IP_MULTICAST_IF:
+ {
+ *((PRUint32*)optval) = *((PRUint32*)&opt->value);
+ *optlen = sizeof(PRUint32);
+ break;
+ }
+ /*case IP_TOS:*/ /*IP_TOS has same value as TCP_MAXSEG */
+ case TCP_MAXSEG:
+ if (level == IPPROTO_TCP) { /* it is TCP_MAXSEG */
+ *((PRIntn*)optval) = *((PRIntn*)&opt->value);
+ *optlen = sizeof(PRIntn);
+ } else { /* it is IP_TOS */
+ *((PRUintn*)optval) = *((PRUint8*)&opt->value);
+ *optlen = sizeof(PRUintn);
+ }
+ break;
+ default:
+ PR_ASSERT(0);
+ break;
+ }
+
+ return PR_SUCCESS;
+
+ErrorExit:
+ macsock_map_error(err);
+ return PR_FAILURE;
+}
+
+PRStatus _MD_setsockopt(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen)
+{
+ OSStatus err;
+ PRInt32 osfd = fd->secret->md.osfd;
+ EndpointRef endpoint = (EndpointRef) osfd;
+ TOptMgmt cmd;
+ TOption *opt;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ unsigned char optionBuffer[kOTOptionHeaderSize + sizeof(PRSocketOptionData) + 1];
+
+ if (endpoint == NULL) {
+ err = kEBADFErr;
+ goto ErrorExit;
+ }
+
+ /*
+ OT wants IPPROTO_IP for level and not XTI_GENERIC. SO_REUSEADDR and SO_KEEPALIVE
+ are equated to IP level and TCP level options respectively and hence we need to set
+ the level correctly.
+ */
+ if (level == SOL_SOCKET) {
+ if (optname == SO_REUSEADDR)
+ level = IPPROTO_IP;
+ else if (optname == SO_KEEPALIVE)
+ level = INET_TCP;
+ }
+
+ opt = (TOption *)&optionBuffer[0];
+ opt->len = kOTOptionHeaderSize + optlen;
+
+ /* special case adjustments for length follow */
+ if (optname == SO_KEEPALIVE) /* we need to pass the timeout value for OT */
+ opt->len = kOTOptionHeaderSize + sizeof(t_kpalive);
+ if (optname == IP_MULTICAST_TTL || optname == IP_TTL) /* it is an unsigned char value */
+ opt->len = kOTOneByteOptionSize;
+ if (optname == IP_TOS && level == IPPROTO_IP)
+ opt->len = kOTOneByteOptionSize;
+
+ opt->level = level;
+ opt->name = optname;
+ opt->status = 0;
+
+ cmd.opt.len = opt->len;
+ cmd.opt.maxlen = sizeof(optionBuffer);
+ cmd.opt.buf = (UInt8*)optionBuffer;
+
+ optionBuffer[opt->len] = 0;
+
+ cmd.flags = T_NEGOTIATE;
+
+ switch (optname) {
+ case SO_LINGER:
+ *((t_linger*)&opt->value) = *((t_linger*)optval);
+ break;
+ case SO_REUSEADDR:
+ case TCP_NODELAY:
+ case SO_RCVBUF:
+ case SO_SNDBUF:
+ *((PRIntn*)&opt->value) = *((PRIntn*)optval);
+ break;
+ case IP_MULTICAST_LOOP:
+ if (*optval != 0)
+ opt->value[0] = T_YES;
+ else
+ opt->value[0] = T_NO;
+ break;
+ case SO_KEEPALIVE:
+ {
+ t_kpalive *kpalive = (t_kpalive *)&opt->value;
+
+ kpalive->kp_onoff = *((long*)optval);
+ kpalive->kp_timeout = 10; /* timeout in minutes */
+ break;
+ }
+ case IP_TTL:
+ *((unsigned char*)&opt->value) = *((PRUintn*)optval);
+ break;
+ case IP_MULTICAST_TTL:
+ *((unsigned char*)&opt->value) = *optval;
+ break;
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ {
+ /* struct ip_mreq and TIPAddMulticast are the same size and optval
+ is pointing to struct ip_mreq */
+ *((TIPAddMulticast *)&opt->value) = *((TIPAddMulticast *)optval);
+ break;
+ }
+ case IP_MULTICAST_IF:
+ {
+ *((PRUint32*)&opt->value) = *((PRUint32*)optval);
+ break;
+ }
+ /*case IP_TOS:*/ /*IP_TOS has same value as TCP_MAXSEG */
+ case TCP_MAXSEG:
+ if (level == IPPROTO_TCP) { /* it is TCP_MAXSEG */
+ *((PRIntn*)&opt->value) = *((PRIntn*)optval);
+ } else { /* it is IP_TOS */
+ *((unsigned char*)&opt->value) = *((PRUintn*)optval);
+ }
+ break;
+ default:
+ PR_ASSERT(0);
+ break;
+ }
+
+ PrepareThreadForAsyncIO(me, endpoint, osfd);
+
+ err = OTOptionManagement(endpoint, &cmd, &cmd);
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);
+
+ err = me->md.osErrCode;
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ if (opt->status == T_FAILURE || opt->status == T_NOTSUPPORT){
+ err = kEOPNOTSUPPErr;
+ goto ErrorExit;
+ }
+
+ if (level == IPPROTO_TCP && optname == TCP_MAXSEG && opt->status == T_READONLY) {
+ err = kEOPNOTSUPPErr;
+ goto ErrorExit;
+ }
+
+ PR_ASSERT(opt->status == T_SUCCESS);
+
+ return PR_SUCCESS;
+
+ErrorExit:
+ macsock_map_error(err);
+ return PR_FAILURE;
+}
+
+PRInt32 _MD_socketavailable(PRFileDesc *fd)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ OSStatus err;
+ EndpointRef endpoint = (EndpointRef) osfd;
+ size_t bytes;
+
+ if (endpoint == NULL) {
+ err = kEBADFErr;
+ goto ErrorExit;
+ }
+
+ bytes = 0;
+
+ err = OTCountDataBytes(endpoint, &bytes);
+ if ((err == kOTLookErr) || // Not really errors, we just need to do a read,
+ (err == kOTNoDataErr)) // or thereÕs nothing there.
+ err = kOTNoError;
+
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ return bytes;
+
+ErrorExit:
+ macsock_map_error(err);
+ return -1;
+}
+
+PRInt32 _MD_accept(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ OSStatus err;
+ EndpointRef endpoint = (EndpointRef) osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ TBind bindReq;
+ PRNetAddr bindAddr;
+ PRInt32 newosfd = -1;
+ EndpointRef newEndpoint;
+ TCall call;
+ PRNetAddr callAddr;
+
+ if (endpoint == NULL) {
+ err = kEBADFErr;
+ goto ErrorExit;
+ }
+
+ memset(&call, 0 , sizeof(call));
+
+ call.addr.maxlen = PR_NETADDR_SIZE(&callAddr);
+ call.addr.len = PR_NETADDR_SIZE(&callAddr);
+ call.addr.buf = (UInt8*) &callAddr;
+
+ PrepareThreadForAsyncIO(me, endpoint, osfd);
+
+ err = OTListen (endpoint, &call);
+ if (err != kOTNoError && (err != kOTNoDataErr || fd->secret->nonblocking)) {
+ me->io_pending = PR_FALSE;
+ goto ErrorExit;
+ }
+
+ while (err == kOTNoDataErr) {
+ WaitOnThisThread(me, timeout);
+ err = me->md.osErrCode;
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ PrepareThreadForAsyncIO(me, endpoint, osfd);
+
+ err = OTListen (endpoint, &call);
+ if (err == kOTNoError)
+ break;
+
+ PR_ASSERT(err == kOTNoDataErr);
+ }
+
+ newosfd = _MD_socket(AF_INET, SOCK_STREAM, 0);
+ if (newosfd == -1)
+ return -1;
+
+ newEndpoint = (EndpointRef)newosfd;
+
+ // Bind to a local port; let the system assign it.
+
+ bindAddr.inet.port = bindAddr.inet.ip = 0;
+
+ bindReq.addr.maxlen = PR_NETADDR_SIZE (&bindAddr);
+ bindReq.addr.len = 0;
+ bindReq.addr.buf = (UInt8*) &bindAddr;
+ bindReq.qlen = 0;
+
+ PrepareThreadForAsyncIO(me, newEndpoint, newosfd);
+
+ err = OTBind(newEndpoint, &bindReq, NULL);
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ WaitOnThisThread(me, timeout);
+
+ err = me->md.osErrCode;
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ PrepareThreadForAsyncIO(me, endpoint, newosfd);
+
+ err = OTAccept (endpoint, newEndpoint, &call);
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ WaitOnThisThread(me, timeout);
+
+ err = me->md.osErrCode;
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ PR_ASSERT(me->md.cookie != NULL);
+
+ if (addr != NULL)
+ *addr = callAddr;
+ if (addrlen != NULL)
+ *addrlen = call.addr.len;
+
+ return newosfd;
+
+ErrorExit:
+ if (newosfd != -1)
+ _MD_closesocket(newosfd);
+ macsock_map_error(err);
+ return -1;
+}
+
+PRInt32 _MD_connect(PRFileDesc *fd, PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ OSStatus err;
+ EndpointRef endpoint = (EndpointRef) osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ TCall sndCall;
+ TBind bindReq;
+ PRNetAddr bindAddr;
+
+ if (endpoint == NULL) {
+ err = kEBADFErr;
+ goto ErrorExit;
+ }
+
+ if (addr == NULL) {
+ err = kEFAULTErr;
+ goto ErrorExit;
+ }
+
+ // Bind to a local port; let the system assign it.
+
+ bindAddr.inet.port = bindAddr.inet.ip = 0;
+
+ bindReq.addr.maxlen = PR_NETADDR_SIZE (&bindAddr);
+ bindReq.addr.len = 0;
+ bindReq.addr.buf = (UInt8*) &bindAddr;
+ bindReq.qlen = 0;
+
+ PrepareThreadForAsyncIO(me, endpoint, osfd);
+
+ err = OTBind(endpoint, &bindReq, NULL);
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);
+
+ err = me->md.osErrCode;
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ memset(&sndCall, 0 , sizeof(sndCall));
+
+ sndCall.addr.maxlen = addrlen;
+ sndCall.addr.len = addrlen;
+ sndCall.addr.buf = (UInt8*) addr;
+
+ PrepareThreadForAsyncIO(me, endpoint, osfd);
+
+ err = OTConnect (endpoint, &sndCall, NULL);
+ if (err != kOTNoError && err != kOTNoDataErr)
+ goto ErrorExit;
+ if (err == kOTNoDataErr && fd->secret->nonblocking) {
+ err = kEINPROGRESSErr;
+ me->io_pending = PR_FALSE;
+ goto ErrorExit;
+ }
+
+ WaitOnThisThread(me, timeout);
+
+ err = me->md.osErrCode;
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ PR_ASSERT(me->md.cookie != NULL);
+
+ err = OTRcvConnect(endpoint, NULL);
+ PR_ASSERT(err == kOTNoError);
+
+ return kOTNoError;
+
+ErrorExit:
+ macsock_map_error(err);
+ return -1;
+}
+
+// Errors:
+// EBADF -- bad socket id
+// EFAULT -- bad buffer
+static PRInt32 SendReceiveStream(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout, SndRcvOpCode opCode)
+{
+ OSStatus err;
+ OTResult result;
+ PRInt32 osfd = fd->secret->md.osfd;
+ EndpointRef endpoint = (EndpointRef) osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 bytesLeft = amount;
+
+ PR_ASSERT(flags == 0);
+
+ if (endpoint == NULL) {
+ err = kEBADFErr;
+ goto ErrorExit;
+ }
+
+ if (buf == NULL) {
+ err = kEFAULTErr;
+ goto ErrorExit;
+ }
+
+ while (bytesLeft > 0) {
+
+ PrepareThreadForAsyncIO(me, endpoint, osfd);
+
+ if (opCode == kSTREAM_SEND)
+ result = OTSnd(endpoint, buf, bytesLeft, NULL);
+ else if (opCode == kSTREAM_RECEIVE)
+ result = OTRcv(endpoint, buf, bytesLeft, NULL);
+ else {
+ err = kEINVALErr;
+ goto ErrorExit;
+ }
+
+ if (result > 0) {
+ buf = (void *) ( (UInt32) buf + (UInt32)result );
+ bytesLeft -= result;
+ me->io_pending = PR_FALSE;
+ if (opCode == kSTREAM_RECEIVE)
+ return result;
+ } else {
+ if (result == kOTOutStateErr) { /* it has been closed */
+ return 0;
+ }
+ if (result == kOTLookErr) {
+ PRBool readReady,writeReady,exceptReady;
+ /* process the event and then continue the operation */
+ (void) GetState(endpoint, &readReady, &writeReady, &exceptReady);
+ continue;
+ }
+ if (result != kOTNoDataErr && result != kOTFlowErr &&
+ result != kEAGAINErr && result != kEWOULDBLOCKErr) {
+ err = result;
+ goto ErrorExit;
+ } else if (fd->secret->nonblocking) {
+ me->io_pending = PR_FALSE;
+ err = result;
+ goto ErrorExit;
+ }
+ WaitOnThisThread(me, timeout);
+ err = me->md.osErrCode;
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ PR_ASSERT(me->md.cookie != NULL);
+ }
+ }
+
+ return amount;
+
+ErrorExit:
+ macsock_map_error(err);
+ return -1;
+}
+
+PRInt32 _MD_recv(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ return (SendReceiveStream(fd, buf, amount, flags, timeout, kSTREAM_RECEIVE));
+}
+
+PRInt32 _MD_send(PRFileDesc *fd,const void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ return (SendReceiveStream(fd, (void *)buf, amount, flags, timeout, kSTREAM_SEND));
+}
+
+
+// Errors:
+// EBADF -- bad socket id
+// EFAULT -- bad buffer
+static PRInt32 SendReceiveDgram(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRNetAddr *addr, PRUint32 *addrlen,
+ PRIntervalTime timeout, SndRcvOpCode opCode)
+{
+ OSStatus err;
+ PRInt32 osfd = fd->secret->md.osfd;
+ EndpointRef endpoint = (EndpointRef) osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 bytesLeft = amount;
+ TUnitData dgram;
+
+ PR_ASSERT(flags == 0);
+
+ if (endpoint == NULL) {
+ err = kEBADFErr;
+ goto ErrorExit;
+ }
+
+ if (buf == NULL || addr == NULL) {
+ err = kEFAULTErr;
+ goto ErrorExit;
+ }
+
+ memset(&dgram, 0 , sizeof(dgram));
+ dgram.addr.maxlen = *addrlen;
+ dgram.addr.len = *addrlen;
+ dgram.addr.buf = (UInt8*) addr;
+ dgram.udata.maxlen = amount;
+ dgram.udata.len = amount;
+ dgram.udata.buf = (UInt8*) buf;
+
+ while (bytesLeft > 0) {
+
+ PrepareThreadForAsyncIO(me, endpoint, osfd);
+
+ if (opCode == kDGRAM_SEND)
+ err = OTSndUData(endpoint, &dgram);
+ else if (opCode == kDGRAM_RECEIVE)
+ err = OTRcvUData(endpoint, &dgram, NULL);
+ else {
+ err = kEINVALErr;
+ goto ErrorExit;
+ }
+
+ if (err == kOTNoError) {
+ buf = (void *) ( (UInt32) buf + (UInt32)dgram.udata.len );
+ bytesLeft -= dgram.udata.len;
+ dgram.udata.buf = (UInt8*) buf;
+ me->io_pending = PR_FALSE;
+ }
+ else {
+ PR_ASSERT(err == kOTNoDataErr || err == kOTOutStateErr);
+ WaitOnThisThread(me, timeout);
+ err = me->md.osErrCode;
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ PR_ASSERT(me->md.cookie != NULL);
+ }
+ }
+
+ if (opCode == kDGRAM_RECEIVE)
+ *addrlen = dgram.addr.len;
+
+ return amount;
+
+ErrorExit:
+ macsock_map_error(err);
+ return -1;
+}
+
+PRInt32 _MD_recvfrom(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRNetAddr *addr, PRUint32 *addrlen,
+ PRIntervalTime timeout)
+{
+ return (SendReceiveDgram(fd, buf, amount, flags, addr, addrlen,
+ timeout, kDGRAM_RECEIVE));
+}
+
+PRInt32 _MD_sendto(PRFileDesc *fd,const void *buf, PRInt32 amount,
+ PRIntn flags, PRNetAddr *addr, PRUint32 addrlen,
+ PRIntervalTime timeout)
+{
+ return (SendReceiveDgram(fd, (void *)buf, amount, flags, addr, &addrlen,
+ timeout, kDGRAM_SEND));
+}
+
+PRInt32 _MD_closesocket(PRInt32 osfd)
+{
+ OSStatus err;
+ EndpointRef endpoint = (EndpointRef) osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (endpoint == NULL) {
+ err = kEBADFErr;
+ goto ErrorExit;
+ }
+
+ if (me->io_pending && me->io_fd == osfd)
+ me->io_pending = PR_FALSE;
+
+#if 0
+ {
+ OTResult state;
+ state = OTGetEndpointState(endpoint);
+
+ err = OTSndOrderlyDisconnect(endpoint);
+ if (err != kOTNoError && err != kOTOutStateErr)
+ goto ErrorExit;
+
+ state = OTGetEndpointState(endpoint);
+
+ err = OTUnbind(endpoint);
+ if (err != kOTNoError && err != kOTOutStateErr)
+ goto ErrorExit;
+
+ state = OTGetEndpointState(endpoint);
+
+ err = OTSetSynchronous(endpoint);
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ err = OTSetBlocking(endpoint);
+ if (err != kOTNoError)
+ goto ErrorExit;
+ }
+#endif
+
+ (void) OTSndOrderlyDisconnect(endpoint);
+
+ err = OTCloseProvider(endpoint);
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ return kOTNoError;
+
+ErrorExit:
+ macsock_map_error(err);
+ return -1;
+}
+
+PRInt32 _MD_writev(PRFileDesc *fd, struct PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout)
+{
+#pragma unused (fd, iov, iov_size, timeout)
+
+ PR_ASSERT(0);
+ _PR_MD_CURRENT_THREAD()->md.osErrCode = unimpErr;
+ return -1;
+}
+
+static PRBool GetState(EndpointRef endpoint, PRBool *readReady, PRBool *writeReady, PRBool *exceptReady)
+{
+ OSStatus err;
+ OTResult resultOT;
+ TDiscon discon;
+ PRBool result = PR_FALSE;
+
+ *readReady = *writeReady = *exceptReady = PR_FALSE;
+
+ resultOT = OTLook(endpoint);
+ switch (resultOT) {
+ case T_DATA:
+ case T_LISTEN:
+ *readReady = PR_TRUE;
+ break;
+ case T_CONNECT:
+ err = OTRcvConnect(endpoint, NULL);
+ PR_ASSERT(err == kOTNoError);
+ break;
+ case T_DISCONNECT:
+ memset(&discon, 0 , sizeof(discon));
+ err = OTRcvDisconnect(endpoint, &discon);
+ PR_ASSERT(err == kOTNoError);
+ macsock_map_error(discon.reason);
+ *exceptReady = PR_TRUE;
+ break;
+ case T_ORDREL:
+ *readReady = PR_TRUE;
+ err = OTRcvOrderlyDisconnect(endpoint);
+ PR_ASSERT(err == kOTNoError);
+ break;
+ }
+ resultOT = OTGetEndpointState(endpoint);
+ switch (resultOT) {
+ case T_DATAXFER:
+ case T_INREL:
+ *writeReady = PR_TRUE;
+ break;
+ default:
+ *writeReady = PR_FALSE;
+ }
+
+ if ((*readReady == PR_TRUE) || (*writeReady==PR_TRUE) || (*exceptReady==PR_TRUE))
+ result = PR_TRUE;
+
+ return result;
+}
+
+PRInt32 _MD_poll(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
+{
+ PRPollDesc *pd, *epd;
+ PRInt32 n = 0;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRIntervalTime sleepTime;
+ PRIntervalTime timein = PR_IntervalNow();
+
+ sleepTime = PR_MillisecondsToInterval(5UL);
+ if (sleepTime > timeout)
+ sleepTime = timeout;
+
+ do {
+
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ PRInt32 osfd;
+ PRInt16 in_flags = pd->in_flags;
+ PRFileDesc *bottom = pd->fd;
+ EndpointRef endpoint;
+ PRInt16 out_flags = 0;
+ PRBool readReady, writeReady, exceptReady;
+
+ pd->out_flags = 0;
+ if (NULL == bottom || in_flags == 0) {
+ continue;
+ }
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ osfd = bottom->secret->md.osfd;
+ endpoint = (EndpointRef) osfd;
+
+ if (GetState(endpoint, &readReady, &writeReady, &exceptReady)) {
+
+ if ((in_flags & PR_POLL_READ) && (readReady)) {
+ out_flags |= PR_POLL_READ;
+ }
+ if ((in_flags & PR_POLL_WRITE) && (writeReady)) {
+ out_flags |= PR_POLL_WRITE;
+ }
+ if ((in_flags & PR_POLL_EXCEPT) && (exceptReady)) {
+ out_flags |= PR_POLL_EXCEPT;
+ }
+ pd->out_flags = out_flags;
+ if (out_flags) {
+ n++;
+ }
+ }
+ }
+
+ if (n > 0)
+ return n;
+
+ (void) PR_Sleep(sleepTime);
+
+ } while ((timeout == PR_INTERVAL_NO_TIMEOUT) ||
+ (((PRIntervalTime)(PR_IntervalNow() - timein)) < timeout));
+
+ return 0; /* timed out */
+}
+
+void _MD_makenonblock(PRFileDesc *fd)
+{
+ OSStatus err;
+ PRInt32 osfd = fd->secret->md.osfd;
+ EndpointRef endpoint = (EndpointRef) osfd;
+
+ err = OTSetNonBlocking(endpoint);
+ PR_ASSERT(err == kOTNoError || err == kOTOutStateErr);
+}
+
+PR_IMPLEMENT(PRInt32) _MD_shutdown(PRFileDesc *fd, PRIntn how)
+{
+#pragma unused (fd, how)
+
+/* Just succeed silently!!! */
+return (0);
+}
+
+PR_IMPLEMENT(PRStatus) _MD_getpeername(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen)
+{
+#pragma unused (fd, addr, addrlen)
+
+ PR_ASSERT(0);
+ _PR_MD_CURRENT_THREAD()->md.osErrCode = unimpErr;
+ return PR_FAILURE;
+}
+
+
+PR_IMPLEMENT(unsigned long) inet_addr(const char *cp)
+{
+ OSStatus err;
+ InetHost host;
+
+ err = OTInetStringToHost((char*) cp, &host);
+ PR_ASSERT(err == kOTNoError);
+
+ return host;
+}
+
+
+static char *sAliases[1] = {NULL};
+static struct hostent sHostEnt = {NULL, &sAliases[0], AF_INET, sizeof (long), NULL};
+static InetHostInfo sHostInfo;
+static InetHost *sAddresses[kMaxHostAddrs+1];
+
+PR_IMPLEMENT(struct hostent *) gethostbyname(const char * name)
+{
+ OSStatus err;
+ PRUint32 index;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PrepareThreadForAsyncIO(me, sSvcRef, NULL);
+
+ err = OTInetStringToAddress(sSvcRef, (char *)name, &sHostInfo);
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);
+
+ err = me->md.osErrCode;
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ sHostEnt.h_name = sHostInfo.name;
+ for (index=0; index<kMaxHostAddrs && sHostInfo.addrs[index] != NULL; index++)
+ sAddresses[index] = &sHostInfo.addrs[index];
+ sAddresses[index] = NULL;
+ sHostEnt.h_addr_list = (char **)sAddresses;
+
+ return (&sHostEnt);
+
+ErrorExit:
+ macsock_map_error(err);
+ return NULL;
+}
+
+
+PR_IMPLEMENT(struct hostent *) gethostbyaddr(const void *addr, int addrlen, int type)
+{
+ PR_ASSERT(type == AF_INET);
+ PR_ASSERT(addrlen == sizeof(struct in_addr));
+
+ OTInetHostToString((InetHost)addr, sHostInfo.name);
+
+ return (gethostbyname(sHostInfo.name));
+}
+
+
+PR_IMPLEMENT(char *) inet_ntoa(struct in_addr addr)
+{
+ OTInetHostToString((InetHost)addr.s_addr, sHostInfo.name);
+
+ return sHostInfo.name;
+}
+
+
+PR_IMPLEMENT(PRStatus) _MD_gethostname(char *name, int namelen)
+{
+ OSStatus err;
+ InetInterfaceInfo info;
+
+ /*
+ * On a Macintosh, we donÕt have the concept of a local host name.
+ * We do though have an IP address & everyone should be happy with
+ * a string version of that for a name.
+ * The alternative here is to ping a local DNS for our name, they
+ * will often know it. This is the cheap, easiest, and safest way out.
+ */
+
+ /* Make sure the string is as long as the longest possible address */
+ if (namelen < strlen("123.123.123.123")) {
+ err = kEINVALErr;
+ goto ErrorExit;
+ }
+
+ err = OTInetGetInterfaceInfo(&info, kDefaultInetInterface);
+ if (err != kOTNoError)
+ goto ErrorExit;
+
+ OTInetHostToString(info.fAddress, name);
+
+ return PR_SUCCESS;
+
+ErrorExit:
+ macsock_map_error(err);
+ return PR_FAILURE;
+}
+
+
+#define kIPName "ip"
+static struct protoent sIPProto = {kIPName, NULL, INET_IP};
+static struct protoent sTCPProto = {kTCPName, NULL, INET_TCP};
+static struct protoent sUDPProto = {kUDPName, NULL, INET_UDP};
+
+PR_IMPLEMENT(struct protoent *) getprotobyname(const char * name)
+{
+ if (strcmp(name, kIPName) == 0)
+ return (&sIPProto);
+
+ if (strcmp(name, kTCPName) == 0)
+ return (&sTCPProto);
+
+ if (strcmp(name, kUDPName) == 0)
+ return (&sUDPProto);
+
+ErrorExit:
+ macsock_map_error(kEINVALErr);
+ return NULL;
+}
+
+
+PR_IMPLEMENT(struct protoent *) getprotobynumber(int number)
+{
+ if (number == INET_IP)
+ return (&sIPProto);
+
+ if (number == INET_TCP)
+ return (&sTCPProto);
+
+ if (number == INET_UDP)
+ return (&sUDPProto);
+
+ErrorExit:
+ macsock_map_error(kEINVALErr);
+ return NULL;
+}
+
+
+int _MD_mac_get_nonblocking_connect_error(PRInt32 osfd)
+{
+ OTResult resultOT;
+ EndpointRef endpoint = (EndpointRef) osfd;
+
+ resultOT = OTGetEndpointState(endpoint);
+ switch (resultOT) {
+ case T_OUTCON:
+ macsock_map_error(kEINPROGRESSErr);
+ return -1;
+ case T_DATAXFER:
+ return 0;
+ case T_IDLE:
+ return -1;
+ default:
+ PR_ASSERT(0);
+ return -1;
+ }
+}
diff --git a/pr/src/md/mac/macthr.c b/pr/src/md/mac/macthr.c
new file mode 100644
index 00000000..fa5a4637
--- /dev/null
+++ b/pr/src/md/mac/macthr.c
@@ -0,0 +1,381 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+#include <Types.h>
+#include <Timer.h>
+#include <OSUtils.h>
+
+#include <LowMem.h>
+
+// This should be in LowMem.h
+#ifndef LMSetStackLowPoint
+#define LMSetStackLowPoint(value) \
+ *((UInt32 *)(0x110)) = (value)
+#endif
+
+PRThread *gPrimaryThread = NULL;
+
+PR_IMPLEMENT(PRThread *) PR_GetPrimaryThread()
+{
+ return gPrimaryThread;
+}
+
+//##############################################################################
+//##############################################################################
+#pragma mark -
+#pragma mark CREATING MACINTOSH THREAD STACKS
+
+
+/*
+** Allocate a new memory segment. We allocate it from our figment heap. Currently,
+** it is being used for per thread stack space.
+**
+** Return the segment's access rights and size. vaddr is used on Unix platforms to
+** map an existing address for the segment.
+*/
+PRStatus _MD_AllocSegment(PRSegment *seg, PRUint32 size, void *vaddr)
+{
+ PR_ASSERT(seg != 0);
+ PR_ASSERT(size != 0);
+ PR_ASSERT(vaddr == 0);
+
+ /*
+ ** Take the actual memory for the segment out of our Figment heap.
+ */
+
+ seg->vaddr = (char *)malloc(size);
+
+ if (seg->vaddr == NULL) {
+
+#if DEBUG
+ DebugStr("\p_MD_AllocSegment failed.");
+#endif
+
+ return PR_FAILURE;
+ }
+
+ seg->access = PR_SEGMENT_RDWR;
+ seg->size = size;
+
+ return PR_SUCCESS;
+}
+
+
+/*
+** Free previously allocated memory segment.
+*/
+void _MD_FreeSegment(PRSegment *seg)
+{
+ PR_ASSERT((seg->flags & _PR_SEG_VM) == 0);
+
+ if (seg->vaddr != NULL)
+ DisposePtr((Ptr)(seg->vaddr));
+}
+
+
+/*
+** The thread's stack has been allocated and its fields are already properly filled
+** in by PR. Perform any debugging related initialization here.
+**
+** Put a recognizable pattern so that we can find it from Macsbug.
+** Put a cookie at the top of the stack so that we can find it from Macsbug.
+*/
+void _MD_InitStack(PRThreadStack *ts, int redZoneBytes)
+ {
+#pragma unused (redZoneBytes)
+#if DEVELOPER_DEBUG
+ // Put a cookie at the top of the stack so that we can find
+ // it from Macsbug.
+
+ memset(ts->allocBase, 0xDC, ts->stackSize);
+
+ ((UInt32 *)ts->stackTop)[-1] = 0xBEEFCAFE;
+ ((UInt32 *)ts->stackTop)[-2] = (UInt32)gPrimaryThread;
+ ((UInt32 *)ts->stackTop)[-3] = (UInt32)(ts);
+ ((UInt32 *)ts->stackBottom)[0] = 0xCAFEBEEF;
+#else
+#pragma unused (ts)
+#endif
+
+ /*
+ ** Turn off the snack stiffer. The NSPR stacks are allocated in the
+ ** application's heap; this throws the stack sniffer for a tizzy.
+ ** Note that the sniffer does not run on machines running the thread manager.
+ ** Yes, we will blast the low-mem every time a new stack is created. We can afford
+ ** a couple extra cycles.
+ */
+ LMSetStackLowPoint(0);
+ }
+
+extern void _MD_ClearStack(PRThreadStack *ts)
+ {
+#if DEVELOPER_DEBUG
+ // Clear out our cookies.
+
+ memset(ts->allocBase, 0xEF, ts->allocSize);
+ ((UInt32 *)ts->stackTop)[-1] = 0;
+ ((UInt32 *)ts->stackTop)[-2] = 0;
+ ((UInt32 *)ts->stackTop)[-3] = 0;
+ ((UInt32 *)ts->stackBottom)[0] = 0;
+#else
+#pragma unused (ts)
+#endif
+ }
+
+
+//##############################################################################
+//##############################################################################
+#pragma mark -
+#pragma mark TIME MANAGER-BASED CLOCK
+
+TMTask gTimeManagerTaskElem;
+
+extern void _MD_IOInterrupt(void);
+_PRInterruptTable _pr_interruptTable[] = {
+ { "clock", _PR_MISSED_CLOCK, _PR_ClockInterrupt, },
+ { "i/o", _PR_MISSED_IO, _MD_IOInterrupt, },
+ { 0 }
+};
+
+pascal void TimerCallback(TMTaskPtr tmTaskPtr)
+{
+ _PRCPU *cpu = _PR_MD_CURRENT_CPU();
+
+ if (_PR_MD_GET_INTSOFF()) {
+ cpu->u.missed[cpu->where] |= _PR_MISSED_CLOCK;
+ PrimeTime((QElemPtr)tmTaskPtr, kMacTimerInMiliSecs);
+ return;
+ }
+ _PR_SET_INTSOFF(1);
+
+ // And tell nspr that a clock interrupt occured.
+ _PR_ClockInterrupt();
+
+ if ((_PR_RUNQREADYMASK(cpu)) >> ((_PR_MD_CURRENT_THREAD()->priority)))
+ _PR_SET_RESCHED_FLAG();
+
+ _PR_SET_INTSOFF(0);
+
+ // Reset the clock timer so that we fire again.
+ PrimeTime((QElemPtr)tmTaskPtr, kMacTimerInMiliSecs);
+}
+
+#if GENERATINGCFM
+
+RoutineDescriptor gTimerCallbackRD = BUILD_ROUTINE_DESCRIPTOR(uppTimerProcInfo, &TimerCallback);
+
+#else
+
+asm void gTimerCallbackRD(void)
+{
+ // Check out LocalA5. If it is zero, then
+ // it is our first time through, and we should
+ // store away our A5. If not, then we are
+ // a real time manager callback, so we should
+ // store away A5, set up our local a5, jsr
+ // to our callback, and then restore the
+ // previous A5.
+
+ lea LocalA5, a0
+ move.l (a0), d0
+ cmpi.l #0, d0
+ bne TimerProc
+
+ move.l a5, (a0)
+ rts
+
+TimerProc:
+
+ // Save A5, restore our local A5
+
+ move.l a5, -(sp)
+ move.l d0, a5
+
+ // Jump to our C routine
+
+ move.l a1, -(sp)
+ jsr TimerCallback
+
+ // Restore the previous A5
+
+ move.l (sp)+, a5
+
+ rts
+
+LocalA5:
+
+ dc.l 0
+
+}
+
+#endif
+
+void _MD_StartInterrupts(void)
+{
+ gPrimaryThread = _PR_MD_CURRENT_THREAD();
+
+ // If we are not generating CFM-happy code, then make sure that
+ // we call our callback wrapper once so that we can
+ // save away our A5.
+
+#if !GENERATINGCFM
+ gTimerCallbackRD();
+#endif
+
+ // Fill in the Time Manager queue element
+
+ gTimeManagerTaskElem.tmAddr = (TimerUPP)&gTimerCallbackRD;
+ gTimeManagerTaskElem.tmCount = 0;
+ gTimeManagerTaskElem.tmWakeUp = 0;
+ gTimeManagerTaskElem.tmReserved = 0;
+
+ // Make sure that our time manager task is ready to go.
+ InsTime((QElemPtr)&gTimeManagerTaskElem);
+
+ PrimeTime((QElemPtr)&gTimeManagerTaskElem, kMacTimerInMiliSecs);
+}
+
+void _MD_StopInterrupts(void)
+{
+ if (gTimeManagerTaskElem.tmAddr != NULL) {
+ RmvTime((QElemPtr)&gTimeManagerTaskElem);
+ gTimeManagerTaskElem.tmAddr = NULL;
+ }
+}
+
+void _MD_PauseCPU(PRIntervalTime timeout)
+{
+#pragma unused (timeout)
+
+ unsigned long finalTicks;
+
+ if (timeout != PR_INTERVAL_NO_WAIT) {
+ Delay(1,&finalTicks);
+ (void) _MD_IOInterrupt();
+ }
+}
+
+
+//##############################################################################
+//##############################################################################
+#pragma mark -
+#pragma mark THREAD SUPPORT FUNCTIONS
+
+#include <OpenTransport.h> /* for error codes */
+
+PRStatus _MD_InitThread(PRThread *thread)
+{
+ thread->md.asyncIOLock = PR_NewLock();
+ PR_ASSERT(thread->md.asyncIOLock != NULL);
+ thread->md.asyncIOCVar = PR_NewCondVar(thread->md.asyncIOLock);
+ PR_ASSERT(thread->md.asyncIOCVar != NULL);
+
+ if (thread->md.asyncIOLock == NULL || thread->md.asyncIOCVar == NULL)
+ return PR_FAILURE;
+ else
+ return PR_SUCCESS;
+}
+
+PRStatus _MD_wait(PRThread *thread, PRIntervalTime timeout)
+{
+#pragma unused (timeout)
+
+ _MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+void WaitOnThisThread(PRThread *thread, PRIntervalTime timeout)
+{
+ intn is;
+ PRIntervalTime timein = PR_IntervalNow();
+ PRStatus status = PR_SUCCESS;
+
+ _PR_INTSOFF(is);
+ PR_Lock(thread->md.asyncIOLock);
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ while ((thread->io_pending) && (status == PR_SUCCESS))
+ status = PR_WaitCondVar(thread->md.asyncIOCVar, PR_INTERVAL_NO_TIMEOUT);
+ } else {
+ while ((thread->io_pending) && ((PRIntervalTime)(PR_IntervalNow() - timein) < timeout))
+ status = PR_WaitCondVar(thread->md.asyncIOCVar, timeout);
+ }
+ if ((status == PR_FAILURE) && (PR_GetError() == PR_PENDING_INTERRUPT_ERROR)) {
+ thread->md.osErrCode = kEINTRErr;
+ } else if (thread->io_pending) {
+ thread->md.osErrCode = kETIMEDOUTErr;
+ PR_SetError(PR_IO_TIMEOUT_ERROR, kETIMEDOUTErr);
+ }
+ PR_Unlock(thread->md.asyncIOLock);
+ _PR_FAST_INTSON(is);
+}
+
+void DoneWaitingOnThisThread(PRThread *thread)
+{
+ intn is;
+
+ _PR_INTSOFF(is);
+ PR_Lock(thread->md.asyncIOLock);
+ thread->io_pending = PR_FALSE;
+ /* let the waiting thread know that async IO completed */
+ PR_NotifyCondVar(thread->md.asyncIOCVar);
+ PR_Unlock(thread->md.asyncIOLock);
+ _PR_FAST_INTSON(is);
+}
+
+//##############################################################################
+//##############################################################################
+#pragma mark -
+#pragma mark PROCESS SUPPORT FUNCTIONS
+
+PRProcess * _MD_CreateProcess(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr)
+{
+#pragma unused (path, argv, envp, attr)
+
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr);
+ return NULL;
+}
+
+PRStatus _MD_DetachProcess(PRProcess *process)
+{
+#pragma unused (process)
+
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr);
+ return PR_FAILURE;
+}
+
+PRStatus _MD_WaitProcess(PRProcess *process, PRInt32 *exitCode)
+{
+#pragma unused (process, exitCode)
+
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr);
+ return PR_FAILURE;
+}
+
+PRStatus _MD_KillProcess(PRProcess *process)
+{
+#pragma unused (process)
+
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr);
+ return PR_FAILURE;
+}
diff --git a/pr/src/md/mac/mactime.c b/pr/src/md/mac/mactime.c
new file mode 100644
index 00000000..254e7b47
--- /dev/null
+++ b/pr/src/md/mac/mactime.c
@@ -0,0 +1,266 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include <OSUtils.h>
+#include <time.h>
+
+#include "primpl.h"
+
+PR_IMPLEMENT(UnsignedWide) dstLocalBaseMicroseconds;
+PR_IMPLEMENT(unsigned long) gJanuaryFirst1970Seconds;
+
+/*
+ * The geographic location and time zone information of a Mac
+ * are stored in extended parameter RAM. The ReadLocation
+ * produdure uses the geographic location record, MachineLocation,
+ * to read the geographic location and time zone information in
+ * extended parameter RAM.
+ *
+ * Because serial port and SLIP conflict with ReadXPram calls,
+ * we cache the call here.
+ *
+ * Caveat: this caching will give the wrong result if a session
+ * extend across the DST changeover time.
+ */
+
+static void MyReadLocation(MachineLocation *loc)
+{
+ static MachineLocation storedLoc;
+ static Boolean didReadLocation = false;
+
+ if (!didReadLocation) {
+ ReadLocation(&storedLoc);
+ didReadLocation = true;
+ }
+ *loc = storedLoc;
+}
+
+static long GMTDelta(void)
+{
+ MachineLocation loc;
+ long gmtDelta;
+
+ MyReadLocation(&loc);
+ gmtDelta = loc.u.gmtDelta & 0x00ffffff;
+ if (gmtDelta & 0x00800000) { /* test sign extend bit */
+ gmtDelta |= 0xff000000;
+ }
+ return gmtDelta;
+}
+
+void MacintoshInitializeTime(void)
+{
+ /*
+ * The NSPR epoch is midnight, Jan. 1, 1970 GMT.
+ *
+ * At midnight Jan. 1, 1970 GMT, the local time was
+ * midnight Jan. 1, 1970 + GMTDelta().
+ *
+ * Midnight Jan. 1, 1970 is 86400 * (365 * (1970 - 1904) + 17)
+ * = 2082844800 seconds since the Mac epoch.
+ * (There were 17 leap years from 1904 to 1970.)
+ *
+ * So the NSPR epoch is 2082844800 + GMTDelta() seconds since
+ * the Mac epoch. Whew! :-)
+ */
+ gJanuaryFirst1970Seconds = 2082844800 + GMTDelta();
+
+ /*
+ * Set up dstLocalBaseMicroseconds just for "prmjtime.c" for Mocha
+ * needs. The entire MOcha time needs to be rewritten using NSPR 2.0
+ * time.
+ */
+ {
+ UnsignedWide upTime;
+ unsigned long currentLocalTimeSeconds,
+ startupTimeSeconds;
+ uint64 startupTimeMicroSeconds;
+ uint32 upTimeSeconds;
+ uint64 oneMillion, upTimeSecondsLong, microSecondsToSeconds;
+
+ Microseconds(&upTime);
+
+ GetDateTime(&currentLocalTimeSeconds);
+
+ LL_I2L(microSecondsToSeconds, PR_USEC_PER_SEC);
+ LL_DIV(upTimeSecondsLong, *((uint64 *)&upTime), microSecondsToSeconds);
+ LL_L2I(upTimeSeconds, upTimeSecondsLong);
+
+ startupTimeSeconds = currentLocalTimeSeconds - upTimeSeconds;
+
+ startupTimeSeconds -= gJanuaryFirst1970Seconds;
+
+ // Now convert the startup time into a wide so that we
+ // can figure out GMT and DST.
+
+ LL_I2L(startupTimeMicroSeconds, startupTimeSeconds);
+ LL_I2L(oneMillion, PR_USEC_PER_SEC);
+ LL_MUL(dstLocalBaseMicroseconds, oneMillion, startupTimeMicroSeconds);
+ }
+}
+
+/*
+ *-----------------------------------------------------------------------
+ *
+ * PR_Now --
+ *
+ * Returns the current time in microseconds since the epoch.
+ * The epoch is midnight January 1, 1970 GMT.
+ * The implementation is machine dependent. This is the Mac
+ * Implementation.
+ * Cf. time_t time(time_t *tp)
+ *
+ *-----------------------------------------------------------------------
+ */
+
+PRTime PR_Now(void)
+{
+ unsigned long currentTime; /* unsigned 32-bit integer, ranging
+ from midnight Jan. 1, 1904 to
+ 6:28:15 AM Feb. 6, 2040 */
+ PRTime retVal;
+ int64 usecPerSec;
+
+ /*
+ * Get the current time expressed as the number of seconds
+ * elapsed since the Mac epoch, midnight, Jan. 1, 1904 (local time).
+ * On a Mac, current time accuracy is up to a second.
+ */
+ GetDateTime(&currentTime);
+
+ /*
+ * Express the current time relative to the NSPR epoch,
+ * midnight, Jan. 1, 1970 GMT.
+ *
+ * At midnight Jan. 1, 1970 GMT, the local time was
+ * midnight Jan. 1, 1970 + GMTDelta().
+ *
+ * Midnight Jan. 1, 1970 is 86400 * (365 * (1970 - 1904) + 17)
+ * = 2082844800 seconds since the Mac epoch.
+ * (There were 17 leap years from 1904 to 1970.)
+ *
+ * So the NSPR epoch is 2082844800 + GMTDelta() seconds since
+ * the Mac epoch. Whew! :-)
+ */
+ currentTime = currentTime - 2082844800 - GMTDelta();
+
+ /* Convert seconds to microseconds */
+ LL_I2L(usecPerSec, PR_USEC_PER_SEC);
+ LL_I2L(retVal, currentTime);
+ LL_MUL(retVal, retVal, usecPerSec);
+
+ return retVal;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * PR_LocalTimeParameters --
+ *
+ * returns the time parameters for the local time zone
+ *
+ * This is the machine-dependent implementation for Mac.
+ *
+ * Caveat: On a Mac, we only know the GMT and DST offsets for
+ * the current time, not for the time in question.
+ * Mac has no support for DST handling.
+ * DST changeover is all manually set by the user.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+PRTimeParameters PR_LocalTimeParameters(const PRExplodedTime *gmt)
+{
+#pragma unused (gmt)
+
+ PRTimeParameters retVal;
+ MachineLocation loc;
+
+ MyReadLocation(&loc);
+
+ /*
+ * On a Mac, the GMT value is in seconds east of GMT. For example,
+ * San Francisco is at -28,800 seconds (8 hours * 3600 seconds per hour)
+ * east of GMT. The gmtDelta field is a 3-byte value contained in a
+ * long word, so you must take care to get it properly.
+ */
+
+ retVal.tp_gmt_offset = loc.u.gmtDelta & 0x00ffffff;
+ if (retVal.tp_gmt_offset & 0x00800000) { /* test sign extend bit */
+ retVal.tp_gmt_offset |= 0xff000000;
+ }
+
+ /*
+ * The daylight saving time value, dlsDelta, is a signed byte
+ * value representing the offset for the hour field -- whether
+ * to add 1 hour, subtract 1 hour, or make no change at all.
+ */
+
+ if (loc.u.dlsDelta) {
+ retVal.tp_gmt_offset -= 3600;
+ retVal.tp_dst_offset = 3600;
+ } else {
+ retVal.tp_dst_offset = 0;
+ }
+ return retVal;
+}
+
+PRIntervalTime _MD_GetInterval(void)
+{
+ PRIntervalTime retVal;
+ PRUint64 upTime, micrototimer;
+
+ /*
+ * Use the Microseconds procedure to obtain the number of
+ * microseconds elapsed since system startup time.
+ */
+ Microseconds((UnsignedWide *)&upTime);
+ LL_I2L(micrototimer, (kMacTimerInMiliSecs * PR_MSEC_PER_SEC));
+ LL_DIV(upTime, upTime, micrototimer);
+ LL_L2I(retVal, upTime);
+
+ return retVal;
+}
+
+struct tm *Maclocaltime(const time_t * t)
+{
+ DateTimeRec dtr;
+ MachineLocation loc;
+ time_t macLocal = *t + gJanuaryFirst1970Seconds; /* GMT Mac */
+ static struct tm statictime;
+ static const short monthday[12] =
+ {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
+
+ SecondsToDate(macLocal, &dtr);
+ statictime.tm_sec = dtr.second;
+ statictime.tm_min = dtr.minute;
+ statictime.tm_hour = dtr.hour;
+ statictime.tm_mday = dtr.day;
+ statictime.tm_mon = dtr.month - 1;
+ statictime.tm_year = dtr.year - 1900;
+ statictime.tm_wday = dtr.dayOfWeek - 1;
+ statictime.tm_yday = monthday[statictime.tm_mon]
+ + statictime.tm_mday - 1;
+ if (2 < statictime.tm_mon && !(statictime.tm_year & 3))
+ ++statictime.tm_yday;
+ MyReadLocation(&loc);
+ statictime.tm_isdst = loc.u.dlsDelta;
+ return(&statictime);
+}
+
+
diff --git a/pr/src/md/mac/mdmac.c b/pr/src/md/mac/mdmac.c
new file mode 100644
index 00000000..510d9b17
--- /dev/null
+++ b/pr/src/md/mac/mdmac.c
@@ -0,0 +1,842 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include <Types.h>
+#include <Timer.h>
+#include <Files.h>
+#include <Errors.h>
+#include <Folders.h>
+#include <Events.h>
+#include <Processes.h>
+#include <TextUtils.h>
+#include <MixedMode.h>
+#include <LowMem.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <stat.h>
+#include <stdarg.h>
+#include <unix.h>
+
+#include "MacErrorHandling.h"
+
+#include "primpl.h"
+#include "prgc.h"
+#include "MacMemAllocator.h"
+
+enum {
+ kGarbageCollectionEmergencyFundSize = 16 * 1024,
+ kGarbageCollectionMemTightFundSize = 48 * 1024
+};
+
+enum {
+ uppExitToShellProcInfo = kPascalStackBased,
+ uppStackSpaceProcInfo = kRegisterBased
+ | RESULT_SIZE(SIZE_CODE(sizeof(long)))
+ | REGISTER_RESULT_LOCATION(kRegisterD0)
+ | REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(UInt16)))
+};
+
+
+#define UNIMPLEMENTED_ROUTINE \
+ DebugStr("\pNot Implemented Yet"); \
+ return 0;
+
+//
+// Local routines
+//
+void PStrFromCStr(const char *, Str255);
+unsigned char GarbageCollectorCacheFlusher(PRUint32 size);
+
+extern PRThread *gPrimaryThread;
+
+pascal void ExitToShellPatch(void);
+
+UniversalProcPtr gExitToShellPatchCallThru = NULL;
+#if GENERATINGCFM
+RoutineDescriptor gExitToShellPatchRD = BUILD_ROUTINE_DESCRIPTOR(uppExitToShellProcInfo, &ExitToShellPatch);
+#else
+#define gExitToShellPatchRD ExitToShellPatch
+#endif
+
+UniversalProcPtr gStackSpacePatchCallThru = NULL;
+#if GENERATINGCFM
+pascal long StackSpacePatch(UInt16);
+RoutineDescriptor StackSpacePatchRD = BUILD_ROUTINE_DESCRIPTOR(uppStackSpaceProcInfo, &StackSpacePatch);
+#else
+pascal long StackSpacePatch();
+asm pascal long StackSpacePatchGlue();
+#endif
+
+
+//##############################################################################
+//##############################################################################
+#pragma mark -
+#pragma mark CREATING MACINTOSH THREAD STACKS
+
+
+#if !GENERATINGCFM
+
+asm long pascal StackSpacePatchCallThruGlue(long theAddress)
+{
+ move.l 4(sp), a0
+ jsr (a0)
+ move.l (sp)+, a0
+ add #0x8, sp
+ move.l d0, -(sp)
+ jmp (a0)
+}
+
+asm pascal long StackSpacePatchGlue()
+{
+
+ // Check out LocalA5. If it is zero, then
+ // it is our first time through, and we should
+ // store away our A5. If not, then we are
+ // a real time manager callback, so we should
+ // store away A5, set up our local a5, jsr
+ // to our callback, and then restore the
+ // previous A5.
+
+ lea LocalA5, a0
+ move.l (a0), d0
+ cmpi.l #0, d0
+ bne DoStackSpace
+
+ move.l a5, (a0)
+ rts
+
+DoStackSpace:
+
+ // Save A5, restore our local A5
+
+ move.l a5, -(sp)
+ move.l d0, a5
+
+ // Jump to our C routine
+
+ clr.l -(sp)
+ jsr StackSpacePatch
+ move.l (sp)+, d0
+
+ // Restore the previous A5
+
+ move.l (sp)+, a5
+
+ rts
+
+LocalA5:
+
+ dc.l 0
+
+}
+
+#endif
+
+#if GENERATINGCFM
+pascal long StackSpacePatch(UInt16 trapNo)
+#else
+pascal long StackSpacePatch()
+#endif
+{
+ char tos;
+ PRThread *thisThread;
+
+ thisThread = PR_CurrentThread();
+
+ // If we are the primary thread, then call through to the
+ // good ol' fashion stack space implementation. Otherwise,
+ // compute it by hand.
+ if ((thisThread == gPrimaryThread) ||
+ (&tos < thisThread->stack->stackBottom) ||
+ (&tos > thisThread->stack->stackTop)) {
+#if GENERATINGCFM
+ return CallOSTrapUniversalProc(gStackSpacePatchCallThru, uppStackSpaceProcInfo, trapNo);
+#else
+ return StackSpacePatchCallThruGlue((long)gStackSpacePatchCallThru);
+#endif
+ }
+ else {
+ return &tos - thisThread->stack->stackBottom;
+ }
+}
+
+
+//##############################################################################
+//##############################################################################
+#pragma mark -
+#pragma mark ENVIRONMENT VARIABLES
+
+
+typedef struct EnvVariable EnvVariable;
+
+struct EnvVariable {
+ char *variable;
+ char *value;
+ EnvVariable *next;
+};
+
+EnvVariable *gEnvironmentVariables = NULL;
+
+char *_MD_GetEnv(const char *name)
+{
+ EnvVariable *currentVariable = gEnvironmentVariables;
+
+ while (currentVariable) {
+ if (!strcmp(currentVariable->variable, name))
+ return currentVariable->value;
+
+ currentVariable = currentVariable->next;
+ }
+
+ return NULL;
+}
+
+PR_IMPLEMENT(int)
+_MD_PutEnv(const char *string)
+{
+ EnvVariable *currentVariable = gEnvironmentVariables;
+ char *variableCopy,
+ *value,
+ *current;
+
+ variableCopy = strdup(string);
+ PR_ASSERT(variableCopy != NULL);
+
+ current = variableCopy;
+ while (*current != '=')
+ current++;
+
+ *current = 0;
+ current++;
+
+ value = current;
+
+ while (currentVariable) {
+ if (!strcmp(currentVariable->variable, variableCopy))
+ break;
+
+ currentVariable = currentVariable->next;
+ }
+
+ if (currentVariable == NULL) {
+ currentVariable = PR_NEW(EnvVariable);
+
+ if (currentVariable == NULL) {
+ PR_DELETE(variableCopy);
+ return PR_FALSE;
+ }
+
+ currentVariable->variable = strdup(variableCopy);
+ currentVariable->value = strdup(value);
+ currentVariable->next = gEnvironmentVariables;
+ gEnvironmentVariables = currentVariable;
+ }
+
+ else {
+ PR_DELETE(currentVariable->value);
+ currentVariable->value = strdup(current);
+
+ /* This is a temporary hack. Working on a real fix, remove this when done. */
+ /* OK, there are two ways to access the */
+ /* library path, getenv() and PR_GetLibraryPath(). Take a look at PR_GetLibraryPath(). */
+ /* You'll see that we keep the path in a global which is intialized at startup from */
+ /* a call to getenv(). From then on, they have nothing in common. */
+ /* We need to keep them in synch. */
+ if (strcmp(currentVariable->variable, "LD_LIBRARY_PATH") == 0)
+ PR_SetLibraryPath(currentVariable->value);
+ }
+
+ PR_DELETE(variableCopy);
+ return PR_TRUE;
+}
+
+
+
+//##############################################################################
+//##############################################################################
+#pragma mark -
+#pragma mark MISCELLANEOUS
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ if (isCurrent) {
+ (void) setjmp(t->md.jb);
+ }
+ *np = sizeof(t->md.jb) / sizeof(PRUint32);
+ return (PRWord*) (t->md.jb);
+}
+
+void _MD_GetRegisters(PRUint32 *to)
+{
+ (void) setjmp((void*) to);
+}
+
+void _MD_EarlyInit()
+{
+ Handle environmentVariables;
+
+#if !defined(MAC_NSPR_STANDALONE)
+ // MacintoshInitializeMemory(); Moved to mdmacmem.c: AllocateRawMemory(Size blockSize)
+#else
+ MacintoshInitializeMemory();
+#endif
+ MacintoshInitializeTime();
+
+ // Install resource-controlled environment variables.
+
+ environmentVariables = GetResource('Envi', 128);
+ if (environmentVariables != NULL) {
+
+ Size resourceSize;
+ char *currentPutEnvString = (char *)*environmentVariables,
+ *currentScanChar = currentPutEnvString;
+
+ resourceSize = GetHandleSize(environmentVariables);
+ DetachResource(environmentVariables);
+ HLock(environmentVariables);
+
+ while (resourceSize--) {
+
+ if ((*currentScanChar == '\n') || (*currentScanChar == '\r')) {
+ *currentScanChar = 0;
+ _MD_PutEnv (currentPutEnvString);
+ currentPutEnvString = currentScanChar + 1;
+ }
+
+ currentScanChar++;
+
+ }
+
+ DisposeHandle(environmentVariables);
+
+ }
+
+#ifdef PR_INTERNAL_LOGGING
+ _MD_PutEnv ("NSPR_LOG_MODULES=clock:6,cmon:6,io:6,mon:6,linker:6,cvar:6,sched:6,thread:6");
+#endif
+
+#if GENERATINGCFM
+ gStackSpacePatchCallThru = GetOSTrapAddress(0x0065);
+ SetOSTrapAddress((UniversalProcPtr)&StackSpacePatchRD, 0x0065);
+ {
+ long foo;
+ foo = StackSpace();
+ }
+#else
+ gStackSpacePatchCallThru = GetOSTrapAddress(0x0065);
+ SetOSTrapAddress((UniversalProcPtr)&StackSpacePatchGlue, 0x0065);
+ StackSpace();
+#endif
+
+ // THIS IS VERY IMPORTANT. Install our ExitToShell patch.
+ // This allows us to deactivate our Time Mananger task even
+ // if we are not totally gracefully exited. If this is not
+ // done then we will randomly crash at later times when the
+ // task is called after the app heap is gone.
+
+ gExitToShellPatchCallThru = GetToolboxTrapAddress(0x01F4);
+ SetToolboxTrapAddress((UniversalProcPtr)&gExitToShellPatchRD, 0x01F4);
+
+}
+
+void _MD_FinalInit()
+{
+ _MD_InitNetAccess();
+}
+
+void PR_InitMemory(void) {
+#ifndef NSPR_AS_SHARED_LIB
+ // Needed for Mac browsers without Java. We donÕt want them calling PR_INIT, since it
+ // brings in all of the thread support. But we do need to allow them to initialize
+ // the NSPR memory package.
+ // This should go away when all clients of the NSPR want threads AND memory.
+ MacintoshInitializeMemory();
+#endif
+}
+
+//##############################################################################
+//##############################################################################
+#pragma mark -
+#pragma mark TERMINATION
+
+typedef pascal void (* ExitToShellProc)(void);
+
+// THIS IS *** VERY *** IMPORTANT... our ExitToShell patch.
+// This allows us to deactivate our Time Mananger task even
+// if we are not totally gracefully exited. If this is not
+// done then we will randomly crash at later times when the
+// task is called after the app heap is gone.
+
+extern TMTask gTimeManagerTaskElem;
+
+pascal void ExitToShellPatch(void)
+{
+ _MD_StopInterrupts();
+
+ CloseOpenTransport();
+
+#if GENERATINGCFM
+ CallUniversalProc(gExitToShellPatchCallThru, uppExitToShellProcInfo);
+#else
+ {
+ ExitToShellProc *exitProc = (ExitToShellProc *)&gExitToShellPatchCallThru;
+ (*exitProc)();
+ }
+#endif
+
+}
+
+
+
+//##############################################################################
+//##############################################################################
+#pragma mark -
+#pragma mark STRING OPERATIONS
+
+#if !defined(MAC_NSPR_STANDALONE)
+
+// PStrFromCStr converts the source C string to a destination
+// pascal string as it copies. The dest string will
+// be truncated to fit into an Str255 if necessary.
+// If the C String pointer is NULL, the pascal string's length is set to zero
+//
+extern void
+PStrFromCStr(const char* src, Str255 dst)
+{
+ short length = 0;
+
+ // handle case of overlapping strings
+ if ( (void*)src == (void*)dst )
+ {
+ unsigned char* curdst = &dst[1];
+ unsigned char thisChar;
+
+ thisChar = *(const unsigned char*)src++;
+ while ( thisChar != '\0' )
+ {
+ unsigned char nextChar;
+
+ // use nextChar so we don't overwrite what we are about to read
+ nextChar = *(const unsigned char*)src++;
+ *curdst++ = thisChar;
+ thisChar = nextChar;
+
+ if ( ++length >= 255 )
+ break;
+ }
+ }
+ else if ( src != NULL )
+ {
+ unsigned char* curdst = &dst[1];
+ short overflow = 255; // count down so test it loop is faster
+ register char temp;
+
+ // Can't do the K&R C thing of Òwhile (*s++ = *t++)Ó because it will copy trailing zero
+ // which might overrun pascal buffer. Instead we use a temp variable.
+ while ( (temp = *src++) != 0 )
+ {
+ *(char*)curdst++ = temp;
+
+ if ( --overflow <= 0 )
+ break;
+ }
+ length = 255 - overflow;
+ }
+ dst[0] = length;
+}
+
+
+void CStrFromPStr(ConstStr255Param pString, char **cString)
+{
+ // Allocates a cString and copies a Pascal string into it.
+ unsigned int len;
+
+ len = pString[0];
+ *cString = malloc(len+1);
+
+ if (*cString != NULL) {
+ strncpy(*cString, (char *)&pString[1], len);
+ (*cString)[len] = NULL;
+ }
+}
+
+
+void dprintf(const char *format, ...)
+{
+#if DEBUG
+ va_list ap;
+ char *buffer;
+
+ va_start(ap, format);
+ buffer = PR_vsmprintf(format, ap);
+ va_end(ap);
+
+ c2pstr(buffer);
+ DebugStr( (unsigned char *)buffer);
+ free(buffer);
+#endif /* DEBUG */
+}
+
+#else
+
+void debugstr(const char *debuggerMsg)
+{
+ Str255 pStr;
+
+ PStrFromCStr(debuggerMsg, pStr);
+ DebugStr(pStr);
+}
+
+
+char *strdup(const char *source)
+{
+ char *newAllocation;
+ size_t stringLength;
+
+#ifdef DEBUG
+ PR_ASSERT(source);
+#endif
+
+ stringLength = strlen(source) + 1;
+
+ newAllocation = (char *)PR_MALLOC(stringLength);
+ if (newAllocation == NULL)
+ return NULL;
+ BlockMoveData(source, newAllocation, stringLength);
+ return newAllocation;
+}
+
+// PStrFromCStr converts the source C string to a destination
+// pascal string as it copies. The dest string will
+// be truncated to fit into an Str255 if necessary.
+// If the C String pointer is NULL, the pascal string's length is set to zero
+//
+void PStrFromCStr(const char* src, Str255 dst)
+{
+ short length = 0;
+
+ // handle case of overlapping strings
+ if ( (void*)src == (void*)dst )
+ {
+ unsigned char* curdst = &dst[1];
+ unsigned char thisChar;
+
+ thisChar = *(const unsigned char*)src++;
+ while ( thisChar != '\0' )
+ {
+ unsigned char nextChar;
+
+ // use nextChar so we don't overwrite what we are about to read
+ nextChar = *(const unsigned char*)src++;
+ *curdst++ = thisChar;
+ thisChar = nextChar;
+
+ if ( ++length >= 255 )
+ break;
+ }
+ }
+ else if ( src != NULL )
+ {
+ unsigned char* curdst = &dst[1];
+ short overflow = 255; // count down so test it loop is faster
+ register char temp;
+
+ // Can't do the K&R C thing of Òwhile (*s++ = *t++)Ó because it will copy trailing zero
+ // which might overrun pascal buffer. Instead we use a temp variable.
+ while ( (temp = *src++) != 0 )
+ {
+ *(char*)curdst++ = temp;
+
+ if ( --overflow <= 0 )
+ break;
+ }
+ length = 255 - overflow;
+ }
+ dst[0] = length;
+}
+
+
+void CStrFromPStr(ConstStr255Param pString, char **cString)
+{
+ // Allocates a cString and copies a Pascal string into it.
+ unsigned int len;
+
+ len = pString[0];
+ *cString = PR_MALLOC(len+1);
+
+ if (*cString != NULL) {
+ strncpy(*cString, (char *)&pString[1], len);
+ (*cString)[len] = NULL;
+ }
+}
+
+
+size_t strlen(const char *source)
+{
+ size_t currentLength = 0;
+
+ if (source == NULL)
+ return currentLength;
+
+ while (*source++ != '\0')
+ currentLength++;
+
+ return currentLength;
+}
+
+int strcmpcore(const char *str1, const char *str2, int caseSensitive)
+{
+ char currentChar1, currentChar2;
+
+ while (1) {
+
+ currentChar1 = *str1;
+ currentChar2 = *str2;
+
+ if (!caseSensitive) {
+
+ if ((currentChar1 >= 'a') && (currentChar1 <= 'z'))
+ currentChar1 += ('A' - 'a');
+
+ if ((currentChar2 >= 'a') && (currentChar2 <= 'z'))
+ currentChar2 += ('A' - 'a');
+
+ }
+
+ if (currentChar1 == '\0')
+ break;
+
+ if (currentChar1 != currentChar2)
+ return currentChar1 - currentChar2;
+
+ str1++;
+ str2++;
+
+ }
+
+ return currentChar1 - currentChar2;
+}
+
+int strcmp(const char *str1, const char *str2)
+{
+ return strcmpcore(str1, str2, true);
+}
+
+int strcasecmp(const char *str1, const char *str2)
+{
+ return strcmpcore(str1, str2, false);
+}
+
+#if GENERATING68K
+asm void *memset(void *target, int pattern, size_t length) // Legal asm qualifier
+{
+ MOVEA.L 4(SP),A0 // target -> A0
+ MOVE.W 10(SP),D0 // pattern -> D0, length -> D1
+ MOVE.L 12(SP),D1
+ CMPI.L #30,D1
+ BLT end
+
+ // Fill D0 with the pattern
+ MOVEQ #0,D2 // Clear D2, weÕll use it as scratch
+ MOVE.B D0,D2 // Fill the bottom byte
+ LSL.W #8,D0 //
+ OR.W D0,D2
+ MOVE.L D2,D0
+ SWAP D2
+ OR.L D2,D0
+
+ // Are we odd aligned?
+ MOVE.L A0,D2 // Copy target address into scratch
+ LSR.B #1,D2 // Sets C bit
+ BCC.S checkAlign2 // If even, check for 16-byte alignment
+ MOVE.B D0,(A0)+ // Take care of odd byte
+ SUBQ.L #1,D1 // Update length
+
+ // Are we odd 16-byte word alligned?
+checkAlign2: LSR.B #1,D2 // Still set from last check
+ BCC totallyAligned
+ MOVE.W D0,(A0)+
+ SUBQ.L #2,D1
+
+totallyAligned: MOVE.L D1,D2
+ LSR.L #4,D2
+ SUBQ.L #1,D2
+copyHunk: MOVE.L D0,(A0)+
+ MOVE.L D0,(A0)+
+ MOVE.L D0,(A0)+
+ MOVE.L D0,(A0)+
+ SUBQ.L #1,D2
+ BCC copyHunk
+ ANDI.W #15,D1 // Check done?
+ BRA end
+dribble: MOVE.B D0,(A0)+
+end: DBF D1,dribble
+ MOVE.L 4(SP),D0 // Return the target
+ RTS
+}
+#endif
+
+void *memcpy(void *to, const void *from, size_t size)
+{
+ if (size != 0) {
+#if DEBUG
+ if ((UInt32)to < 0x1000)
+ DebugStr("\pmemcpy has illegal to argument");
+ if ((UInt32)from < 0x1000)
+ DebugStr("\pmemcpy has illegal from argument");
+#endif
+ BlockMoveData(from, to, size);
+ }
+ return to;
+}
+
+void dprintf(const char *format, ...)
+{
+ va_list ap;
+ char *buffer;
+
+ va_start(ap, format);
+ buffer = (char *)PR_vsmprintf(format, ap);
+ va_end(ap);
+
+ debugstr(buffer);
+ PR_DELETE(buffer);
+}
+
+void
+exit(int result)
+{
+#pragma unused (result)
+
+ ExitToShell();
+}
+
+void abort(void)
+{
+ exit(-1);
+}
+
+#endif
+
+//##############################################################################
+//##############################################################################
+#pragma mark -
+#pragma mark FLUSHING THE GARBAGE COLLECTOR
+
+#if !defined(MAC_NSPR_STANDALONE)
+
+unsigned char GarbageCollectorCacheFlusher(PRUint32)
+{
+
+ PRIntn is;
+
+ UInt32 oldPriority;
+
+ // If java wasn't completely initialized, then bail
+ // harmlessly.
+
+ if (PR_GetGCInfo()->lock == NULL)
+ return false;
+
+#if DEBUG
+ if (_MD_GET_INTSOFF() == 1)
+ DebugStr("\pGarbageCollectorCacheFlusher at interrupt time!");
+#endif
+
+ // The synchronization here is very tricky. We really
+ // don't want any other threads to run while we are
+ // cleaning up the gc heap... they could call malloc,
+ // and then we would be in trouble in a big way. So,
+ // we jack up our priority and that of the finalizer
+ // so that we won't yield to other threads.
+ // dkc 5/17/96
+
+ oldPriority = PR_GetThreadPriority(PR_GetCurrentThread());
+ _PR_INTSOFF(is);
+ _PR_SetThreadPriority(PR_GetCurrentThread(), (PRThreadPriority)30);
+ _PR_INTSON(is);
+
+ // Garbage collect twice. This will finalize any
+ // dangling AWT resources (images, components), and
+ // then free up their GC space, too.
+ // dkc 2/15/96
+ // interrupts must be on during PR_GC
+
+ PR_GC();
+
+ // By setting the finalizer priority to 31, then we
+ // ensure it will run before us. When it finishes
+ // its list of finalizations, it returns to us
+ // for the second garbage collection.
+
+ PR_Yield();
+
+ PR_GC();
+
+ // Restore our old priorities.
+
+ _PR_INTSOFF(is);
+ _PR_SetThreadPriority(PR_GetCurrentThread(), (PRThreadPriority)oldPriority);
+ _PR_INTSON(is);
+
+ return false;
+}
+
+#endif
+
+//##############################################################################
+//##############################################################################
+#pragma mark -
+#pragma mark MISCELLANEOUS-HACKS
+
+
+//
+// ***** HACK FIX THESE ****
+//
+extern long _MD_GetOSName(char *buf, long count)
+{
+ long len;
+
+ len = PR_snprintf(buf, count, "Mac OS");
+
+ return 0;
+}
+
+extern long _MD_GetOSVersion(char *buf, long count)
+{
+ long len;
+
+ len = PR_snprintf(buf, count, "7.5");
+
+ return 0;
+}
+
+extern long _MD_GetArchitecture(char *buf, long count)
+{
+ long len;
+
+#if defined(GENERATINGPOWERPC) && GENERATINGPOWERPC
+ len = PR_snprintf(buf, count, "PowerPC");
+#else
+ len = PR_snprintf(buf, count, "Motorola68k");
+#endif
+
+ return 0;
+}
diff --git a/pr/src/md/mac/prcpucfg.h b/pr/src/md/mac/prcpucfg.h
new file mode 100644
index 00000000..88c9797f
--- /dev/null
+++ b/pr/src/md/mac/prcpucfg.h
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_MAC
+#define XP_MAC
+#endif
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_BYTES_PER_BYTE 1L
+#define PR_BYTES_PER_SHORT 2L
+#define PR_BYTES_PER_INT 4L
+#define PR_BYTES_PER_INT64 8L
+#define PR_BYTES_PER_LONG 4L
+#define PR_BYTES_PER_FLOAT 4L
+#define PR_BYTES_PER_DOUBLE 8L
+#define PR_BYTES_PER_WORD 4L
+#define PR_BYTES_PER_DWORD 8L
+
+#define PR_BITS_PER_BYTE 8L
+#define PR_BITS_PER_SHORT 16L
+#define PR_BITS_PER_INT 32L
+#define PR_BITS_PER_INT64 64L
+#define PR_BITS_PER_LONG 32L
+#define PR_BITS_PER_FLOAT 32L
+#define PR_BITS_PER_DOUBLE 64L
+#define PR_BITS_PER_WORD 32L
+
+#define PR_BITS_PER_BYTE_LOG2 3L
+#define PR_BITS_PER_SHORT_LOG2 4L
+#define PR_BITS_PER_INT_LOG2 5L
+#define PR_BITS_PER_INT64_LOG2 6L
+#define PR_BITS_PER_LONG_LOG2 5L
+#define PR_BITS_PER_FLOAT_LOG2 5L
+#define PR_BITS_PER_DOUBLE_LOG2 6L
+#define PR_BITS_PER_WORD_LOG2 5L
+
+#define PR_ALIGN_OF_SHORT 2L
+#define PR_ALIGN_OF_INT 4L
+#define PR_ALIGN_OF_LONG 4L
+#define PR_ALIGN_OF_INT64 2L
+#define PR_ALIGN_OF_FLOAT 4L
+#define PR_ALIGN_OF_DOUBLE 4L
+#define PR_ALIGN_OF_POINTER 4L
+#define PR_ALIGN_OF_WORD 4L
+
+#define PR_BYTES_PER_WORD_LOG2 2L
+#define PR_BYTES_PER_DWORD_LOG2 3L
+#define PR_WORDS_PER_DWORD_LOG2 1L
+
+#define _PR_LOCAL_THREADS_ONLY
+
+#ifndef NO_NSPR_10_SUPPORT
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+#endif NO_NSPR_10_SUPPORT
+
+#endif /* nspr_cpucfg___ */
diff --git a/pr/src/md/os2/Makefile b/pr/src/md/os2/Makefile
new file mode 100644
index 00000000..1b23db00
--- /dev/null
+++ b/pr/src/md/os2/Makefile
@@ -0,0 +1,51 @@
+#
+# The contents of this file are subject to the Netscape Public License
+# Version 1.0 (the "NPL"); you may not use this file except in
+# compliance with the NPL. You may obtain a copy of the NPL at
+# http://www.mozilla.org/NPL/
+#
+# Software distributed under the NPL is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+# for the specific language governing rights and limitations under the
+# NPL.
+#
+# The Initial Developer of this code under the NPL is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1998 Netscape Communications Corporation. All Rights
+# Reserved.
+#
+
+#! gmake
+
+MOD_DEPTH = ../../../..
+
+include $(MOD_DEPTH)/config/config.mk
+
+ifeq ($(OS_TARGET), OS2)
+CSRCS = \
+ os2misc.c \
+ os2sem.c \
+ os2inrval.c \
+ os2gc.c \
+ os2thred.c \
+ os2io.c \
+ os2cv.c \
+ os2sock.c \
+ os2_errors.c \
+ os2poll.c \
+ $(NULL)
+endif
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(DIST)/include/private -I$(DIST)/include
+
+include $(MOD_DEPTH)/config/rules.mk
+
+export:: $(TARGETS)
+ @cmd /C "copy *.obj $(OBJDIR)\*.o > nul"
+
+install:: export
+
+
+
diff --git a/pr/src/md/os2/os2_errors.c b/pr/src/md/os2/os2_errors.c
new file mode 100644
index 00000000..33f1a062
--- /dev/null
+++ b/pr/src/md/os2/os2_errors.c
@@ -0,0 +1,1053 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "prerror.h"
+#include "primpl.h"
+
+void _MD_os2_map_opendir_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ case ERROR_INVALID_ACCESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_INVALID_NAME:
+ case ERROR_INVALID_PARAMETER:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ case ERROR_NOT_DOS_DISK:
+ case ERROR_NOT_READY:
+ case ERROR_OPEN_FAILED:
+ case ERROR_PATH_BUSY:
+ case ERROR_CANNOT_MAKE:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_DEVICE_IN_USE:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_SHARING_BUFFER_EXCEEDED:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_closedir_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_ACCESS_DENIED:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_readdir_error(PRInt32 err)
+{
+
+ switch (err) {
+ case ERROR_NO_MORE_FILES:
+ PR_SetError(PR_NO_MORE_FILES_ERROR, err);
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NOT_DOS_DISK:
+ case ERROR_LOCK_VIOLATION:
+ case ERROR_BROKEN_PIPE:
+ case ERROR_NOT_READY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_delete_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+/* The error code for stat() is in errno. */
+void _MD_os2_map_stat_error(PRInt32 err)
+{
+ switch (err) {
+ case ENOENT:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ }
+}
+
+void _MD_os2_map_fstat_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_rename_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_INVALID_NAME:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_EXISTS:
+ PR_SetError(PR_FILE_EXISTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+/* The error code for access() is in errno. */
+void _MD_os2_map_access_error(PRInt32 err)
+{
+ switch (err) {
+ case ENOENT:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ }
+}
+
+void _MD_os2_map_mkdir_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_EXISTS:
+ PR_SetError(PR_FILE_EXISTS_ERROR, err);
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_INVALID_NAME:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_DISK_FULL:
+ case ERROR_HANDLE_DISK_FULL:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_rmdir_error(PRInt32 err)
+{
+
+ switch (err) {
+ case ERROR_FILE_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_INVALID_NAME:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_read_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_transmitfile_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_write_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ case ERROR_DISK_FULL:
+ case ERROR_HANDLE_DISK_FULL:
+ case ENOSPC:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case EMSGSIZE:
+ case EINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case EISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_lseek_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_SEEK_ON_DEVICE:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_fsync_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_DISK_FULL:
+ case ERROR_HANDLE_DISK_FULL:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_close_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_INVALID_HANDLE:
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_socket_error(PRInt32 err)
+{
+ switch (err) {
+ case EPROTONOSUPPORT:
+ PR_SetError(PR_PROTOCOL_NOT_SUPPORTED_ERROR, err);
+ break;
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ case ENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_recv_error(PRInt32 err)
+{
+ switch (err) {
+ case EWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_recvfrom_error(PRInt32 err)
+{
+ switch (err) {
+ case EWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_send_error(PRInt32 err)
+{
+ switch (err) {
+ case EWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case EMSGSIZE:
+ case EINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case EISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_sendto_error(PRInt32 err)
+{
+ switch (err) {
+ case EWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case EMSGSIZE:
+ case EINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case EISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_accept_error(PRInt32 err)
+{
+ switch (err) {
+ case EWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case EOPNOTSUPP:
+ PR_SetError(PR_NOT_TCP_SOCKET_ERROR, err);
+ break;
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EMFILE:
+ PR_SetError(PR_PROC_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ENOBUFS:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_acceptex_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_connect_error(PRInt32 err)
+{
+ switch (err) {
+ case EWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case EINPROGRESS:
+ case EALREADY:
+ case EINVAL:
+ PR_SetError(PR_ALREADY_INITIATED_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case EADDRNOTAVAIL:
+ PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case EAFNOSUPPORT:
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err);
+ break;
+ case ETIMEDOUT:
+ PR_SetError(PR_IO_TIMEOUT_ERROR, err);
+ break;
+ case ECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case ENETUNREACH:
+ PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, err);
+ break;
+ case EADDRINUSE:
+ PR_SetError(PR_ADDRESS_IN_USE_ERROR, err);
+ break;
+ case EISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_bind_error(PRInt32 err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EADDRNOTAVAIL:
+ PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err);
+ break;
+ case EADDRINUSE:
+ PR_SetError(PR_ADDRESS_IN_USE_ERROR, err);
+ break;
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case EINVAL:
+ PR_SetError(PR_SOCKET_ADDRESS_IS_BOUND_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_listen_error(PRInt32 err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case EOPNOTSUPP:
+ PR_SetError(PR_NOT_TCP_SOCKET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_shutdown_error(PRInt32 err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case ENOTCONN:
+ PR_SetError(PR_NOT_CONNECTED_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_getsockname_error(PRInt32 err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_getpeername_error(PRInt32 err)
+{
+
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case ENOTCONN:
+ PR_SetError(PR_NOT_CONNECTED_ERROR, err);
+ break;
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_getsockopt_error(PRInt32 err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case ENOPROTOOPT:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EINVAL:
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_setsockopt_error(PRInt32 err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case ENOPROTOOPT:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EINVAL:
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_open_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_EXISTS:
+ PR_SetError(PR_FILE_EXISTS_ERROR, err);
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_INVALID_NAME:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ERROR_NOT_READY:
+ case ERROR_OPEN_FAILED:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_DISK_FULL:
+ case ERROR_HANDLE_DISK_FULL:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_gethostname_error(PRInt32 err)
+{
+ switch (err) {
+ case SOCEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ENETDOWN:
+ case EINPROGRESS:
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_os2_map_select_error(PRInt32 err)
+{
+ PRErrorCode prerror;
+
+ switch (err) {
+ /*
+ * OS/2 select() only works on sockets. So in this
+ * context, ENOTSOCK is equivalent to EBADF on Unix.
+ */
+ case ENOTSOCK:
+ prerror = PR_BAD_DESCRIPTOR_ERROR;
+ break;
+ case EINVAL:
+ prerror = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ case SOCEFAULT:
+ prerror = PR_ACCESS_FAULT_ERROR;
+ break;
+ default:
+ prerror = PR_UNKNOWN_ERROR;
+ }
+ PR_SetError(prerror, err);
+}
+
+void _MD_os2_map_lockf_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_MORE_DATA:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
diff --git a/pr/src/md/os2/os2cv.c b/pr/src/md/os2/os2cv.c
new file mode 100644
index 00000000..34264dd5
--- /dev/null
+++ b/pr/src/md/os2/os2cv.c
@@ -0,0 +1,332 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * os2cv.c -- OS/2 Machine-Dependent Code for Condition Variables
+ *
+ * We implement our own condition variable wait queue. Each thread
+ * has a semaphore object (thread->md.blocked_sema) to block on while
+ * waiting on a condition variable.
+ *
+ * We use a deferred condition notify algorithm. When PR_NotifyCondVar
+ * or PR_NotifyAllCondVar is called, the condition notifies are simply
+ * recorded in the _MDLock structure. We defer the condition notifies
+ * until right after we unlock the lock. This way the awakened threads
+ * have a better chance to reaquire the lock.
+ */
+
+#include "primpl.h"
+
+/*
+ * AddThreadToCVWaitQueueInternal --
+ *
+ * Add the thread to the end of the condition variable's wait queue.
+ * The CV's lock must be locked when this function is called.
+ */
+
+static void
+AddThreadToCVWaitQueueInternal(PRThread *thred, struct _MDCVar *cv)
+{
+ PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL)
+ || (cv->waitTail == NULL && cv->waitHead == NULL));
+ cv->nwait += 1;
+ thred->md.inCVWaitQueue = PR_TRUE;
+ thred->md.next = NULL;
+ thred->md.prev = cv->waitTail;
+ if (cv->waitHead == NULL) {
+ cv->waitHead = thred;
+ } else {
+ cv->waitTail->md.next = thred;
+ }
+ cv->waitTail = thred;
+}
+
+/*
+ * md_UnlockAndPostNotifies --
+ *
+ * Unlock the lock, and then do the deferred condition notifies.
+ * If waitThred and waitCV are not NULL, waitThred is added to
+ * the wait queue of waitCV before the lock is unlocked.
+ *
+ * This function is called by _PR_MD_WAIT_CV and _PR_MD_UNLOCK,
+ * the two places where a lock is unlocked.
+ */
+static void
+md_UnlockAndPostNotifies(
+ _MDLock *lock,
+ PRThread *waitThred,
+ _MDCVar *waitCV)
+{
+ PRIntn index;
+ _MDNotified post;
+ _MDNotified *notified, *prev = NULL;
+
+ /*
+ * Time to actually notify any conditions that were affected
+ * while the lock was held. Get a copy of the list that's in
+ * the lock structure and then zero the original. If it's
+ * linked to other such structures, we own that storage.
+ */
+ post = lock->notified; /* a safe copy; we own the lock */
+
+#if defined(DEBUG)
+ memset(&lock->notified, 0, sizeof(_MDNotified)); /* reset */
+#else
+ lock->notified.length = 0; /* these are really sufficient */
+ lock->notified.link = NULL;
+#endif
+
+ /*
+ * Figure out how many threads we need to wake up.
+ */
+ notified = &post; /* this is where we start */
+ do {
+ for (index = 0; index < notified->length; ++index) {
+ _MDCVar *cv = notified->cv[index].cv;
+ PRThread *thred;
+ int i;
+
+ /* Fast special case: no waiting threads */
+ if (cv->waitHead == NULL) {
+ notified->cv[index].notifyHead = NULL;
+ continue;
+ }
+
+ /* General case */
+ if (-1 == notified->cv[index].times) {
+ /* broadcast */
+ thred = cv->waitHead;
+ while (thred != NULL) {
+ thred->md.inCVWaitQueue = PR_FALSE;
+ thred = thred->md.next;
+ }
+ notified->cv[index].notifyHead = cv->waitHead;
+ cv->waitHead = cv->waitTail = NULL;
+ cv->nwait = 0;
+ } else {
+ thred = cv->waitHead;
+ i = notified->cv[index].times;
+ while (thred != NULL && i > 0) {
+ thred->md.inCVWaitQueue = PR_FALSE;
+ thred = thred->md.next;
+ i--;
+ }
+ notified->cv[index].notifyHead = cv->waitHead;
+ cv->waitHead = thred;
+ if (cv->waitHead == NULL) {
+ cv->waitTail = NULL;
+ } else {
+ if (cv->waitHead->md.prev != NULL) {
+ cv->waitHead->md.prev->md.next = NULL;
+ cv->waitHead->md.prev = NULL;
+ }
+ }
+ cv->nwait -= notified->cv[index].times - i;
+ }
+ }
+ notified = notified->link;
+ } while (NULL != notified);
+
+ if (waitThred) {
+ AddThreadToCVWaitQueueInternal(waitThred, waitCV);
+ }
+
+ /* Release the lock before notifying */
+ LeaveCriticalSection(&lock->mutex);
+
+ notified = &post; /* this is where we start */
+ do {
+ for (index = 0; index < notified->length; ++index) {
+ PRThread *thred;
+ PRThread *next;
+
+ thred = notified->cv[index].notifyHead;
+ while (thred != NULL) {
+ BOOL rv;
+
+ next = thred->md.next;
+ thred->md.prev = thred->md.next = NULL;
+ rv = DosPostEventSem(thred->md.blocked_sema.sem);
+ PR_ASSERT(rv == NO_ERROR);
+ thred = next;
+ }
+ }
+ prev = notified;
+ notified = notified->link;
+ if (&post != prev) PR_DELETE(prev);
+ } while (NULL != notified);
+}
+
+/*
+ * Notifies just get posted to the protecting mutex. The
+ * actual notification is done when the lock is released so that
+ * MP systems don't contend for a lock that they can't have.
+ */
+static void md_PostNotifyToCvar(_MDCVar *cvar, _MDLock *lock,
+ PRBool broadcast)
+{
+ PRIntn index = 0;
+ _MDNotified *notified = &lock->notified;
+
+ while (1) {
+ for (index = 0; index < notified->length; ++index) {
+ if (notified->cv[index].cv == cvar) {
+ if (broadcast) {
+ notified->cv[index].times = -1;
+ } else if (-1 != notified->cv[index].times) {
+ notified->cv[index].times += 1;
+ }
+ return;
+ }
+ }
+ /* if not full, enter new CV in this array */
+ if (notified->length < _MD_CV_NOTIFIED_LENGTH) break;
+
+ /* if there's no link, create an empty array and link it */
+ if (NULL == notified->link) {
+ notified->link = PR_NEWZAP(_MDNotified);
+ }
+
+ notified = notified->link;
+ }
+
+ /* A brand new entry in the array */
+ notified->cv[index].times = (broadcast) ? -1 : 1;
+ notified->cv[index].cv = cvar;
+ notified->length += 1;
+}
+
+/*
+ * _PR_MD_NEW_CV() -- Creating new condition variable
+ * ... Solaris uses cond_init() in similar function.
+ *
+ * returns: -1 on failure
+ * 0 when it succeeds.
+ *
+ */
+PR_IMPLEMENT(PRInt32)
+_PR_MD_NEW_CV(_MDCVar *cv)
+{
+ cv->magic = _MD_MAGIC_CV;
+ /*
+ * The waitHead, waitTail, and nwait fields are zeroed
+ * when the PRCondVar structure is created.
+ */
+ return 0;
+}
+
+PR_IMPLEMENT(void) _PR_MD_FREE_CV(_MDCVar *cv)
+{
+ cv->magic = (PRUint32)-1;
+ return;
+}
+
+/*
+ * _PR_MD_WAIT_CV() -- Wait on condition variable
+ */
+PR_IMPLEMENT(void)
+_PR_MD_WAIT_CV(_MDCVar *cv, _MDLock *lock, PRIntervalTime timeout )
+{
+ PRThread *thred = _PR_MD_CURRENT_THREAD();
+ ULONG rv, count;
+ ULONG msecs = (timeout == PR_INTERVAL_NO_TIMEOUT) ?
+ SEM_INDEFINITE_WAIT : PR_IntervalToMilliseconds(timeout);
+
+ /*
+ * If we have pending notifies, post them now.
+ */
+ if (0 != lock->notified.length) {
+ md_UnlockAndPostNotifies(lock, thred, cv);
+ } else {
+ AddThreadToCVWaitQueueInternal(thred, cv);
+ LeaveCriticalSection(&lock->mutex);
+ }
+
+ /* Wait for notification or timeout; don't really care which */
+ rv = DosWaitEventSem(thred->md.blocked_sema.sem, msecs);
+ DosResetEventSem(thred->md.blocked_sema.sem, &count);
+
+ EnterCriticalSection(&(lock->mutex));
+
+ PR_ASSERT(rv == NO_ERROR || rv == ERROR_TIMEOUT);
+
+ if(rv == ERROR_TIMEOUT)
+ {
+ if (thred->md.inCVWaitQueue) {
+ PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL)
+ || (cv->waitTail == NULL && cv->waitHead == NULL));
+ cv->nwait -= 1;
+ thred->md.inCVWaitQueue = PR_FALSE;
+ if (cv->waitHead == thred) {
+ cv->waitHead = thred->md.next;
+ if (cv->waitHead == NULL) {
+ cv->waitTail = NULL;
+ } else {
+ cv->waitHead->md.prev = NULL;
+ }
+ } else {
+ PR_ASSERT(thred->md.prev != NULL);
+ thred->md.prev->md.next = thred->md.next;
+ if (thred->md.next != NULL) {
+ thred->md.next->md.prev = thred->md.prev;
+ } else {
+ PR_ASSERT(cv->waitTail == thred);
+ cv->waitTail = thred->md.prev;
+ }
+ }
+ thred->md.next = thred->md.prev = NULL;
+ } else {
+ /*
+ * This thread must have been notified, but the
+ * SemRelease call happens after SemRequest
+ * times out. Wait on the semaphore again to make it
+ * non-signaled. We assume this wait won't take long.
+ */
+ rv = DosWaitEventSem(thred->md.blocked_sema.sem, SEM_INDEFINITE_WAIT);
+ DosResetEventSem(thred->md.blocked_sema.sem, &count);
+ PR_ASSERT(rv == NO_ERROR);
+ }
+ }
+ PR_ASSERT(thred->md.inCVWaitQueue == PR_FALSE);
+ return;
+} /* --- end _PR_MD_WAIT_CV() --- */
+
+PR_IMPLEMENT(void)
+_PR_MD_NOTIFY_CV(_MDCVar *cv, _MDLock *lock)
+{
+ md_PostNotifyToCvar(cv, lock, PR_FALSE);
+ return;
+}
+
+PR_IMPLEMENT(void)
+_PR_MD_NOTIFYALL_CV(_MDCVar *cv, _MDLock *lock)
+{
+ md_PostNotifyToCvar(cv, lock, PR_TRUE);
+ return;
+}
+
+PR_IMPLEMENT(void)
+_PR_MD_UNLOCK(_MDLock *lock)
+{
+ if (0 != lock->notified.length) {
+ md_UnlockAndPostNotifies(lock, NULL, NULL);
+ } else {
+ LeaveCriticalSection(&lock->mutex);
+ }
+ return;
+}
diff --git a/pr/src/md/os2/os2gc.c b/pr/src/md/os2/os2gc.c
new file mode 100644
index 00000000..00db7e15
--- /dev/null
+++ b/pr/src/md/os2/os2gc.c
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * GC related routines
+ *
+ */
+#include "primpl.h"
+
+extern APIRET (* APIENTRY QueryThreadContext)(TID, ULONG, PCONTEXTRECORD);
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ CONTEXTRECORD context;
+ context.ContextFlags = CONTEXT_INTEGER;
+
+ if (_PR_IS_NATIVE_THREAD(t)) {
+ context.ContextFlags |= CONTEXT_CONTROL;
+ if (QueryThreadContext(t->md.handle, CONTEXT_CONTROL, &context)) {
+ t->md.gcContext[0] = context.ctx_RegEax;
+ t->md.gcContext[1] = context.ctx_RegEbx;
+ t->md.gcContext[2] = context.ctx_RegEcx;
+ t->md.gcContext[3] = context.ctx_RegEdx;
+ t->md.gcContext[4] = context.ctx_RegEsi;
+ t->md.gcContext[5] = context.ctx_RegEdi;
+ t->md.gcContext[6] = context.ctx_RegEsp;
+ t->md.gcContext[7] = context.ctx_RegEbp;
+ *np = PR_NUM_GCREGS;
+ } else {
+ PR_ASSERT(0);/* XXX */
+ }
+ }
+ return (PRWord *)&t->md.gcContext;
+}
+
+/* This function is not used right now, but is left as a reference.
+ * If you ever need to get the fiberID from the currently running fiber,
+ * this is it.
+ */
+void *
+GetMyFiberID()
+{
+ void *fiberData = 0;
+
+ /* A pointer to our tib entry is found at FS:[18]
+ * At offset 10h is the fiberData pointer. The context of the
+ * fiber is stored in there.
+ */
+#ifdef HAVE_ASM
+ __asm {
+ mov EDX, FS:[18h]
+ mov EAX, DWORD PTR [EDX+10h]
+ mov [fiberData], EAX
+ }
+#endif
+
+ return fiberData;
+}
diff --git a/pr/src/md/os2/os2inrval.c b/pr/src/md/os2/os2inrval.c
new file mode 100644
index 00000000..a204a639
--- /dev/null
+++ b/pr/src/md/os2/os2inrval.c
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * OS/2 interval timers
+ *
+ */
+
+#include "primpl.h"
+
+ULONG _os2_ticksPerSec = -1;
+PRIntn _os2_bitShift = 0;
+PRInt32 _os2_highMask = 0;
+
+
+
+PR_IMPLEMENT(void)
+_PR_MD_INTERVAL_INIT()
+{
+ ULONG count;
+
+ if (DosTmrQueryFreq(&_os2_ticksPerSec) == NO_ERROR)
+ {
+ while(_os2_ticksPerSec > PR_INTERVAL_MAX) {
+ _os2_ticksPerSec >>= 1;
+ _os2_bitShift++;
+ _os2_highMask = (_os2_highMask << 1)+1;
+ }
+ }
+ else
+ _os2_ticksPerSec = -1;
+
+ PR_ASSERT(_os2_ticksPerSec > PR_INTERVAL_MIN && _os2_ticksPerSec < PR_INTERVAL_MAX);
+}
+
+PR_IMPLEMENT(PRIntervalTime)
+_PR_MD_GET_INTERVAL()
+{
+ QWORD count;
+
+ /* Sadly; nspr requires the interval to range from 1000 ticks per second
+ * to only 100000 ticks per second; Counter is too high
+ * resolution...
+ */
+ if (DosTmrQueryTime(&count) == NO_ERROR) {
+ PRInt32 top = count.ulHi & _os2_highMask;
+ top = top << (32 - _os2_bitShift);
+ count.ulLo = count.ulLo >> _os2_bitShift;
+ count.ulHi = count.ulLo + top;
+ return (PRUint32)count.ulLo;
+ }
+ else{
+ ULONG msCount = PR_FAILURE;
+ DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, &msCount, sizeof(msCount));
+ return msCount;
+ }
+}
+
+PR_IMPLEMENT(PRIntervalTime)
+_PR_MD_INTERVAL_PER_SEC()
+{
+ if(_os2_ticksPerSec != -1)
+ return _os2_ticksPerSec;
+ else
+ return 1000;
+}
diff --git a/pr/src/md/os2/os2io.c b/pr/src/md/os2/os2io.c
new file mode 100644
index 00000000..b2e89d27
--- /dev/null
+++ b/pr/src/md/os2/os2io.c
@@ -0,0 +1,620 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/* OS2 IO module
+ *
+ * Assumes synchronous I/O.
+ *
+ */
+
+#include "primpl.h"
+#include <direct.h>
+
+struct _MDLock _pr_ioq_lock;
+
+PR_IMPLEMENT(PRStatus)
+_PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PRInt32 rv;
+ ULONG count;
+
+ PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ?
+ SEM_INDEFINITE_WAIT : PR_IntervalToMilliseconds(ticks);
+ rv = DosWaitEventSem(thread->md.blocked_sema.sem, msecs);
+ DosResetEventSem(thread->md.blocked_sema.sem, &count);
+ switch(rv)
+ {
+ case NO_ERROR:
+ return PR_SUCCESS;
+ break;
+ case ERROR_TIMEOUT:
+ _PR_THREAD_LOCK(thread);
+ if (thread->state == _PR_IO_WAIT) {
+ ;
+ } else {
+ if (thread->wait.cvar != NULL) {
+ thread->wait.cvar = NULL;
+ _PR_THREAD_UNLOCK(thread);
+ } else {
+ /* The CVAR was notified just as the timeout
+ * occurred. This led to us being notified twice.
+ * call SemRequest() to clear the semaphore.
+ */
+ _PR_THREAD_UNLOCK(thread);
+ rv = DosWaitEventSem(thread->md.blocked_sema.sem, 0);
+ DosResetEventSem(thread->md.blocked_sema.sem, &count);
+ PR_ASSERT(rv == NO_ERROR);
+ }
+ }
+ return PR_SUCCESS;
+ break;
+ default:
+ break;
+ }
+ return PR_FAILURE;
+}
+PR_IMPLEMENT(PRStatus)
+_PR_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if ( _PR_IS_NATIVE_THREAD(thread) )
+ {
+ if (DosPostEventSem(thread->md.blocked_sema.sem) != NO_ERROR)
+ return PR_FAILURE;
+ else
+ return PR_SUCCESS;
+ }
+}
+
+
+/* --- FILE IO ----------------------------------------------------------- */
+/*
+ * _PR_MD_OPEN() -- Open a file
+ *
+ * returns: a fileHandle
+ *
+ * The NSPR open flags (osflags) are translated into flags for OS/2
+ *
+ * Mode seems to be passed in as a unix style file permissions argument
+ * as in 0666, in the case of opening the logFile.
+ *
+ */
+PRInt32
+_PR_MD_OPEN(const char *name, PRIntn osflags, int mode)
+{
+ HFILE file;
+ PRInt32 access = OPEN_SHARE_DENYNONE;
+ PRInt32 flags = OPEN_ACTION_OPEN_IF_EXISTS;
+ PRInt32 rc;
+ PRUword actionTaken;
+
+ ULONG CurMaxFH = 0;
+ LONG ReqCount = 1;
+
+ if (osflags & PR_RDONLY)
+ access |= OPEN_ACCESS_READONLY;
+ else if (osflags & PR_WRONLY)
+ access |= OPEN_ACCESS_WRITEONLY;
+ else if(osflags & PR_RDWR)
+ access |= OPEN_ACCESS_READWRITE;
+ if (osflags & PR_CREATE_FILE)
+ flags |= OPEN_ACTION_CREATE_IF_NEW;
+ else if (osflags & PR_TRUNCATE){
+ flags &= ~OPEN_ACTION_OPEN_IF_EXISTS;
+ flags |= OPEN_ACTION_REPLACE_IF_EXISTS;
+ }
+
+ /* OS/2 sets the Max file handles per process to 20 by default */
+ DosSetRelMaxFH(&ReqCount, &CurMaxFH);
+
+ rc = DosOpen((char*)name,
+ &file, /* file handle if successful */
+ &actionTaken, /* reason for failure */
+ 0, /* initial size of new file */
+ FILE_NORMAL, /* file system attributes */
+ flags, /* Open flags */
+ access, /* Open mode and rights */
+ 0); /* OS/2 Extended Attributes */
+ if (rc != NO_ERROR) {
+ _PR_MD_MAP_OPEN_ERROR(rc);
+ return -1;
+ }
+
+ return (PRInt32)file;
+}
+
+PRInt32
+_PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len)
+{
+ PRUword bytes;
+ int rv;
+
+ rv = DosRead((HFILE)fd->secret->md.osfd,
+ (PVOID)buf,
+ len,
+ &bytes);
+
+ if (rv != NO_ERROR)
+ {
+ /* ERROR_HANDLE_EOF can only be returned by async io */
+ PR_ASSERT(rv != ERROR_HANDLE_EOF);
+ if (rv == ERROR_BROKEN_PIPE)
+ return 0;
+ else {
+ _PR_MD_MAP_READ_ERROR(rv);
+ return -1;
+ }
+ }
+ return bytes;
+}
+
+PRInt32
+_PR_MD_WRITE(PRFileDesc *fd, const void *buf, PRInt32 len)
+{
+ PRUword bytes;
+ int rv;
+
+ /* No longer using DosWrite since it doesn't convert \n to \n\r like C runtime does */
+#if 0
+ rv = DosWrite((HFILE)fd->secret->md.osfd,
+ (PVOID)buf,
+ len,
+ &bytes);
+
+ if (rv != NO_ERROR)
+ {
+ _PR_MD_MAP_WRITE_ERROR(rv);
+ return -1;
+ }
+#else
+ bytes = write(fd->secret->md.osfd, buf, len);
+ if (rv == -1)
+ _PR_MD_MAP_WRITE_ERROR(errno);
+#endif
+
+ return bytes;
+} /* --- end _PR_MD_WRITE() --- */
+
+PRInt32
+_PR_MD_LSEEK(PRFileDesc *fd, PRInt32 offset, int whence)
+{
+ PRInt32 rv;
+ PRUword newLocation;
+
+ rv = DosSetFilePtr((HFILE)fd->secret->md.osfd, offset, whence, &newLocation);
+
+ if (rv != NO_ERROR) {
+ _PR_MD_MAP_LSEEK_ERROR(rv);
+ return -1;
+ } else
+ return newLocation;
+}
+
+PRInt64
+_PR_MD_LSEEK64(PRFileDesc *fd, PRInt64 offset, int whence)
+{
+ PRInt64 result;
+ PRInt32 rv, low = offset.lo, hi = offset.hi;
+ PRUword newLocation;
+
+ rv = DosSetFilePtr((HFILE)fd->secret->md.osfd, low, whence, &newLocation);
+ rv = DosSetFilePtr((HFILE)fd->secret->md.osfd, hi, FILE_CURRENT, &newLocation);
+
+ if (rv != NO_ERROR) {
+ _PR_MD_MAP_LSEEK_ERROR(rv);
+ hi = newLocation = -1;
+ }
+
+ result.lo = hi;
+ result.hi = newLocation;
+ return result;
+}
+
+PRInt32
+_PR_MD_FSYNC(PRFileDesc *fd)
+{
+ PRInt32 rc = DosResetBuffer((HFILE)fd->secret->md.osfd);
+
+ if (rc != NO_ERROR) {
+ if (rc != ERROR_ACCESS_DENIED) {
+ _PR_MD_MAP_FSYNC_ERROR(rc);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+PRInt32
+_MD_CloseFile(PRInt32 osfd)
+{
+ PRInt32 rv;
+
+ rv = DosClose((HFILE)osfd);
+ if (rv != NO_ERROR)
+ _PR_MD_MAP_CLOSE_ERROR(rv);
+ return rv;
+}
+
+
+/* --- DIR IO ------------------------------------------------------------ */
+#define GetFileFromDIR(d) (d)->d_entry.achName
+
+void FlipSlashes(char *cp, int len)
+{
+ while (--len >= 0) {
+ if (cp[0] == '/') {
+ cp[0] = PR_DIRECTORY_SEPARATOR;
+ }
+ cp++;
+ }
+}
+
+/*
+**
+** Local implementations of standard Unix RTL functions which are not provided
+** by the VAC RTL.
+**
+*/
+
+PRInt32
+_PR_MD_CLOSE_DIR(_MDDir *d)
+{
+ PRInt32 rc;
+
+ if ( d ) {
+ rc = DosFindClose(d->d_hdl);
+ if(rc == NO_ERROR){
+ d->magic = (PRUint32)-1;
+ return PR_SUCCESS;
+ } else {
+ _PR_MD_MAP_CLOSEDIR_ERROR(rc);
+ return PR_FAILURE;
+ }
+ }
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+}
+
+
+PRStatus
+_PR_MD_OPEN_DIR(_MDDir *d, const char *name)
+{
+ char filename[ CCHMAXPATH ];
+ PRUword numEntries, rc;
+
+ PR_snprintf(filename, CCHMAXPATH, "%s%s%s",
+ name, PR_DIRECTORY_SEPARATOR_STR, "*.*");
+ FlipSlashes( filename, strlen(filename) );
+
+ d->d_hdl = HDIR_CREATE;
+
+ rc = DosFindFirst( filename, &d->d_hdl, FILE_DIRECTORY, &(d->d_entry), sizeof(d->d_entry), &numEntries, FIL_STANDARD);
+ if ( rc != NO_ERROR ) {
+ _PR_MD_MAP_OPENDIR_ERROR(rc);
+ return PR_FAILURE;
+ }
+ d->firstEntry = PR_TRUE;
+ d->magic = _MD_MAGIC_DIR;
+ return PR_SUCCESS;
+}
+
+char *
+_PR_MD_READ_DIR(_MDDir *d, PRIntn flags)
+{
+ PRUword numFiles = 1;
+ BOOL rv;
+ char *fileName;
+
+ if ( d ) {
+ while (1) {
+ if (d->firstEntry) {
+ d->firstEntry = PR_FALSE;
+ rv = NO_ERROR;
+ } else {
+ rv = DosFindNext(d->d_hdl, &(d->d_entry), sizeof(d->d_entry), &numFiles);
+ }
+ if (rv != NO_ERROR) {
+ break;
+ }
+ fileName = GetFileFromDIR(d);
+ if ( (flags & PR_SKIP_DOT) &&
+ (fileName[0] == '.') && (fileName[1] == '\0'))
+ continue;
+ if ( (flags & PR_SKIP_DOT_DOT) &&
+ (fileName[0] == '.') && (fileName[1] == '.') &&
+ (fileName[2] == '\0'))
+ continue;
+ /*
+ * XXX
+ * Is this the correct definition of a hidden file on OS/2?
+ */
+ if ( (flags & PR_SKIP_HIDDEN) && (fileName[0] == '.'))
+ continue;
+ return fileName;
+ }
+ PR_ASSERT(NO_ERROR != rv);
+ _PR_MD_MAP_READDIR_ERROR(rv);
+ return NULL;
+ }
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+}
+
+PRInt32
+_PR_MD_DELETE(const char *name)
+{
+ PRInt32 rc = DosDelete((char*)name);
+ if(rc == NO_ERROR) {
+ return 0;
+ } else {
+ _PR_MD_MAP_DELETE_ERROR(rc);
+ return -1;
+ }
+}
+
+PRInt32
+_PR_MD_STAT(const char *fn, struct stat *info)
+{
+ PRInt32 rv;
+
+ rv = _stat((char*)fn, info);
+ if (-1 == rv) {
+ /*
+ * Check for MSVC runtime library _stat() bug.
+ * (It's really a bug in FindFirstFile().)
+ * If a pathname ends in a backslash or slash,
+ * e.g., c:\temp\ or c:/temp/, _stat() will fail.
+ * Note: a pathname ending in a slash (e.g., c:/temp/)
+ * can be handled by _stat() on NT but not on Win95.
+ *
+ * We remove the backslash or slash at the end and
+ * try again.
+ *
+ * Not sure if this happens on OS/2 or not,
+ * but it doesn't hurt to be careful.
+ */
+
+ int len = strlen(fn);
+ if (len > 0 && len <= _MAX_PATH
+ && (fn[len - 1] == '\\' || fn[len - 1] == '/')) {
+ char newfn[_MAX_PATH + 1];
+
+ strcpy(newfn, fn);
+ newfn[len - 1] = '\0';
+ rv = _stat(newfn, info);
+ }
+ }
+
+ if (-1 == rv) {
+ _PR_MD_MAP_STAT_ERROR(errno);
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info)
+{
+ struct stat sb;
+ PRInt32 rv;
+
+ if ( (rv = _PR_MD_STAT(fn, &sb)) == 0 ) {
+ if (info) {
+ if (S_IFREG & sb.st_mode)
+ info->type = PR_FILE_FILE ;
+ else if (S_IFDIR & sb.st_mode)
+ info->type = PR_FILE_DIRECTORY;
+ else
+ info->type = PR_FILE_OTHER;
+ info->size = sb.st_size;
+ info->modifyTime.lo = sb.st_mtime;
+ info->creationTime.lo = sb.st_ctime;
+ }
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info)
+{
+ PRFileInfo info32;
+ PRInt32 rv = _PR_MD_GETFILEINFO(fn, &info32);
+ if (0 == rv)
+ {
+ info->type = info32.type;
+ info->size.lo = info32.size;
+ info->modifyTime = info32.modifyTime;
+ info->creationTime = info32.creationTime;
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info)
+{
+ /* For once, the VAC compiler/library did a nice thing.
+ * The file handle used by the C runtime is the same one
+ * returned by the OS when you call DosOpen(). This means
+ * that you can take an OS HFILE and use it with C file
+ * functions. The only caveat is that you have to call
+ * _setmode() first to initialize some junk. This is
+ * immensely useful because I did not have a clue how to
+ * implement this function otherwise. The windows folks
+ * took the source from the Microsoft C library source, but
+ * IBM wasn't kind enough to ship the source with VAC.
+ * On second thought, the needed function could probably
+ * be gotten from the OS/2 GNU library source, but the
+ * point is now moot.
+ */
+ struct stat hinfo;
+
+ _setmode(fd->secret->md.osfd, O_BINARY);
+ if(fstat((int)fd->secret->md.osfd, &hinfo) != NO_ERROR) {
+ _PR_MD_MAP_FSTAT_ERROR(errno);
+ return -1;
+ }
+
+ if (hinfo.st_mode & S_IFDIR)
+ info->type = PR_FILE_DIRECTORY;
+ else
+ info->type = PR_FILE_FILE;
+
+ info->size = hinfo.st_size;
+ info->modifyTime.lo = hinfo.st_mtime;
+ info->creationTime.lo = hinfo.st_ctime;
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info)
+{
+ PRFileInfo info32;
+ PRInt32 rv = _PR_MD_GETOPENFILEINFO(fd, &info32);
+ if (0 == rv)
+ {
+ info->type = info32.type;
+ info->size.lo = info32.size;
+ info->modifyTime = info32.modifyTime;
+ info->creationTime = info32.creationTime;
+ }
+ return rv;
+}
+
+
+PRInt32
+_PR_MD_RENAME(const char *from, const char *to)
+{
+ PRInt32 rc;
+ /* Does this work with dot-relative pathnames? */
+ if ( (rc = DosMove((char *)from, (char *)to)) == NO_ERROR) {
+ return 0;
+ } else {
+ _PR_MD_MAP_RENAME_ERROR(rc);
+ return -1;
+ }
+}
+
+PRInt32
+_PR_MD_ACCESS(const char *name, PRIntn how)
+{
+PRInt32 rv;
+ switch (how) {
+ case PR_ACCESS_WRITE_OK:
+ rv = access(name, 02);
+ break;
+ case PR_ACCESS_READ_OK:
+ rv = access(name, 04);
+ break;
+ case PR_ACCESS_EXISTS:
+ return access(name, 00);
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+ if (rv < 0)
+ _PR_MD_MAP_ACCESS_ERROR(errno);
+ return rv;
+}
+
+PRInt32
+_PR_MD_MKDIR(const char *name, PRIntn mode)
+{
+ PRInt32 rc;
+ /* XXXMB - how to translate the "mode"??? */
+ if ((rc = DosCreateDir((char *)name, NULL)) == NO_ERROR) {
+ return 0;
+ } else {
+ _PR_MD_MAP_MKDIR_ERROR(rc);
+ return -1;
+ }
+}
+
+PRInt32
+_PR_MD_RMDIR(const char *name)
+{
+ PRInt32 rc;
+ if ( (rc = DosDeleteDir((char *)name)) == NO_ERROR) {
+ return 0;
+ } else {
+ _PR_MD_MAP_RMDIR_ERROR(rc);
+ return -1;
+ }
+}
+
+PR_IMPLEMENT(PRStatus)
+_PR_MD_LOCKFILE(PRInt32 f)
+{
+ PRInt32 rv;
+ FILELOCK lock, unlock;
+
+ lock.lOffset = 0;
+ lock.lRange = 0xffffffff;
+ unlock.lOffset = 0;
+ unlock.lRange = 0;
+
+ /*
+ * loop trying to DosSetFileLocks(),
+ * pause for a few miliseconds when can't get the lock
+ * and try again
+ */
+ for( rv = FALSE; rv == FALSE; /* do nothing */ )
+ {
+
+ rv = DosSetFileLocks( (HFILE) f,
+ &unlock, &lock,
+ 0, 0);
+ if ( rv != NO_ERROR )
+ {
+ DosSleep( 50 ); /* Sleep() a few milisecs and try again. */
+ }
+ } /* end for() */
+ return PR_SUCCESS;
+} /* end _PR_MD_LOCKFILE() */
+
+PR_IMPLEMENT(PRStatus)
+_PR_MD_TLOCKFILE(PRInt32 f)
+{
+ return _PR_MD_LOCKFILE(f);
+} /* end _PR_MD_TLOCKFILE() */
+
+
+PR_IMPLEMENT(PRStatus)
+_PR_MD_UNLOCKFILE(PRInt32 f)
+{
+ PRInt32 rv;
+ FILELOCK lock, unlock;
+
+ lock.lOffset = 0;
+ lock.lRange = 0;
+ unlock.lOffset = 0;
+ unlock.lRange = 0xffffffff;
+
+ rv = DosSetFileLocks( (HFILE) f,
+ &unlock, &lock,
+ 0, 0);
+
+ if ( rv != NO_ERROR )
+ {
+ return PR_SUCCESS;
+ }
+ else
+ {
+ return PR_FAILURE;
+ }
+} /* end _PR_MD_UNLOCKFILE() */
+
diff --git a/pr/src/md/os2/os2misc.c b/pr/src/md/os2/os2misc.c
new file mode 100644
index 00000000..00913013
--- /dev/null
+++ b/pr/src/md/os2/os2misc.c
@@ -0,0 +1,513 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * os2misc.c
+ *
+ */
+#include "primpl.h"
+
+PR_IMPLEMENT(char *)
+_PR_MD_GET_ENV(const char *name)
+{
+ return getenv(name);
+}
+
+PR_IMPLEMENT(PRIntn)
+_PR_MD_PUT_ENV(const char *name)
+{
+ return putenv(name);
+}
+
+
+/*
+ **************************************************************************
+ **************************************************************************
+ **
+ ** Date and time routines
+ **
+ **************************************************************************
+ **************************************************************************
+ */
+
+#include <sys/timeb.h>
+/*
+ *-----------------------------------------------------------------------
+ *
+ * PR_Now --
+ *
+ * Returns the current time in microseconds since the epoch.
+ * The epoch is midnight January 1, 1970 GMT.
+ * The implementation is machine dependent. This is the
+ * implementation for OS/2.
+ * Cf. time_t time(time_t *tp)
+ *
+ *-----------------------------------------------------------------------
+ */
+
+PR_IMPLEMENT(PRTime)
+PR_Now(void)
+{
+ PRInt64 s, ms, ms2us, s2us;
+ struct timeb b;
+
+ ftime(&b);
+ LL_I2L(ms2us, PR_USEC_PER_MSEC);
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(s, b.time);
+ LL_I2L(ms, b.millitm);
+ LL_MUL(ms, ms, ms2us);
+ LL_MUL(s, s, s2us);
+ LL_ADD(s, s, ms);
+ return s;
+}
+
+
+/*
+ ***********************************************************************
+ ***********************************************************************
+ *
+ * Process creation routines
+ *
+ ***********************************************************************
+ ***********************************************************************
+ */
+
+/*
+ * Assemble the command line by concatenating the argv array.
+ * On success, this function returns 0 and the resulting command
+ * line is returned in *cmdLine. On failure, it returns -1.
+ */
+static int assembleCmdLine(char *const *argv, char **cmdLine)
+{
+ char *const *arg;
+ char *p, *q;
+ int cmdLineSize;
+ int numBackslashes;
+ int i;
+ int argNeedQuotes;
+
+ /*
+ * Find out how large the command line buffer should be.
+ */
+ cmdLineSize = 0;
+ for (arg = argv; *arg; arg++) {
+ /*
+ * \ and " need to be escaped by a \. In the worst case,
+ * every character is a \ or ", so the string of length
+ * may double. If we quote an argument, that needs two ".
+ * Finally, we need a space between arguments, a null between
+ * the EXE name and the arguments, and 2 nulls at the end
+ * of command line.
+ */
+ cmdLineSize += 2 * strlen(*arg) /* \ and " need to be escaped */
+ + 2 /* we quote every argument */
+ + 4; /* space in between, or final nulls */
+ }
+ p = *cmdLine = PR_MALLOC(cmdLineSize);
+ if (p == NULL) {
+ return -1;
+ }
+
+ for (arg = argv; *arg; arg++) {
+ /* Add a space to separates the arguments */
+ if (arg > argv + 1) {
+ *p++ = ' ';
+ }
+ q = *arg;
+ numBackslashes = 0;
+ argNeedQuotes = 0;
+
+ /* If the argument contains white space, it needs to be quoted. */
+ if (strpbrk(*arg, " \f\n\r\t\v")) {
+ argNeedQuotes = 1;
+ }
+
+ if (argNeedQuotes) {
+ *p++ = '"';
+ }
+ while (*q) {
+ if (*q == '\\') {
+ numBackslashes++;
+ q++;
+ } else if (*q == '"') {
+ if (numBackslashes) {
+ /*
+ * Double the backslashes since they are followed
+ * by a quote
+ */
+ for (i = 0; i < 2 * numBackslashes; i++) {
+ *p++ = '\\';
+ }
+ numBackslashes = 0;
+ }
+ /* To escape the quote */
+ *p++ = '\\';
+ *p++ = *q++;
+ } else {
+ if (numBackslashes) {
+ /*
+ * Backslashes are not followed by a quote, so
+ * don't need to double the backslashes.
+ */
+ for (i = 0; i < numBackslashes; i++) {
+ *p++ = '\\';
+ }
+ numBackslashes = 0;
+ }
+ *p++ = *q++;
+ }
+ }
+
+ /* Now we are at the end of this argument */
+ if (numBackslashes) {
+ /*
+ * Double the backslashes if we have a quote string
+ * delimiter at the end.
+ */
+ if (argNeedQuotes) {
+ numBackslashes *= 2;
+ }
+ for (i = 0; i < numBackslashes; i++) {
+ *p++ = '\\';
+ }
+ }
+ if (argNeedQuotes) {
+ *p++ = '"';
+ }
+ if(arg == argv)
+ *p++ = '\0';
+ }
+
+ /* Add 2 nulls at the end */
+ *p++ = '\0';
+ *p = '\0';
+ return 0;
+}
+
+/*
+ * Assemble the environment block by concatenating the envp array
+ * (preserving the terminating null byte in each array element)
+ * and adding a null byte at the end.
+ *
+ * Returns 0 on success. The resulting environment block is returned
+ * in *envBlock. Note that if envp is NULL, a NULL pointer is returned
+ * in *envBlock. Returns -1 on failure.
+ */
+static int assembleEnvBlock(char **envp, char **envBlock)
+{
+ char *p;
+ char *q;
+ char **env;
+ char *curEnv;
+ char *cwdStart, *cwdEnd;
+ int envBlockSize;
+
+ PPIB ppib = NULL;
+ PTIB ptib = NULL;
+
+ if (envp == NULL) {
+ *envBlock = NULL;
+ return 0;
+ }
+
+ if(DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR)
+ return -1;
+
+ curEnv = ppib->pib_pchenv;
+
+ cwdStart = curEnv;
+ while (*cwdStart) {
+ if (cwdStart[0] == '=' && cwdStart[1] != '\0'
+ && cwdStart[2] == ':' && cwdStart[3] == '=') {
+ break;
+ }
+ cwdStart += strlen(cwdStart) + 1;
+ }
+ cwdEnd = cwdStart;
+ if (*cwdEnd) {
+ cwdEnd += strlen(cwdEnd) + 1;
+ while (*cwdEnd) {
+ if (cwdEnd[0] != '=' || cwdEnd[1] == '\0'
+ || cwdEnd[2] != ':' || cwdEnd[3] != '=') {
+ break;
+ }
+ cwdEnd += strlen(cwdEnd) + 1;
+ }
+ }
+ envBlockSize = cwdEnd - cwdStart;
+
+ for (env = envp; *env; env++) {
+ envBlockSize += strlen(*env) + 1;
+ }
+ envBlockSize++;
+
+ p = *envBlock = PR_MALLOC(envBlockSize);
+ if (p == NULL) {
+ return -1;
+ }
+
+ q = cwdStart;
+ while (q < cwdEnd) {
+ *p++ = *q++;
+ }
+
+ for (env = envp; *env; env++) {
+ q = *env;
+ while (*q) {
+ *p++ = *q++;
+ }
+ *p++ = '\0';
+ }
+ *p = '\0';
+ return 0;
+}
+
+/*
+ * For qsort. We sort (case-insensitive) the environment strings
+ * before generating the environment block.
+ */
+static int compare(const void *arg1, const void *arg2)
+{
+ return stricmp(* (char**)arg1, * (char**)arg2);
+}
+PRProcess * _PR_CreateOS2Process(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr)
+{
+ char szFailed[CCHMAXPATH];
+ RESULTCODES procInfo;
+ APIRET retVal;
+ char *cmdLine = NULL;
+ char *envBlock = NULL;
+ char **newEnvp;
+ PRProcess *proc = NULL;
+ HFILE hStdIn = 0,
+ hStdOut = 0,
+ hStdErr = 0;
+
+
+ proc = PR_NEW(PRProcess);
+ if (!proc) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ goto errorExit;
+ }
+
+ if (assembleCmdLine(argv, &cmdLine) == -1) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ goto errorExit;
+ }
+
+ if (envp == NULL) {
+ newEnvp = NULL;
+ } else {
+ int i;
+ int numEnv = 0;
+ while (envp[numEnv]) {
+ numEnv++;
+ }
+ newEnvp = (char **) PR_MALLOC((numEnv+1) * sizeof(char *));
+ for (i = 0; i <= numEnv; i++) {
+ newEnvp[i] = envp[i];
+ }
+ qsort((void *) newEnvp, (size_t) numEnv, sizeof(char *), compare);
+ }
+ if (assembleEnvBlock(newEnvp, &envBlock) == -1) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ goto errorExit;
+ }
+
+ if (attr) {
+ PRBool redirected = PR_FALSE;
+
+ /* On OS/2, there is really no way to pass file handles for stdin, stdout,
+ * and stderr to a new process. Instead, we can make it a child process
+ * and make the given file handles a copy of our stdin, stdout, and stderr.
+ * The child process then inherits ours, and we set ours back. Twisted
+ * and gross I know. If you know a better way, please use it.
+ */
+ if (attr->stdinFd) {
+ hStdIn = (HFILE) attr->stdinFd->secret->md.osfd;
+ DosDupHandle(0, &hStdIn);
+ }
+ if (attr->stdoutFd) {
+ hStdOut = (HFILE) attr->stdoutFd->secret->md.osfd;
+ DosDupHandle(1, &hStdOut);
+ }
+ if (attr->stderrFd) {
+ hStdErr = (HFILE) attr->stderrFd->secret->md.osfd;
+ DosDupHandle(2, &hStdErr);
+ }
+ }
+
+ retVal = DosExecPgm(szFailed,
+ CCHMAXPATH,
+ EXEC_ASYNCRESULT,
+ cmdLine,
+ envBlock,
+ &procInfo,
+ argv[0]);
+
+ /* Restore our old values. Hope this works */
+ if(hStdIn){
+ hStdIn = 0;
+ DosDupHandle(0, &hStdIn);
+ }
+ if(hStdOut){
+ hStdOut = 1;
+ DosDupHandle(1, &hStdOut);
+ }
+ if(hStdErr){
+ hStdErr = 1;
+ DosDupHandle(0, &hStdErr);
+ }
+
+ if (retVal != NO_ERROR) {
+ /* XXX what error code? */
+ PR_SetError(PR_UNKNOWN_ERROR, retVal);
+ goto errorExit;
+ }
+
+ proc->md.pid = procInfo.codeTerminate;
+
+ PR_DELETE(cmdLine);
+ if (envBlock) {
+ PR_DELETE(envBlock);
+ }
+ return proc;
+
+errorExit:
+ if (cmdLine) {
+ PR_DELETE(cmdLine);
+ }
+ if (envBlock) {
+ PR_DELETE(envBlock);
+ }
+ if (proc) {
+ PR_DELETE(proc);
+ }
+ return NULL;
+
+} /* _PR_CreateWindowsProcess */
+
+PRStatus _PR_DetachOS2Process(PRProcess *process)
+{
+ /* This is basically what they did on Windows (CloseHandle)
+ * but I don't think it will do much on OS/2. A process is
+ * either created as a child or not. You can't 'detach' it
+ * later on.
+ */
+ DosClose(process->md.pid);
+ PR_DELETE(process);
+ return PR_SUCCESS;
+}
+
+/*
+ * XXX: This will currently only work on a child process.
+ */
+PRStatus _PR_WaitOS2Process(PRProcess *process,
+ PRInt32 *exitCode)
+{
+ ULONG ulRetVal;
+ RESULTCODES results;
+ PID pidEnded = 0;
+
+ ulRetVal = DosWaitChild(DCWA_PROCESS, DCWW_WAIT,
+ &results,
+ &pidEnded, process->md.pid);
+
+ if (ulRetVal != NO_ERROR) {
+ printf("\nDosWaitChild rc = %i\n", ulRetVal);
+ PR_SetError(PR_UNKNOWN_ERROR, ulRetVal);
+ return PR_FAILURE;
+ }
+ PR_DELETE(process);
+ return PR_SUCCESS;
+}
+
+PRStatus _PR_KillOS2Process(PRProcess *process)
+{
+ ULONG ulRetVal;
+ if ((ulRetVal = DosKillProcess(DKP_PROCESS, process->md.pid)) == NO_ERROR) {
+ return PR_SUCCESS;
+ }
+ PR_SetError(PR_UNKNOWN_ERROR, ulRetVal);
+ return PR_FAILURE;
+}
+
+PR_IMPLEMENT(PRStatus) _MD_OS2GetHostName(char *name, PRUint32 namelen)
+{
+ PRIntn rv;
+ PRInt32 syserror;
+
+ rv = gethostname(name, (PRInt32) namelen);
+ if (0 == rv) {
+ return PR_SUCCESS;
+ }
+ _PR_MD_MAP_GETHOSTNAME_ERROR(sock_errno());
+ return PR_FAILURE;
+}
+
+PR_IMPLEMENT(void)
+_PR_MD_WAKEUP_CPUS( void )
+{
+ return;
+}
+
+
+/*
+ **********************************************************************
+ *
+ * Memory-mapped files are not supported on OS/2 (or Win16).
+ *
+ **********************************************************************
+ */
+
+PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size)
+{
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+void * _MD_MemMap(
+ PRFileMap *fmap,
+ PRInt64 offset,
+ PRUint32 len)
+{
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+}
+
+PRStatus _MD_MemUnmap(void *addr, PRUint32 len)
+{
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRStatus _MD_CloseFileMap(PRFileMap *fmap)
+{
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
diff --git a/pr/src/md/os2/os2poll.c b/pr/src/md/os2/os2poll.c
new file mode 100644
index 00000000..b3353aa7
--- /dev/null
+++ b/pr/src/md/os2/os2poll.c
@@ -0,0 +1,165 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * This file implements _PR_MD_PR_POLL for OS/2.
+ */
+
+#include "primpl.h"
+
+PRInt32
+_PR_MD_PR_POLL(PRPollDesc *pds, PRIntn npds,
+ PRIntervalTime timeout)
+{
+ PRPollDesc *pd, *epd;
+ PRInt32 n, err, pdcnt;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ fd_set rd, wt, ex;
+ struct timeval tv, *tvp = NULL;
+ int maxfd = -1;
+
+ /*
+ * For restarting _MD_SELECT() if it is interrupted by a signal.
+ * We use these variables to figure out how much time has elapsed
+ * and how much of the timeout still remains.
+ */
+ PRIntervalTime start, elapsed, remaining;
+
+ FD_ZERO(&rd);
+ FD_ZERO(&wt);
+ FD_ZERO(&ex);
+
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ PRInt32 osfd;
+ PRInt16 in_flags = pd->in_flags;
+ PRFileDesc *bottom = pd->fd;
+
+ if ((NULL == bottom) || (in_flags == 0)) {
+ continue;
+ }
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ osfd = bottom->secret->md.osfd;
+
+ if (osfd > maxfd) {
+ maxfd = osfd;
+ }
+ if (in_flags & PR_POLL_READ) {
+ FD_SET(osfd, &rd);
+ }
+ if (in_flags & PR_POLL_WRITE) {
+ FD_SET(osfd, &wt);
+ }
+ if (in_flags & PR_POLL_EXCEPT) {
+ FD_SET(osfd, &ex);
+ }
+ }
+ if (timeout != PR_INTERVAL_NO_TIMEOUT) {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(timeout) % PR_USEC_PER_SEC;
+ tvp = &tv;
+ start = PR_IntervalNow();
+ }
+
+retry:
+ n = _MD_SELECT(maxfd + 1, &rd, &wt, &ex, tvp);
+ if (n == -1 && errno == EINTR) {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ goto retry;
+ } else {
+ elapsed = (PRIntervalTime) (PR_IntervalNow() - start);
+ if (elapsed > timeout) {
+ n = 0; /* timed out */
+ } else {
+ remaining = timeout - elapsed;
+ tv.tv_sec = PR_IntervalToSeconds(remaining);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ remaining - PR_SecondsToInterval(tv.tv_sec));
+ goto retry;
+ }
+ }
+ }
+
+ if (n > 0) {
+ n = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ PRInt32 osfd;
+ PRInt16 in_flags = pd->in_flags;
+ PRInt16 out_flags = 0;
+ PRFileDesc *bottom = pd->fd;
+
+ if ((NULL == bottom) || (in_flags == 0)) {
+ pd->out_flags = 0;
+ continue;
+ }
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ osfd = bottom->secret->md.osfd;
+
+ if ((in_flags & PR_POLL_READ) && FD_ISSET(osfd, &rd)) {
+ out_flags |= PR_POLL_READ;
+ }
+ if ((in_flags & PR_POLL_WRITE) && FD_ISSET(osfd, &wt)) {
+ out_flags |= PR_POLL_WRITE;
+ }
+ if ((in_flags & PR_POLL_EXCEPT) && FD_ISSET(osfd, &ex)) {
+ out_flags |= PR_POLL_EXCEPT;
+ }
+ pd->out_flags = out_flags;
+ if (out_flags) {
+ n++;
+ }
+ }
+ PR_ASSERT(n > 0);
+ } else if (n < 0) {
+ err = _MD_ERRNO();
+ if (err == EBADF) {
+ /* Find the bad fds */
+ n = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ int optval;
+ int optlen = sizeof(optval);
+ PRFileDesc *bottom = pd->fd;
+ pd->out_flags = 0;
+ if ((NULL == bottom) || (pd->in_flags == 0)) {
+ continue;
+ }
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ if (getsockopt(bottom->secret->md.osfd, SOL_SOCKET,
+ SO_TYPE, (char *) &optval, &optlen) == -1) {
+ PR_ASSERT(_MD_ERRNO() == ENOTSOCK);
+ if (_MD_ERRNO() == ENOTSOCK) {
+ pd->out_flags = PR_POLL_NVAL;
+ n++;
+ }
+ }
+ }
+ PR_ASSERT(n > 0);
+ } else {
+ PR_ASSERT(err != EINTR); /* should have been handled above */
+ _PR_MD_MAP_SELECT_ERROR(err);
+ }
+ }
+ return n;
+ }
+
diff --git a/pr/src/md/os2/os2sem.c b/pr/src/md/os2/os2sem.c
new file mode 100644
index 00000000..684ca945
--- /dev/null
+++ b/pr/src/md/os2/os2sem.c
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * OS/2-specific semaphore handling code.
+ *
+ */
+
+#include "primpl.h"
+
+
+PR_IMPLEMENT(void)
+_PR_MD_NEW_SEM(_MDSemaphore *md, PRUintn value)
+{
+ int rv;
+
+ /* Our Sems don't support a value > 1 */
+ PR_ASSERT(value <= 1);
+
+ rv = DosCreateEventSem(NULL, &md->sem, 0, 0);
+ PR_ASSERT(rv == NO_ERROR);
+}
+
+PR_IMPLEMENT(void)
+_PR_MD_DESTROY_SEM(_MDSemaphore *md)
+{
+ int rv;
+ rv = DosCloseEventSem(md->sem);
+ PR_ASSERT(rv == NO_ERROR);
+
+}
+
+PR_IMPLEMENT(PRStatus)
+_PR_MD_TIMED_WAIT_SEM(_MDSemaphore *md, PRIntervalTime ticks)
+{
+ int rv;
+ rv = DosWaitEventSem(md->sem, PR_IntervalToMilliseconds(ticks));
+
+ if (rv == NO_ERROR)
+ return PR_SUCCESS;
+ else
+ return PR_FAILURE;
+}
+
+PR_IMPLEMENT(PRStatus)
+_PR_MD_WAIT_SEM(_MDSemaphore *md)
+{
+ return _PR_MD_TIMED_WAIT_SEM(md, PR_INTERVAL_NO_TIMEOUT);
+}
+
+PR_IMPLEMENT(void)
+_PR_MD_POST_SEM(_MDSemaphore *md)
+{
+ int rv;
+ rv = DosPostEventSem(md->sem);
+ PR_ASSERT(rv == NO_ERROR);
+}
+
+
diff --git a/pr/src/md/os2/os2sock.c b/pr/src/md/os2/os2sock.c
new file mode 100644
index 00000000..3eb26d35
--- /dev/null
+++ b/pr/src/md/os2/os2sock.c
@@ -0,0 +1,770 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/* OS/2 Sockets module
+ *
+ */
+
+/*Note from DSR111297 - it should be noted that there are two flavors of select() on OS/2 */
+/*There is standard BSD (which is kind of slow) and a new flavor of select() that takes */
+/*an integer list of sockets, the number of read sockets, write sockets, except sockets, and */
+/*a millisecond count for timeout. In the interest of performance I have choosen the OS/2 */
+/*specific version of select(). See OS/2 TCP/IP Programmer's Toolkit for more info. */
+
+#include "primpl.h"
+
+void
+_PR_MD_INIT_IO()
+{
+ sock_init();
+}
+
+/* --- SOCKET IO --------------------------------------------------------- */
+
+
+PRInt32
+_PR_MD_SOCKET(int af, int type, int flags)
+{
+ int sock;
+ PRUint32 one = 1;
+ PRInt32 rv;
+ PRInt32 err;
+
+ sock = socket(af, type, flags);
+
+ if (sock == -1 )
+ {
+ int rv = sock_errno();
+ soclose(sock);
+ _PR_MD_MAP_SOCKET_ERROR(rv);
+ return (PRInt32) -1;
+ }
+
+ /*
+ ** Make the socket Non-Blocking
+ */
+ rv = ioctl( sock, FIONBIO, (char *) &one, sizeof(one));
+ if ( rv != 0 )
+ {
+ err = sock_errno();
+ return -1;
+ }
+
+ return (PRInt32)sock;
+}
+
+/*
+** _MD_CloseSocket() -- Close a socket
+**
+*/
+PRInt32
+_MD_CloseSocket(PRInt32 osfd)
+{
+ PRInt32 rv = -1;
+
+ rv = soclose((int) osfd );
+ if (rv < 0)
+ _PR_MD_MAP_SOCKET_ERROR(sock_errno());
+
+ return rv;
+}
+
+PRInt32
+_MD_SocketAvailable(PRFileDesc *fd)
+{
+ PRInt32 result;
+
+ if (ioctl(fd->secret->md.osfd, FIONREAD, (char *) &result, sizeof(result)) < 0) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, sock_errno());
+ return -1;
+ }
+ return result;
+}
+
+PRInt32
+_MD_Accept(PRFileDesc *fd, PRNetAddr *raddr, PRUint32 *rlen,
+ PRIntervalTime timeout )
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+#ifdef BSD_SELECT
+ fd_set rd;
+ struct timeval tv, *tvp;
+
+ FD_ZERO(&rd);
+ FD_SET(osfd, &rd);
+#else
+ int socks[1];
+ socks[0] = osfd;
+#endif
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ {
+ while ((rv = accept(osfd, (struct sockaddr *) raddr, (int *) rlen)) == -1)
+ {
+ if (((err = sock_errno()) == EWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+#ifdef BSD_SELECT
+ if ((rv = select(osfd + 1, &rd, NULL, NULL,NULL)) == -1) {
+#else
+ if ((rv = select(socks, 1, 0, 0, -1)) == -1) {
+#endif
+ _PR_MD_MAP_SELECT_ERROR(sock_errno());
+ break;
+ }
+ }
+ else {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ break;
+ }
+ }
+ return(rv);
+ }
+ else if (timeout == PR_INTERVAL_NO_WAIT)
+ {
+ if ((rv = accept(osfd, (struct sockaddr *) raddr, (int *) rlen)) == -1)
+ {
+ if (((err = sock_errno()) == EWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ else
+ {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ }
+ }
+ return(rv);
+ }
+ else
+ {
+retry:
+ if ((rv = accept(osfd, (struct sockaddr *) raddr, (int *) rlen)) == -1)
+ {
+ if (((err = sock_errno()) == EWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+#ifdef BSD_SELECT
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ rv = select(osfd + 1, &rd, NULL, NULL, tvp);
+#else
+ long lTimeout = PR_IntervalToMilliseconds(timeout);
+ rv = select(socks, 1, 0, 0, lTimeout);
+#endif
+ if (rv > 0) {
+ goto retry;
+ }
+ else if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ } else {
+ _PR_MD_MAP_SELECT_ERROR(sock_errno());
+ }
+ } else {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ }
+ }
+ }
+ return(rv);
+} /* end _MD_Accept() */
+
+
+
+PRInt32
+_PR_MD_CONNECT(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv;
+ int err, len;
+#ifdef BSD_SELECT
+ fd_set wd, ex;
+ struct timeval tv, *tvp;
+#else
+ int socks[1];
+ long lTimeout = -1;
+#endif
+
+ if ((rv = connect(osfd, (struct sockaddr *) addr, addrlen)) == -1)
+ {
+ err = sock_errno();
+ if ((!fd->secret->nonblocking) && (err == EINPROGRESS) || (err == EWOULDBLOCK))
+ {
+#ifdef BSD_SELECT
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ tvp = NULL;
+ else
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+
+ FD_ZERO(&wd);
+ FD_SET(osfd, &wd);
+ FD_ZERO(&ex);
+ FD_SET(osfd, &ex);
+ rv = select(osfd + 1, NULL, &wd, &ex, tvp);
+#else
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ lTimeout = -1;
+ else
+ {
+ lTimeout = PR_IntervalToMilliseconds(timeout);
+ }
+
+ socks[0] = osfd;
+ rv = select(socks, 0, 1, 1, lTimeout);
+#endif
+ if (rv > 0)
+ {
+#ifdef BSD_SELECT
+ if (FD_ISSET(osfd, &ex))
+ {
+ DosSleep(0);
+ len = sizeof(err);
+ if (getsockopt(osfd, SOL_SOCKET, SO_ERROR,
+ (char *) &err, &len) < 0)
+ {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(sock_errno());
+ return -1;
+ }
+ if (err != 0)
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ else
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return -1;
+ }
+ if (FD_ISSET(osfd, &wd))
+ {
+ /* it's connected */
+ return 0;
+ }
+#else
+ if (getsockopt(osfd, SOL_SOCKET, SO_ERROR,
+ (char *) &err, &len) < 0)
+ {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(sock_errno());
+ return -1;
+ }
+ else
+ return 0; /* It's connected ! */
+#endif
+ }
+ else if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return(-1);
+ } else if (rv < 0)
+ {
+ _PR_MD_MAP_SELECT_ERROR(sock_errno());
+ return(-1);
+ }
+ }
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ }
+ return rv;
+}
+
+
+PRInt32
+_PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen)
+{
+ PRInt32 rv;
+ int one = 1;
+
+ rv = bind(fd->secret->md.osfd, (struct sockaddr*) &(addr->inet), addrlen);
+
+ if (rv == -1) {
+ _PR_MD_MAP_BIND_ERROR(sock_errno());
+ return -1;
+ }
+
+ return 0;
+}
+
+
+PRInt32
+_PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+#ifdef BSD_SELECT
+ struct timeval tv, *tvp;
+ fd_set rd;
+#else
+ int socks[1];
+ long lTimeout = -1;
+#endif
+
+ while ((rv = recv( osfd, buf, amount, 0)) == -1)
+ {
+ if (((err = sock_errno()) == EWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+#ifdef BSD_SELECT
+ FD_ZERO(&rd);
+ FD_SET(osfd, &rd);
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ {
+ tvp = NULL;
+ }
+ else
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ if ((rv = select(osfd + 1, &rd, NULL, NULL, tvp)) == -1)
+#else
+ socks[0] = osfd;
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ {
+ lTimeout = -1;
+ }
+ else
+ {
+ lTimeout = PR_IntervalToMilliseconds(timeout);
+ }
+ if ((rv = select(socks, 1, 0, 0, lTimeout)) == -1)
+#endif
+ {
+ _PR_MD_MAP_SELECT_ERROR(sock_errno());
+ return -1;
+ }
+ else if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ }
+ else
+ {
+ _PR_MD_MAP_RECV_ERROR(err);
+ break;
+ }
+ } /* end while() */
+ return(rv);
+}
+
+PRInt32
+_PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+#ifdef BSD_SELECT
+ struct timeval tv, *tvp;
+ fd_set wd;
+#else
+ int socks[1];
+ long lTimeout = -1;
+#endif
+ PRInt32 bytesSent = 0;
+
+ while(bytesSent < amount )
+ {
+ while ((rv = send( osfd, (char *) buf, amount, 0 )) == -1)
+ {
+ if (((err = sock_errno()) == EWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+#ifdef BSD_SELECT
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT )
+ {
+ tvp = NULL;
+ }
+ else
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET(osfd, &wd);
+ if ((rv = select( osfd + 1, NULL, &wd, NULL,tvp)) == -1) {
+#else
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT )
+ {
+ lTimeout = -1;
+ }
+ else
+ {
+ lTimeout = PR_IntervalToMilliseconds(timeout);
+ }
+ socks[0] = osfd;
+ if ((rv = select( socks, 0, 1, 0, lTimeout)) == -1) {
+#endif
+ _PR_MD_MAP_SELECT_ERROR(sock_errno());
+ break;
+ }
+ if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ }
+ else {
+ _PR_MD_MAP_SEND_ERROR(err);
+ return -1;
+ }
+ }
+ bytesSent += rv;
+ if (fd->secret->nonblocking)
+ {
+ break;
+ }
+ if ((rv >= 0) && (bytesSent < amount ))
+ {
+#ifdef BSD_SELECT
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT )
+ {
+ tvp = NULL;
+ }
+ else
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET(osfd, &wd);
+ if ((rv = select(osfd + 1, NULL, &wd, NULL,tvp)) == -1) {
+#else
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT )
+ {
+ lTimeout = -1;
+ }
+ else
+ {
+ lTimeout = PR_IntervalToMilliseconds(timeout);
+ }
+ socks[0] = osfd;
+ if ((rv = select(socks, 0, 1, 0,lTimeout)) == -1) {
+#endif
+ _PR_MD_MAP_SELECT_ERROR(sock_errno());
+ break;
+ }
+ if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ }
+ }
+ return bytesSent;
+}
+
+PRInt32
+_PR_MD_SENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ PRInt32 bytesSent = 0;
+#ifdef BSD_SELECT
+ struct timeval tv, *tvp;
+ fd_set wd;
+#else
+ int socks[1];
+ long lTimeout = -1;
+#endif
+
+ while(bytesSent < amount)
+ {
+ while ((rv = sendto( osfd, (char *) buf, amount, 0, (struct sockaddr *) addr,
+ addrlen)) == -1)
+ {
+ if (((err = sock_errno()) == EWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+#ifdef BSD_SELECT
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT )
+ {
+ tvp = NULL;
+ }
+ else
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET(osfd, &wd);
+ if ((rv = select(osfd + 1, NULL, &wd, NULL, tvp)) == -1) {
+#else
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT )
+ {
+ lTimeout = -1;
+ }
+ else
+ {
+ lTimeout = PR_IntervalToMilliseconds(timeout);
+ }
+ socks[0] = osfd;
+ if ((rv = select(socks, 0, 1, 0, lTimeout)) == -1) {
+#endif
+ _PR_MD_MAP_SELECT_ERROR(sock_errno());
+ break;
+ }
+ if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ }
+ else {
+ _PR_MD_MAP_SENDTO_ERROR(err);
+ return -1;
+ }
+ }
+ bytesSent += rv;
+ if (fd->secret->nonblocking)
+ {
+ break;
+ }
+ if ((rv >= 0) && (bytesSent < amount ))
+ {
+#ifdef BSD_SELECT
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT )
+ {
+ tvp = NULL;
+ }
+ else
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET(osfd, &wd);
+ if ((rv = select( osfd + 1, NULL, &wd, NULL, tvp)) == -1) {
+#else
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT )
+ {
+ lTimeout = -1;
+ }
+ else
+ {
+ lTimeout = PR_IntervalToMilliseconds(timeout);
+ }
+ socks[0] = osfd;
+ if ((rv = select( socks, 0, 1, 0, lTimeout)) == -1) {
+#endif
+ _PR_MD_MAP_SELECT_ERROR(sock_errno());
+ break;
+ }
+ if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ }
+ }
+ return bytesSent;
+}
+
+PRInt32
+_PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ PRUint32 addrlen_temp = *addrlen;
+#ifdef BSD_SELECT
+ struct timeval tv, *tvp;
+ fd_set rd;
+#else
+ int socks[1];
+ long lTimeout = -1;
+#endif
+
+ while ((rv = recvfrom( osfd, (char *) buf, amount, 0, (struct sockaddr *) addr,
+ (int *) addrlen)) == -1)
+ {
+ if (((err = sock_errno()) == EWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+#ifdef BSD_SELECT
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ {
+ tvp = NULL;
+ }
+ else
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&rd);
+ FD_SET(osfd, &rd);
+ if ((rv = select(osfd + 1, &rd, NULL, NULL, tvp)) == -1)
+#else
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ {
+ lTimeout = -1;
+ }
+ else
+ {
+ lTimeout = PR_IntervalToMilliseconds(timeout);
+ }
+ socks[0] = osfd;
+ if ((rv = select(socks, 1, 0, 0, lTimeout)) == -1)
+#endif
+ {
+ _PR_MD_MAP_SELECT_ERROR(sock_errno());
+ return -1;
+ } else if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+
+ /* recvfrom blows this value away if it fails first time */
+ *addrlen = addrlen_temp;
+ }
+ else
+ {
+ _PR_MD_MAP_RECVFROM_ERROR(err);
+ break;
+ }
+ }
+ return(rv);
+}
+
+PRInt32
+_PR_MD_WRITEV(PRFileDesc *fd, PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout)
+{
+ int index;
+ int sent = 0;
+ int rv;
+
+ for (index=0; index < iov_size; index++)
+ {
+ rv = _PR_MD_SEND(fd, iov[index].iov_base, iov[index].iov_len, 0, timeout);
+ if (rv > 0)
+ sent += rv;
+ if ( rv != iov[index].iov_len )
+ {
+ if (rv < 0)
+ {
+ if (fd->secret->nonblocking
+ && (PR_GetError() == PR_WOULD_BLOCK_ERROR)
+ && (sent > 0))
+ {
+ return sent;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ /* Only a nonblocking socket can have partial sends */
+ PR_ASSERT(fd->secret->nonblocking);
+ return sent;
+ }
+ }
+ return sent;
+}
+
+PRInt32
+_PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how)
+{
+PRInt32 rv;
+
+ rv = shutdown(fd->secret->md.osfd, how);
+ if (rv < 0)
+ _PR_MD_MAP_SHUTDOWN_ERROR(sock_errno());
+ return rv;
+}
+
+PRStatus
+_PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
+{
+ PRInt32 rv;
+
+ rv = getsockname((int)fd->secret->md.osfd, (struct sockaddr *)addr, (int *) len);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_GETSOCKNAME_ERROR(sock_errno());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
+{
+ PRInt32 rv;
+
+ rv = getpeername((int)fd->secret->md.osfd, (struct sockaddr *)addr, (int *) len);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_GETPEERNAME_ERROR(sock_errno());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen)
+{
+ PRInt32 rv;
+
+ rv = getsockopt((int)fd->secret->md.osfd, level, optname, optval, optlen);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(sock_errno());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen)
+{
+ PRInt32 rv;
+
+ rv = setsockopt((int)fd->secret->md.osfd, level, optname, (char *) optval, optlen);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_SETSOCKOPT_ERROR(sock_errno());
+ return PR_FAILURE;
+ }
+}
+
+void
+_MD_MakeNonblock(PRFileDesc *f)
+{
+ return; /* do nothing! */
+}
diff --git a/pr/src/md/os2/os2thred.c b/pr/src/md/os2/os2thred.c
new file mode 100644
index 00000000..e51b3eb3
--- /dev/null
+++ b/pr/src/md/os2/os2thred.c
@@ -0,0 +1,245 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+#include <process.h> /* for _beginthread() */
+
+APIRET (* APIENTRY QueryThreadContext)(TID, ULONG, PCONTEXTRECORD);
+
+/* --- globals ------------------------------------------------ */
+_NSPR_TLS* pThreadLocalStorage = 0;
+_PRInterruptTable _pr_interruptTable[] = { { 0 } };
+
+PR_IMPLEMENT(void)
+_PR_MD_ENSURE_TLS()
+{
+ if(!pThreadLocalStorage)
+ {
+ /* Allocate thread local storage (TLS). Note, that only 32 bytes can
+ * be allocated at a time.
+ */
+ int rc = DosAllocThreadLocalMemory(sizeof(_NSPR_TLS) / 4, (PULONG*)&pThreadLocalStorage);
+ PR_ASSERT(rc == NO_ERROR);
+ memset(pThreadLocalStorage, 0, sizeof(_NSPR_TLS));
+ }
+}
+
+PR_IMPLEMENT(void)
+_PR_MD_EARLY_INIT()
+{
+ HMODULE hmod;
+
+ if (DosLoadModule(NULL, 0, "DOSCALL1.DLL", &hmod) == 0)
+ DosQueryProcAddr(hmod, 877, "DOSQUERYTHREADCONTEXT",
+ (PFN *)&QueryThreadContext);
+}
+
+PR_IMPLEMENT(void)
+_PR_MD_INIT_PRIMORDIAL_THREAD(PRThread *thread)
+{
+ PTIB ptib;
+ PPIB ppib;
+ PRUword rc;
+
+ rc = DosGetInfoBlocks(&ptib, &ppib);
+
+ thread->md.handle = ptib->tib_ptib2->tib2_ultid;
+}
+
+
+PR_IMPLEMENT(PRStatus)
+_PR_MD_INIT_THREAD(PRThread *thread)
+{
+ APIRET rc;
+
+ if (thread->flags & _PR_PRIMORDIAL)
+ _PR_MD_INIT_PRIMORDIAL_THREAD(thread);
+
+ /* Create the blocking IO semaphore */
+ _PR_MD_NEW_SEM(&thread->md.blocked_sema, 1);
+ return (thread->md.blocked_sema.sem != NULL) ? PR_SUCCESS : PR_FAILURE;
+}
+
+PR_IMPLEMENT(PRStatus)
+_PR_MD_CREATE_THREAD(PRThread *thread,
+ void (*start)(void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ thread->md.handle = thread->id = (TID) _beginthread(
+ (void(* _Optlink)(void*))start,
+ NULL,
+ thread->stack->stackSize,
+ thread);
+ if(thread->md.handle == -1) {
+ return PR_FAILURE;
+ }
+ _PR_MD_SET_PRIORITY(&(thread->md), priority);
+
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(void)
+_PR_MD_YIELD(void)
+{
+ /* Isn't there some problem with DosSleep(0) on OS/2? */
+ DosSleep(0);
+}
+
+PR_IMPLEMENT(void)
+_PR_MD_SET_PRIORITY(_MDThread *thread, PRThreadPriority newPri)
+{
+ int nativePri;
+ BOOL rv;
+
+ if (newPri < PR_PRIORITY_FIRST) {
+ newPri = PR_PRIORITY_FIRST;
+ } else if (newPri > PR_PRIORITY_LAST) {
+ newPri = PR_PRIORITY_LAST;
+ }
+ switch (newPri) {
+ case PR_PRIORITY_LOW:
+ nativePri = PRTYC_IDLETIME;
+ break;
+ case PR_PRIORITY_NORMAL:
+ nativePri = PRTYC_REGULAR;
+ break;
+ case PR_PRIORITY_HIGH:
+ nativePri = PRTYC_FOREGROUNDSERVER;
+ break;
+ case PR_PRIORITY_URGENT:
+ nativePri = PRTYC_TIMECRITICAL;
+ }
+ rv = DosSetPriority(PRTYS_THREAD, nativePri, 0, thread->handle);
+ PR_ASSERT(rv == NO_ERROR);
+ if (rv != NO_ERROR) {
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("PR_SetThreadPriority: can't set thread priority\n"));
+ }
+ return;
+}
+
+PR_IMPLEMENT(void)
+_PR_MD_CLEAN_THREAD(PRThread *thread)
+{
+ /* Just call _PR_MD_EXIT_THREAD for now */
+ _PR_MD_EXIT_THREAD(thread);
+}
+
+PR_IMPLEMENT(void)
+_PR_MD_EXIT_THREAD(PRThread *thread)
+{
+ _PR_MD_DESTROY_SEM(&thread->md.blocked_sema);
+
+ if (thread->md.handle) {
+ /* DosKillThread will not kill a suspended thread, but it will mark it
+ * for death; we must resume it after killing it to make sure it knows
+ * it is about to die (pretty wicked, huh?).
+ *
+ * DosKillThread will not kill the current thread, instead we must use
+ * DosExit.
+ */
+ if ( thread != _MD_CURRENT_THREAD() ) {
+ DosKillThread( thread->md.handle );
+ DosResumeThread( thread->md.handle );
+ } else {
+ _endthread();
+ }
+ thread->md.handle = 0;
+ }
+
+ _PR_MD_SET_CURRENT_THREAD(NULL);
+}
+
+
+PR_IMPLEMENT(void)
+_PR_MD_EXIT(PRIntn status)
+{
+ _exit(status);
+}
+
+#ifdef HAVE_THREAD_AFFINITY
+PR_EXTERN(PRInt32)
+_PR_MD_SETTHREADAFFINITYMASK(PRThread *thread, PRUint32 mask )
+{
+ /* Can we do this on OS/2? Only on SMP versions? */
+ PR_ASSERT(!"Not implemented");
+ return 0;
+
+ /* This is what windows does:
+ int rv;
+
+ rv = SetThreadAffinityMask(thread->md.handle, mask);
+
+ return rv?0:-1;
+ */
+}
+
+PR_EXTERN(PRInt32)
+_PR_MD_GETTHREADAFFINITYMASK(PRThread *thread, PRUint32 *mask)
+{
+ /* Can we do this on OS/2? Only on SMP versions? */
+ PR_ASSERT(!"Not implemented");
+ return 0;
+
+ /* This is what windows does:
+ PRInt32 rv, system_mask;
+
+ rv = GetProcessAffinityMask(GetCurrentProcess(), mask, &system_mask);
+
+ return rv?0:-1;
+ */
+}
+#endif /* HAVE_THREAD_AFFINITY */
+
+PR_IMPLEMENT(void)
+_PR_MD_SUSPEND_CPU(_PRCPU *cpu)
+{
+ _PR_MD_SUSPEND_THREAD(cpu->thread);
+}
+
+PR_IMPLEMENT(void)
+_PR_MD_RESUME_CPU(_PRCPU *cpu)
+{
+ _PR_MD_RESUME_THREAD(cpu->thread);
+}
+
+PR_IMPLEMENT(void)
+_PR_MD_SUSPEND_THREAD(PRThread *thread)
+{
+ if (_PR_IS_NATIVE_THREAD(thread)) {
+ APIRET rc;
+
+ /* XXXMB - DosSuspendThread() is not a blocking call; how do we
+ * know when the thread is *REALLY* suspended?
+ */
+ rc = DosSuspendThread(thread->md.handle);
+ PR_ASSERT(rc == NO_ERROR);
+ }
+}
+
+PR_IMPLEMENT(void)
+_PR_MD_RESUME_THREAD(PRThread *thread)
+{
+ if (_PR_IS_NATIVE_THREAD(thread)) {
+ DosResumeThread(thread->md.handle);
+ }
+}
+
diff --git a/pr/src/md/prosdep.c b/pr/src/md/prosdep.c
new file mode 100644
index 00000000..fc2d35cb
--- /dev/null
+++ b/pr/src/md/prosdep.c
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "prbit.h"
+#include "prsystem.h"
+
+#if defined(XP_MAC)
+#include "prosdep.h"
+#else
+#include "md/prosdep.h"
+#endif
+
+#ifdef XP_UNIX
+#include <unistd.h>
+#endif
+
+PRInt32 _pr_pageShift;
+PRInt32 _pr_pageSize;
+
+/*
+** Get system page size
+*/
+static void GetPageSize(void)
+{
+ PRInt32 pageSize;
+
+ /* Get page size */
+#ifdef XP_UNIX
+#if defined SUNOS4 || defined LINUX || defined BSDI || defined AIX \
+ || defined FREEBSD
+ _pr_pageSize = getpagesize();
+#elif defined(HPUX)
+ /* I have no idea. Don't get me started. --Rob */
+ _pr_pageSize = sysconf(_SC_PAGE_SIZE);
+#else
+ _pr_pageSize = sysconf(_SC_PAGESIZE);
+#endif
+#endif /* XP_UNIX */
+
+#ifdef XP_MAC
+ _pr_pageSize = 4096;
+#endif /* XP_MAC */
+
+#ifdef XP_PC
+#ifdef _WIN32
+ SYSTEM_INFO info;
+ GetSystemInfo(&info);
+ _pr_pageSize = info.dwPageSize;
+#else
+ _pr_pageSize = 4096;
+#endif
+#endif /* XP_PC */
+
+ pageSize = _pr_pageSize;
+ PR_CEILING_LOG2(_pr_pageShift, pageSize);
+}
+
+PR_IMPLEMENT(PRInt32) PR_GetPageShift(void)
+{
+ if (!_pr_pageSize) {
+ GetPageSize();
+ }
+ return _pr_pageShift;
+}
+
+PR_IMPLEMENT(PRInt32) PR_GetPageSize(void)
+{
+ if (!_pr_pageSize) {
+ GetPageSize();
+ }
+ return _pr_pageSize;
+}
diff --git a/pr/src/md/unix/Makefile b/pr/src/md/unix/Makefile
new file mode 100644
index 00000000..2c3a3f77
--- /dev/null
+++ b/pr/src/md/unix/Makefile
@@ -0,0 +1,229 @@
+#
+# The contents of this file are subject to the Netscape Public License
+# Version 1.0 (the "NPL"); you may not use this file except in
+# compliance with the NPL. You may obtain a copy of the NPL at
+# http://www.mozilla.org/NPL/
+#
+# Software distributed under the NPL is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+# for the specific language governing rights and limitations under the
+# NPL.
+#
+# The Initial Developer of this code under the NPL is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1998 Netscape Communications Corporation. All Rights
+# Reserved.
+#
+
+MOD_DEPTH = ../../../..
+
+include $(MOD_DEPTH)/config/config.mk
+
+# Disable optimization of the nspr on SunOS4.1.3
+ifeq ($(OS_ARCH),SunOS)
+ifeq ($(OS_RELEASE),4.1.3_U1)
+OPTIMIZER =
+endif
+endif
+
+CSRCS = \
+ unix.c \
+ unix_errors.c \
+ uxproces.c \
+ uxwrap.c \
+ $(NULL)
+
+PTH_USER_CSRCS = \
+ pthreads_user.c \
+ $(NULL)
+
+IRIX_CSRCS = \
+ irix.c \
+ $(NULL)
+
+SUNOS4_CSRCS = \
+ sunos4.c \
+ $(NULL)
+
+SOLARIS_CSRCS = \
+ solaris.c \
+ $(NULL)
+
+AIX_CSRCS = \
+ aix.c \
+ $(NULL)
+
+FREEBSD_CSRCS = \
+ freebsd.c \
+ $(NULL)
+
+BSDI_CSRCS = \
+ bsdi.c \
+ $(NULL)
+
+HPUX_CSRCS = \
+ hpux.c \
+ $(NULL)
+
+OSF1_CSRCS = \
+ osf1.c \
+ $(NULL)
+
+LINUX_CSRCS = \
+ linux.c \
+ $(NULL)
+
+UNIXWARE_CSRCS = \
+ unixware.c \
+ $(NULL)
+
+RELIANTUNIX_CSRCS = \
+ reliantunix.c \
+ $(NULL)
+
+NEC_CSRCS = \
+ nec.c \
+ $(NULL)
+
+SONY_CSRCS = \
+ sony.c \
+ $(NULL)
+
+NCR_CSRCS = \
+ ncr.c \
+ $(NULL)
+
+SCOOS_CSRCS = \
+ scoos.c \
+ $(NULL)
+
+ifeq ($(PTHREADS_USER),1)
+CSRCS += $(PTH_USER_CSRCS)
+endif
+
+ifeq ($(OS_ARCH),IRIX)
+CSRCS += $(IRIX_CSRCS)
+endif
+
+ifeq ($(OS_ARCH),SunOS)
+ifeq ($(OS_RELEASE),4.1.3_U1)
+CSRCS += $(SUNOS4_CSRCS)
+else
+CSRCS += $(SOLARIS_CSRCS)
+endif
+endif
+
+ifeq ($(OS_ARCH),AIX)
+CSRCS += $(AIX_CSRCS)
+endif
+ifeq ($(OS_ARCH),FreeBSD)
+CSRCS += $(FREEBSD_CSRCS)
+endif
+ifeq ($(OS_ARCH),BSD_386)
+CSRCS += $(BSDI_CSRCS)
+endif
+ifeq ($(OS_ARCH),HP-UX)
+CSRCS += $(HPUX_CSRCS)
+endif
+ifeq ($(OS_ARCH),OSF1)
+CSRCS += $(OSF1_CSRCS)
+endif
+ifeq ($(OS_ARCH),Linux)
+CSRCS += $(LINUX_CSRCS)
+endif
+ifeq ($(OS_ARCH),UNIXWARE)
+CSRCS += $(UNIXWARE_CSRCS)
+endif
+ifeq ($(OS_ARCH),ReliantUNIX)
+CSRCS += $(RELIANTUNIX_CSRCS)
+endif
+ifeq ($(OS_ARCH),NEC)
+CSRCS += $(NEC_CSRCS)
+endif
+ifeq ($(OS_ARCH),NEWS-OS)
+CSRCS += $(SONY_CSRCS)
+endif
+ifeq ($(OS_ARCH),NCR)
+CSRCS += $(NCR_CSRCS)
+endif
+ifeq ($(OS_ARCH),SCO_SV)
+CSRCS += $(SCOOS_CSRCS)
+endif
+
+#
+# Some Unix platforms have an assembly language file.
+# E.g., AIX 3.2, Solaris (both sparc and x86).
+#
+ifeq ($(OS_ARCH), AIX)
+ ifeq ($(OS_RELEASE), 3.2)
+ ASFILES = os_$(OS_ARCH).s
+ endif
+endif
+
+ifeq ($(OS_ARCH),SunOS)
+ ifeq ($(OS_TEST),i86pc)
+ ASFILES = os_$(OS_ARCH)_x86.s
+ else
+ ifneq ($(OS_RELEASE),4.1.3_U1)
+ ASFILES = os_$(OS_ARCH).s
+ endif
+ endif
+endif
+
+ifeq ($(OS_ARCH), ReliantUNIX)
+ ASFILES = os_$(OS_ARCH).s
+endif
+
+ifeq ($(OS_ARCH)$(OS_RELEASE),BSD_3862.1)
+ ASFILES = os_BSD_386_2.s
+endif
+
+TARGETS = $(OBJS)
+
+ifeq ($(OS_ARCH),AIX)
+ifneq ($(OS_RELEASE),4.2)
+ifneq ($(USE_PTHREADS), 1)
+#TARGETS += $(OBJDIR)/aixwrap.o
+endif
+endif
+endif
+
+ifeq ($(OS_ARCH),SunOS)
+ ifneq ($(OS_RELEASE),4.1.3_U1)
+ ifeq ($(OS_TEST),sun4u)
+ LIBRARY_NAME = $(ULTRASPARC_LIBRARY)
+ LIBRARY_VERSION = $(MOD_VERSION)
+ ULTRASPARC_ASFILES = os_$(OS_ARCH)_ultrasparc.s
+ ULTRASPARC_ASOBJS = $(addprefix $(OBJDIR)/,$(ULTRASPARC_ASFILES:.s=.o))
+ TARGETS += $(ULTRASPARC_ASOBJS) $(SHARED_LIBRARY)
+ endif
+ endif
+endif
+
+INCLUDES = -I$(DIST)/include/private -I$(DIST)/include
+
+include $(MOD_DEPTH)/config/rules.mk
+
+export:: $(TARGETS)
+#ifeq ($(OS_ARCH),AIX)
+#ifneq ($(OS_RELEASE),4.2)
+#ifneq ($(USE_PTHREADS), 1)
+# $(INSTALL) -m 444 $(OBJDIR)/aixwrap.o $(DIST)/lib
+#endif
+#endif
+#endif
+ifeq ($(OS_ARCH),SunOS)
+ifneq ($(OS_RELEASE),4.1.3_U1)
+ifeq ($(OS_TEST),sun4u)
+$(SHARED_LIBRARY): $(ULTRASPARC_ASOBJS)
+ $(LD) -G -z text -o $@ $(ULTRASPARC_ASOBJS)
+ $(INSTALL) -m 444 $(SHARED_LIBRARY) $(DIST)/lib
+
+$(ULTRASPARC_ASOBJS): $(ULTRASPARC_ASFILES)
+ as -o $@ -K PIC -P -D_ASM -D__STDC__=0 -xarch=v8plus $(ULTRASPARC_ASFILES)
+endif
+endif
+endif
+
+
+install:: export
diff --git a/pr/src/md/unix/aix.c b/pr/src/md/unix/aix.c
new file mode 100644
index 00000000..8ae7e303
--- /dev/null
+++ b/pr/src/md/unix/aix.c
@@ -0,0 +1,165 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+/*
+ * NSPR 2.0 overrides the system select() and poll() functions.
+ * On AIX 4.2, we use dlopen("/unix", 0) and dlsym() to get to the
+ * original system select() and poll() functions.
+ */
+
+#ifndef AIX4_1
+
+#include <sys/select.h>
+#include <sys/poll.h>
+#include <dlfcn.h>
+
+static void *aix_handle = NULL;
+static int (*aix_select_fcn)() = NULL;
+static int (*aix_poll_fcn)() = NULL;
+
+int _MD_SELECT(int width, fd_set *r, fd_set *w, fd_set *e, struct timeval *t)
+{
+ int rv;
+
+ if (!aix_select_fcn) {
+ if (!aix_handle) {
+ aix_handle = dlopen("/unix",0);
+ if (!aix_handle) {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return -1;
+ }
+ }
+ aix_select_fcn = (int(*)())dlsym(aix_handle,"select");
+ if (!aix_select_fcn) {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return -1;
+ }
+ }
+ rv = (*aix_select_fcn)(width, r, w, e, t);
+ return rv;
+}
+
+int _MD_POLL(void *listptr, unsigned long nfds, long timeout)
+{
+ int rv;
+
+ if (!aix_poll_fcn) {
+ if (!aix_handle) {
+ aix_handle = dlopen("/unix",0);
+ if (!aix_handle) {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return -1;
+ }
+ }
+ aix_poll_fcn = (int(*)())dlsym(aix_handle,"poll");
+ if (!aix_poll_fcn) {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return -1;
+ }
+ }
+ rv = (*aix_poll_fcn)(listptr, nfds, timeout);
+ return rv;
+}
+
+#else
+
+/*
+ * In AIX versions prior to 4.2, we use the two-step rename/link trick.
+ * The binary must contain at least one "poll" symbol for linker's rename
+ * to work. So we must have this dummy function that references poll().
+ */
+#include <sys/poll.h>
+void _pr_aix_dummy()
+{
+ poll(0,0,0);
+}
+
+#endif /* !AIX4_1 */
+
+#if !defined(PTHREADS_USER)
+
+void _MD_EarlyInit(void)
+{
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+#ifndef _PR_PTHREADS
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+#else
+ *np = 0;
+ return NULL;
+#endif
+}
+
+#ifndef _PR_PTHREADS
+PR_IMPLEMENT(void)
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PR_IMPLEMENT(PRStatus)
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus)
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus)
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for AIX */
+PR_IMPLEMENT(void)
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for AIX.");
+}
+
+PR_IMPLEMENT(PRStatus)
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for AIX.");
+}
+#endif /* _PR_PTHREADS */
+#endif /* PTHREADS_USER */
diff --git a/pr/src/md/unix/aixwrap.c b/pr/src/md/unix/aixwrap.c
new file mode 100644
index 00000000..e5c0a630
--- /dev/null
+++ b/pr/src/md/unix/aixwrap.c
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * File: aixwrap.c
+ * Description:
+ * This file contains a single function, _MD_SELECT(), which simply
+ * invokes the select() function. This file is used in an ugly
+ * hack to override the system select() function on AIX releases
+ * prior to 4.2. (On AIX 4.2, we use a different mechanism to
+ * override select().)
+ */
+
+#ifndef AIX4_1
+#error aixwrap.c should only be used on AIX 4.1
+#else
+
+#include <sys/select.h>
+#include <sys/poll.h>
+
+int _MD_SELECT(int width, fd_set *r, fd_set *w, fd_set *e, struct timeval *t)
+{
+ return select(width, r, w, e, t);
+}
+
+int _MD_POLL(void *listptr, unsigned long nfds, long timeout)
+{
+ return poll(listptr, nfds, timeout);
+}
+
+#endif /* AIX4_1 */
diff --git a/pr/src/md/unix/bsdi.c b/pr/src/md/unix/bsdi.c
new file mode 100644
index 00000000..dae92a47
--- /dev/null
+++ b/pr/src/md/unix/bsdi.c
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+#include <signal.h>
+
+void _MD_EarlyInit(void)
+{
+ /*
+ * Ignore FPE because coercion of a NaN to an int causes SIGFPE
+ * to be raised.
+ */
+ struct sigaction act;
+
+ act.sa_handler = SIG_IGN;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+ sigaction(SIGFPE, &act, 0);
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+}
+
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for BSDI */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for BSDI.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for BSDI.");
+ return PR_FAILURE;
+}
diff --git a/pr/src/md/unix/freebsd.c b/pr/src/md/unix/freebsd.c
new file mode 100644
index 00000000..2cedf308
--- /dev/null
+++ b/pr/src/md/unix/freebsd.c
@@ -0,0 +1,232 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+#include <signal.h>
+
+void _MD_EarlyInit(void)
+{
+ /*
+ * Ignore FPE because coercion of a NaN to an int causes SIGFPE
+ * to be raised.
+ */
+ struct sigaction act;
+
+ act.sa_handler = SIG_IGN;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+ sigaction(SIGFPE, &act, 0);
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+#ifndef _PR_PTHREADS
+ if (isCurrent) {
+ (void) sigsetjmp(CONTEXT(t), 1);
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+#else
+ *np = 0;
+ return NULL;
+#endif
+}
+
+#ifndef _PR_PTHREADS
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for OSF1 */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for OSF1.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for OSF1.");
+ return PR_FAILURE;
+}
+#endif /* ! _PR_PTHREADS */
+
+#if defined(_PR_NEED_FAKE_POLL)
+
+#include <fcntl.h>
+
+int poll(struct pollfd *filedes, unsigned long nfds, int timeout)
+{
+ int i;
+ int rv;
+ int maxfd;
+ fd_set rd, wr, ex;
+ struct timeval tv, *tvp;
+
+ if (timeout < 0 && timeout != -1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (timeout == -1) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+ tvp = &tv;
+ }
+
+ maxfd = -1;
+ FD_ZERO(&rd);
+ FD_ZERO(&wr);
+ FD_ZERO(&ex);
+
+ for (i = 0; i < nfds; i++) {
+ int osfd = filedes[i].fd;
+ int events = filedes[i].events;
+ PRBool fdHasEvent = PR_FALSE;
+
+ if (osfd < 0) {
+ continue; /* Skip this osfd. */
+ }
+
+ /*
+ * Map the native poll flags to nspr poll flags.
+ * POLLIN, POLLRDNORM ===> PR_POLL_READ
+ * POLLOUT, POLLWRNORM ===> PR_POLL_WRITE
+ * POLLPRI, POLLRDBAND ===> PR_POLL_EXCEPTION
+ * POLLNORM, POLLWRBAND (and POLLMSG on some platforms)
+ * are ignored.
+ *
+ * The output events POLLERR and POLLHUP are never turned on.
+ * POLLNVAL may be turned on.
+ */
+
+ if (events & (POLLIN | POLLRDNORM)) {
+ FD_SET(osfd, &rd);
+ fdHasEvent = PR_TRUE;
+ }
+ if (events & (POLLOUT | POLLWRNORM)) {
+ FD_SET(osfd, &wr);
+ fdHasEvent = PR_TRUE;
+ }
+ if (events & (POLLPRI | POLLRDBAND)) {
+ FD_SET(osfd, &ex);
+ fdHasEvent = PR_TRUE;
+ }
+ if (fdHasEvent && osfd > maxfd) {
+ maxfd = osfd;
+ }
+ }
+
+ rv = select(maxfd + 1, &rd, &wr, &ex, tvp);
+
+ /* Compute poll results */
+ if (rv > 0) {
+ rv = 0;
+ for (i = 0; i < nfds; i++) {
+ PRBool fdHasEvent = PR_FALSE;
+
+ filedes[i].revents = 0;
+ if (filedes[i].fd < 0) {
+ continue;
+ }
+ if (FD_ISSET(filedes[i].fd, &rd)) {
+ if (filedes[i].events & POLLIN) {
+ filedes[i].revents |= POLLIN;
+ }
+ if (filedes[i].events & POLLRDNORM) {
+ filedes[i].revents |= POLLRDNORM;
+ }
+ fdHasEvent = PR_TRUE;
+ }
+ if (FD_ISSET(filedes[i].fd, &wr)) {
+ if (filedes[i].events & POLLOUT) {
+ filedes[i].revents |= POLLOUT;
+ }
+ if (filedes[i].events & POLLWRNORM) {
+ filedes[i].revents |= POLLWRNORM;
+ }
+ fdHasEvent = PR_TRUE;
+ }
+ if (FD_ISSET(filedes[i].fd, &ex)) {
+ if (filedes[i].events & POLLPRI) {
+ filedes[i].revents |= POLLPRI;
+ }
+ if (filedes[i].events & POLLRDBAND) {
+ filedes[i].revents |= POLLRDBAND;
+ }
+ fdHasEvent = PR_TRUE;
+ }
+ if (fdHasEvent) {
+ rv++;
+ }
+ }
+ PR_ASSERT(rv > 0);
+ } else if (rv == -1 && errno == EBADF) {
+ rv = 0;
+ for (i = 0; i < nfds; i++) {
+ filedes[i].revents = 0;
+ if (filedes[i].fd < 0) {
+ continue;
+ }
+ if (fcntl(filedes[i].fd, F_GETFL, 0) == -1) {
+ filedes[i].revents = POLLNVAL;
+ rv++;
+ }
+ }
+ PR_ASSERT(rv > 0);
+ }
+ PR_ASSERT(-1 != timeout || rv != 0);
+
+ return rv;
+}
+#endif /* _PR_NEED_FAKE_POLL */
diff --git a/pr/src/md/unix/hpux.c b/pr/src/md/unix/hpux.c
new file mode 100644
index 00000000..701a854b
--- /dev/null
+++ b/pr/src/md/unix/hpux.c
@@ -0,0 +1,334 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+#if defined(HPUX10_30) || defined(HPUX11)
+/* for fesettrapenable */
+#include <fenv.h>
+#else
+/* for fpsetmask */
+#include <math.h>
+#endif
+#include <setjmp.h>
+#include <signal.h>
+#include <values.h>
+
+/*
+** On HP-UX we need to define a SIGFPE handler because coercion of a
+** NaN to an int causes SIGFPE to be raised. Thanks to Marianne
+** Mueller and Doug Priest at SunSoft for this fix.
+**
+** Under DCE threads, sigaction() installs a per-thread signal handler,
+** so we use the sigvector() interface to install a process-wide
+** handler.
+*/
+
+#ifdef _PR_DCETHREADS
+static void
+CatchFPE(int sig, int code, struct sigcontext *scp)
+{
+ unsigned i, e;
+ int r, t;
+ int *source, *destination;
+
+ /* check excepting instructions */
+ for ( i = 0; i < 7; i++ ) {
+ e = *(i+&(scp->sc_sl.sl_ss.ss_frexcp1));
+ if ( e & 0xfc000000 != 0 ) {
+ if ((e & 0xf4017720) == 0x24010200) {
+ r = ((e >> 20) & 0x3e);
+ t = (e & 0x1f) << 1;
+ if (e & 0x08000000) {
+ r |= (e >> 7) & 1;
+ t |= (e >> 6) & 1;
+ }
+ source = (int *)(&scp->sc_sl.sl_ss.ss_frstat + r);
+ destination = (int *)(&scp->sc_sl.sl_ss.ss_frstat + t);
+ *destination = *source < 0 ? -MAXINT-1 : MAXINT;
+ }
+ }
+ *(i+&(scp->sc_sl.sl_ss.ss_frexcp1)) = 0;
+ }
+
+ /* clear T-bit */
+ scp->sc_sl.sl_ss.ss_frstat &= ~0x40;
+}
+#else /* _PR_DCETHREADS */
+static void
+CatchFPE(int sig, siginfo_t *info, void *context)
+{
+ ucontext_t *ucp = (ucontext_t *) context;
+ unsigned i, e;
+ int r, t;
+ int *source, *destination;
+
+ /* check excepting instructions */
+ for ( i = 0; i < 7; i++ ) {
+ e = *(i+&(ucp->uc_mcontext.ss_frexcp1));
+ if ( e & 0xfc000000 != 0 ) {
+ if ((e & 0xf4017720) == 0x24010200) {
+ r = ((e >> 20) & 0x3e);
+ t = (e & 0x1f) << 1;
+ if (e & 0x08000000) {
+ r |= (e >> 7) & 1;
+ t |= (e >> 6) & 1;
+ }
+ source = (int *)(&ucp->uc_mcontext.ss_frstat + r);
+ destination = (int *)(&ucp->uc_mcontext.ss_frstat + t);
+ *destination = *source < 0 ? -MAXINT-1 : MAXINT;
+ }
+ }
+ *(i+&(ucp->uc_mcontext.ss_frexcp1)) = 0;
+ }
+
+ /* clear T-bit */
+ ucp->uc_mcontext.ss_frstat &= ~0x40;
+}
+#endif /* _PR_DCETHREADS */
+
+void _MD_hpux_install_sigfpe_handler(void)
+{
+#ifdef _PR_DCETHREADS
+ struct sigvec v;
+
+ v.sv_handler = CatchFPE;
+ v.sv_mask = 0;
+ v.sv_flags = 0;
+ sigvector(SIGFPE, &v, NULL);
+#else
+ struct sigaction act;
+
+ sigaction(SIGFPE, NULL, &act);
+ act.sa_flags |= SA_SIGINFO;
+ act.sa_sigaction = CatchFPE;
+ sigaction(SIGFPE, &act, NULL);
+#endif /* _PR_DCETHREADS */
+
+#if defined(HPUX10_30) || defined(HPUX11)
+ fesettrapenable(FE_INVALID);
+#else
+ fpsetmask(FP_X_INV);
+#endif
+}
+
+#if !defined(PTHREADS_USER)
+
+void _MD_EarlyInit(void)
+{
+ _MD_hpux_install_sigfpe_handler();
+
+#ifndef _PR_PTHREADS
+ /*
+ * The following piece of code is taken from ns/nspr/src/md_HP-UX.c.
+ * In the comment for revision 1.6, dated 1995/09/11 23:33:34,
+ * robm says:
+ * This version has some problems which need to be addressed.
+ * First, intercept all system calls and prevent them from
+ * executing the library code which performs stack switches
+ * before normal system call invocation. In order for library
+ * calls which make system calls to work (like stdio), however,
+ * we must also allocate our own stack and switch the primordial
+ * stack to use it. This isn't so bad, except that I fudged the
+ * backtrace length when copying the old stack to the new one.
+ *
+ * This is the original comment of robm in the code:
+ * XXXrobm Horrific. To avoid a problem with HP's system call
+ * code, we allocate a new stack for the primordial thread and
+ * use it. However, we don't know how far back the original stack
+ * goes. We should create a routine that performs a backtrace and
+ * finds out just how much we need to copy. As a temporary measure,
+ * I just copy an arbitrary guess.
+ *
+ * In an email to servereng dated 2 Jan 1997, Mike Patnode (mikep)
+ * suggests that this only needs to be done for HP-UX 9.
+ */
+#ifdef HPUX9
+#define PIDOOMA_STACK_SIZE 524288
+#define BACKTRACE_SIZE 8192
+ {
+ jmp_buf jb;
+ char *newstack;
+ char *oldstack;
+
+ if(!setjmp(jb)) {
+ newstack = (char *) PR_MALLOC(PIDOOMA_STACK_SIZE);
+ oldstack = (char *) (*(((int *) jb) + 1) - BACKTRACE_SIZE);
+ memcpy(newstack, oldstack, BACKTRACE_SIZE);
+ *(((int *) jb) + 1) = (int) (newstack + BACKTRACE_SIZE);
+ longjmp(jb, 1);
+ }
+ }
+#endif /* HPUX9 */
+#endif /* !_PR_PTHREADS */
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+#ifndef _PR_PTHREADS
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+#else
+ *np = 0;
+ return NULL;
+#endif
+}
+
+#ifndef _PR_PTHREADS
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for HP-UX */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for HP-UX.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for HP-UX.");
+}
+#endif /* _PR_PTHREADS */
+
+void
+_MD_suspend_thread(PRThread *thread)
+{
+#ifdef _PR_PTHREADS
+#endif
+}
+
+void
+_MD_resume_thread(PRThread *thread)
+{
+#ifdef _PR_PTHREADS
+#endif
+}
+#endif /* PTHREADS_USER */
+
+/*
+ * See if we have the privilege to set the scheduling policy and
+ * priority of threads. Returns 0 if privilege is available.
+ * Returns EPERM otherwise.
+ */
+
+#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
+PRIntn pt_hpux_privcheck()
+{
+ PRIntn policy;
+ struct sched_param schedule;
+ PRIntn rv;
+ pthread_t me = pthread_self();
+
+ rv = pthread_getschedparam(me, &policy, &schedule);
+ PR_ASSERT(0 == rv);
+ rv = pthread_setschedparam(me, policy, &schedule);
+ PR_ASSERT(0 == rv || EPERM == rv);
+ return rv;
+}
+#endif
+
+/*
+ * The HP version of strchr is buggy. It looks past the end of the
+ * string and causes a segmentation fault when our (NSPR) version
+ * of malloc is used.
+ *
+ * A better solution might be to put a cushion in our malloc just in
+ * case HP's version of strchr somehow gets used instead of this one.
+ */
+char *
+strchr(const char *s, int c)
+{
+ char ch;
+
+ if (!s) {
+ return NULL;
+ }
+
+ ch = (char) c;
+
+ while ((*s) && ((*s) != ch)) {
+ s++;
+ }
+
+ if ((*s) == ch) {
+ return (char *) s;
+ }
+
+ return NULL;
+}
+
+/*
+ * Implemementation of memcmp in HP-UX (verified on releases A.09.03,
+ * A.09.07, and B.10.10) dumps core if called with:
+ * 1. First operand with address = 1(mod 4).
+ * 2. Size = 1(mod 4)
+ * 3. Last byte of the second operand is the last byte of the page and
+ * next page is not accessible(not mapped or protected)
+ * Thus, using the following naive version (tons of optimizations are
+ * possible;^)
+ */
+
+int memcmp(const void *s1, const void *s2, size_t n)
+{
+ register unsigned char *p1 = (unsigned char *) s1,
+ *p2 = (unsigned char *) s2;
+
+ while (n-- > 0) {
+ register int r = ((int) ((unsigned int) *p1))
+ - ((int) ((unsigned int) *p2));
+ if (r) return r;
+ p1++; p2++;
+ }
+ return 0;
+}
diff --git a/pr/src/md/unix/irix.c b/pr/src/md/unix/irix.c
new file mode 100644
index 00000000..58ebe922
--- /dev/null
+++ b/pr/src/md/unix/irix.c
@@ -0,0 +1,1308 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+#include <signal.h>
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/syssgi.h>
+#include <sys/time.h>
+#include <sys/immu.h>
+#include <sys/utsname.h>
+#include <sys/sysmp.h>
+#include <sys/pda.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <sys/resource.h>
+#include <sys/procfs.h>
+#include <task.h>
+
+static void _MD_IntervalInit(void);
+
+#if defined(_PR_PTHREADS)
+#else /* defined(_PR_PTHREADS) */
+
+char *_nspr_sproc_private; /* ptr. to private region in every sproc */
+
+extern PRUintn _pr_numCPU;
+
+typedef struct nspr_arena {
+ PRCList links;
+ usptr_t *usarena;
+} nspr_arena;
+
+#define ARENA_PTR(qp) \
+ ((nspr_arena *) ((char*) (qp) - offsetof(nspr_arena , links)))
+
+static usptr_t *alloc_new_arena(void);
+
+PRCList arena_list = PR_INIT_STATIC_CLIST(&arena_list);
+ulock_t arena_list_lock;
+nspr_arena first_arena;
+int _nspr_irix_arena_cnt = 1;
+
+long _nspr_irix_lock_cnt = 0;
+long _nspr_irix_sem_cnt = 0;
+long _nspr_irix_pollsem_cnt = 0;
+
+usptr_t *_pr_usArena;
+ulock_t _pr_heapLock;
+
+usema_t *_pr_irix_exit_sem;
+PRInt32 _pr_irix_exit_now = 0;
+
+
+#define _NSPR_DEF_INITUSERS 100 /* default value of CONF_INITUSERS */
+#define _NSPR_DEF_INITSIZE (4 * 1024 * 1024) /* 4 MB */
+
+int _irix_initusers = _NSPR_DEF_INITUSERS;
+int _irix_initsize = _NSPR_DEF_INITSIZE;
+
+PRIntn _pr_io_in_progress, _pr_clock_in_progress;
+
+PRInt32 _pr_md_irix_sprocs_created, _pr_md_irix_sprocs_failed;
+PRInt32 _pr_md_irix_sprocs = 1;
+PRCList _pr_md_irix_sproc_list =
+PR_INIT_STATIC_CLIST(&_pr_md_irix_sproc_list);
+
+sigset_t ints_off;
+extern sigset_t timer_set;
+
+#if !defined(PR_SETABORTSIG)
+#define PR_SETABORTSIG 18
+#endif
+/*
+ * terminate the entire application if any sproc exits abnormally
+ */
+PRBool _nspr_terminate_on_error = PR_TRUE;
+
+/*
+ * exported interface to set the shared arena parameters
+ */
+void _PR_Irix_Set_Arena_Params(PRInt32 initusers, PRInt32 initsize)
+{
+ _irix_initusers = initusers;
+ _irix_initsize = initsize;
+}
+
+static usptr_t *alloc_new_arena()
+{
+ return(usinit("/dev/zero"));
+}
+
+static PRStatus new_poll_sem(struct _MDThread *mdthr, int val)
+{
+PRIntn _is;
+PRStatus rv = PR_SUCCESS;
+usema_t *sem = NULL;
+PRCList *qp;
+nspr_arena *arena;
+usptr_t *irix_arena;
+PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (me && !_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(_is);
+ _PR_LOCK(arena_list_lock);
+ for (qp = arena_list.next; qp != &arena_list; qp = qp->next) {
+ arena = ARENA_PTR(qp);
+ sem = usnewpollsema(arena->usarena, val);
+ if (sem != NULL) {
+ mdthr->cvar_pollsem = sem;
+ mdthr->pollsem_arena = arena->usarena;
+ break;
+ }
+ }
+ if (sem == NULL) {
+ /*
+ * If no space left in the arena allocate a new one.
+ */
+ if (errno == ENOMEM) {
+ arena = PR_NEWZAP(nspr_arena);
+ if (arena != NULL) {
+ irix_arena = alloc_new_arena();
+ if (irix_arena) {
+ PR_APPEND_LINK(&arena->links, &arena_list);
+ _nspr_irix_arena_cnt++;
+ arena->usarena = irix_arena;
+ sem = usnewpollsema(arena->usarena, val);
+ if (sem != NULL) {
+ mdthr->cvar_pollsem = sem;
+ mdthr->pollsem_arena = arena->usarena;
+ } else
+ rv = PR_FAILURE;
+ } else {
+ PR_DELETE(arena);
+ rv = PR_FAILURE;
+ }
+
+ } else
+ rv = PR_FAILURE;
+ } else
+ rv = PR_FAILURE;
+ }
+ _PR_UNLOCK(arena_list_lock);
+ if (me && !_PR_IS_NATIVE_THREAD(me))
+ _PR_FAST_INTSON(_is);
+ if (rv == PR_SUCCESS)
+ _MD_ATOMIC_INCREMENT(&_nspr_irix_pollsem_cnt);
+ return rv;
+}
+
+static void free_poll_sem(struct _MDThread *mdthr)
+{
+PRIntn _is;
+PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (me && !_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(_is);
+ usfreepollsema(mdthr->cvar_pollsem, mdthr->pollsem_arena);
+ if (me && !_PR_IS_NATIVE_THREAD(me))
+ _PR_FAST_INTSON(_is);
+ _MD_ATOMIC_DECREMENT(&_nspr_irix_pollsem_cnt);
+}
+
+static PRStatus new_lock(struct _MDLock *lockp)
+{
+PRIntn _is;
+PRStatus rv = PR_SUCCESS;
+ulock_t lock = NULL;
+PRCList *qp;
+nspr_arena *arena;
+usptr_t *irix_arena;
+PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (me && !_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(_is);
+ _PR_LOCK(arena_list_lock);
+ for (qp = arena_list.next; qp != &arena_list; qp = qp->next) {
+ arena = ARENA_PTR(qp);
+ lock = usnewlock(arena->usarena);
+ if (lock != NULL) {
+ lockp->lock = lock;
+ lockp->arena = arena->usarena;
+ break;
+ }
+ }
+ if (lock == NULL) {
+ /*
+ * If no space left in the arena allocate a new one.
+ */
+ if (errno == ENOMEM) {
+ arena = PR_NEWZAP(nspr_arena);
+ if (arena != NULL) {
+ irix_arena = alloc_new_arena();
+ if (irix_arena) {
+ PR_APPEND_LINK(&arena->links, &arena_list);
+ _nspr_irix_arena_cnt++;
+ arena->usarena = irix_arena;
+ lock = usnewlock(irix_arena);
+ if (lock != NULL) {
+ lockp->lock = lock;
+ lockp->arena = arena->usarena;
+ } else
+ rv = PR_FAILURE;
+ } else {
+ PR_DELETE(arena);
+ rv = PR_FAILURE;
+ }
+
+ } else
+ rv = PR_FAILURE;
+ } else
+ rv = PR_FAILURE;
+ }
+ _PR_UNLOCK(arena_list_lock);
+ if (me && !_PR_IS_NATIVE_THREAD(me))
+ _PR_FAST_INTSON(_is);
+ if (rv == PR_SUCCESS)
+ _MD_ATOMIC_INCREMENT(&_nspr_irix_lock_cnt);
+ return rv;
+}
+
+static void free_lock(struct _MDLock *lockp)
+{
+PRIntn _is;
+PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (me && !_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(_is);
+ usfreelock(lockp->lock, lockp->arena);
+ if (me && !_PR_IS_NATIVE_THREAD(me))
+ _PR_FAST_INTSON(_is);
+ _MD_ATOMIC_DECREMENT(&_nspr_irix_lock_cnt);
+}
+
+void _MD_FREE_LOCK(struct _MDLock *lockp)
+{
+ PRIntn _is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (me && !_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(_is);
+ free_lock(lockp);
+ if (me && !_PR_IS_NATIVE_THREAD(me))
+ _PR_FAST_INTSON(_is);
+}
+
+/*
+ * _MD_get_attached_thread
+ * Return the thread pointer of the current thread if it is attached.
+ *
+ * This function is needed for Irix because the thread-local-storage is
+ * implemented by mmapin'g a page with the MAP_LOCAL flag. This causes the
+ * sproc-private page to inherit contents of the page of the caller of sproc().
+ */
+PRThread *_MD_get_attached_thread(void)
+{
+ if (_MD_GET_SPROC_PID() == getpid())
+ return _PR_MD_CURRENT_THREAD();
+ else
+ return 0;
+}
+
+PRStatus _MD_NEW_LOCK(struct _MDLock *lockp)
+{
+ PRStatus rv;
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (me && !_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(is);
+ rv = new_lock(lockp);
+ if (me && !_PR_IS_NATIVE_THREAD(me))
+ _PR_FAST_INTSON(is);
+ return rv;
+}
+
+static void
+sigchld_handler(int sig)
+{
+ pid_t pid;
+ int status;
+
+ /*
+ * If an sproc exited abnormally send a SIGKILL signal to all the
+ * sprocs in the process to terminate the application
+ */
+ while ((pid = waitpid(0, &status, WNOHANG)) > 0) {
+ if (WIFSIGNALED(status) && ((WTERMSIG(status) == SIGSEGV) ||
+ (WTERMSIG(status) == SIGBUS) ||
+ (WTERMSIG(status) == SIGABRT) ||
+ (WTERMSIG(status) == SIGILL)))
+
+ prctl(PR_SETEXITSIG, SIGKILL);
+ exit(status);
+ }
+}
+
+static void save_context_and_block(int sig)
+{
+PRThread *me = _PR_MD_CURRENT_THREAD();
+_PRCPU *cpu = _PR_MD_CURRENT_CPU();
+
+ /*
+ * save context
+ */
+ (void) setjmp(me->md.jb);
+ /*
+ * unblock the suspending thread
+ */
+ if (me->cpu) {
+ /*
+ * I am a cpu thread, not a user-created GLOBAL thread
+ */
+ unblockproc(cpu->md.suspending_id);
+ } else {
+ unblockproc(me->md.suspending_id);
+ }
+ /*
+ * now, block current thread
+ */
+ blockproc(getpid());
+}
+
+/*
+** The irix kernel has a bug in it which causes async connect's which are
+** interrupted by a signal to fail terribly (EADDRINUSE is returned).
+** We work around the bug by blocking signals during the async connect
+** attempt.
+*/
+PRInt32 _MD_irix_connect(
+ PRInt32 osfd, const PRNetAddr *addr, PRInt32 addrlen, PRIntervalTime timeout)
+{
+ PRInt32 rv;
+ sigset_t oldset;
+
+ sigprocmask(SIG_BLOCK, &ints_off, &oldset);
+ rv = connect(osfd, addr, addrlen);
+ sigprocmask(SIG_SETMASK, &oldset, 0);
+
+ return(rv);
+}
+
+#include "prprf.h"
+
+/********************************************************************/
+/********************************************************************/
+/*************** Various thread like things for IRIX ****************/
+/********************************************************************/
+/********************************************************************/
+
+void *_MD_GetSP(PRThread *t)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ void *sp;
+
+ if (me == t)
+ (void) setjmp(t->md.jb);
+
+ sp = (void *)(t->md.jb[JB_SP]);
+ PR_ASSERT((sp >= (void *) t->stack->stackBottom) &&
+ (sp <= (void *) (t->stack->stackBottom + t->stack->stackSize)));
+ return(sp);
+}
+
+void _MD_InitLocks()
+{
+ char buf[200];
+ char *init_users, *init_size;
+
+ PR_snprintf(buf, sizeof(buf), "/dev/zero");
+
+ if (init_users = getenv("_NSPR_IRIX_INITUSERS"))
+ _irix_initusers = atoi(init_users);
+
+ if (init_size = getenv("_NSPR_IRIX_INITSIZE"))
+ _irix_initsize = atoi(init_size);
+
+ usconfig(CONF_INITUSERS, _irix_initusers);
+ usconfig(CONF_INITSIZE, _irix_initsize);
+ usconfig(CONF_AUTOGROW, 1);
+ usconfig(CONF_AUTORESV, 1);
+ if (usconfig(CONF_ARENATYPE, US_SHAREDONLY) < 0) {
+ perror("PR_Init: unable to config mutex arena");
+ exit(-1);
+ }
+
+ _pr_usArena = usinit(buf);
+ if (!_pr_usArena) {
+ fprintf(stderr,
+ "PR_Init: Error - unable to create lock/monitor arena\n");
+ exit(-1);
+ }
+ _pr_heapLock = usnewlock(_pr_usArena);
+
+ arena_list_lock = usnewlock(_pr_usArena);
+ _nspr_irix_lock_cnt = 3;
+
+ _pr_irix_exit_sem = usnewsema(_pr_usArena, 0);
+ _nspr_irix_sem_cnt = 1;
+
+ first_arena.usarena = _pr_usArena;
+ PR_INIT_CLIST(&first_arena.links);
+ PR_APPEND_LINK(&first_arena.links, &arena_list);
+}
+
+/* _PR_IRIX_CHILD_PROCESS is a private API for Server group */
+void _PR_IRIX_CHILD_PROCESS()
+{
+ PR_ASSERT(_PR_MD_CURRENT_CPU() == _pr_primordialCPU);
+ PR_ASSERT(_pr_numCPU == 1);
+ PR_ASSERT((_pr_userActive + _pr_systemActive) == 2);
+ /*
+ * save the new pid
+ */
+ _pr_primordialCPU->md.id = getpid();
+}
+
+static PRStatus pr_cvar_wait_sem(PRThread *thread, PRIntervalTime timeout)
+{
+ struct timeval tv, *tvp;
+ fd_set rd;
+ int rv;
+
+ if(timeout == PR_INTERVAL_NO_TIMEOUT) tvp = NULL;
+ else {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&rd);
+ FD_SET(thread->md.cvar_pollsemfd, &rd);
+ /*
+ * call uspsema only if a previous select call on this semaphore
+ * did not timeout
+ */
+ if (!thread->md.cvar_pollsem_select) {
+ rv = _PR_WAIT_SEM(thread->md.cvar_pollsem);
+ PR_ASSERT(rv >= 0);
+ } else
+ rv = 0;
+again:
+ if(!rv) {
+ rv = _MD_SELECT(thread->md.cvar_pollsemfd + 1, &rd,
+ NULL,NULL,tvp);
+ if ((rv == -1) && (errno == EINTR)) {
+ rv = 0;
+ goto again;
+ }
+ PR_ASSERT(rv >= 0);
+ }
+
+ if (rv > 0) {
+ /*
+ * acquired the semaphore, call uspsema next time
+ */
+ thread->md.cvar_pollsem_select = 0;
+ return PR_SUCCESS;
+ } else {
+ /*
+ * select timed out; must call select, not uspsema, when trying
+ * to acquire the semaphore the next time
+ */
+ thread->md.cvar_pollsem_select = 1;
+ return PR_FAILURE;
+ }
+}
+
+PRStatus _MD_wait(PRThread *thread, PRIntervalTime ticks)
+{
+ if ( thread->flags & _PR_GLOBAL_SCOPE ) {
+ _MD_CHECK_FOR_EXIT();
+ if (pr_cvar_wait_sem(thread, ticks) == PR_FAILURE) {
+ _MD_CHECK_FOR_EXIT();
+ /*
+ * wait timed out
+ */
+ _PR_THREAD_LOCK(thread);
+ if (thread->wait.cvar) {
+ /*
+ * The thread will remove itself from the waitQ
+ * of the cvar in _PR_WaitCondVar
+ */
+ thread->wait.cvar = NULL;
+ thread->state = _PR_RUNNING;
+ _PR_THREAD_UNLOCK(thread);
+ } else {
+ _PR_THREAD_UNLOCK(thread);
+ /*
+ * This thread was woken up by a notifying thread
+ * at the same time as a timeout; so, consume the
+ * extra post operation on the semaphore
+ */
+ _MD_CHECK_FOR_EXIT();
+ pr_cvar_wait_sem(thread, PR_INTERVAL_NO_TIMEOUT);
+ }
+ _MD_CHECK_FOR_EXIT();
+ }
+ } else {
+ _PR_MD_SWITCH_CONTEXT(thread);
+ }
+ return PR_SUCCESS;
+}
+
+PRStatus _MD_WakeupWaiter(PRThread *thread)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 pid, rv;
+ PRIntn is;
+
+ PR_ASSERT(_pr_md_idle_cpus >= 0);
+ if (thread == NULL) {
+ if (_pr_md_idle_cpus)
+ _MD_Wakeup_CPUs();
+ } else if (!_PR_IS_NATIVE_THREAD(thread)) {
+ if (_pr_md_idle_cpus)
+ _MD_Wakeup_CPUs();
+ } else {
+ PR_ASSERT(_PR_IS_NATIVE_THREAD(thread));
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(is);
+ _MD_CVAR_POST_SEM(thread);
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_FAST_INTSON(is);
+ }
+ return PR_SUCCESS;
+}
+
+PRStatus _MD_CreateThread(PRThread *thread,
+void (*start)(void *),
+PRThreadPriority priority,
+PRThreadScope scope,
+PRThreadState state,
+PRUint32 stackSize)
+{
+ typedef void (*SprocEntry) (void *, size_t);
+ SprocEntry spentry = (SprocEntry)start;
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 pid;
+ PRStatus rv;
+
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(is);
+ thread->md.cvar_pollsem_select = 0;
+ thread->flags |= _PR_GLOBAL_SCOPE;
+ pid = sprocsp(
+ spentry, /* startup func */
+ PR_SALL, /* attribute flags */
+ (void *)thread, /* thread param */
+ NULL, /* stack address */
+ stackSize); /* stack size */
+ if (pid > 0) {
+ /*
+ * Wait for the sproc to signal me after it has initialized
+ * itself
+ */
+ if (!_PR_IS_NATIVE_THREAD(me))
+ blockproc(me->cpu->md.id);
+ else
+ blockproc(me->md.id);
+ if (thread->md.cvar_pollsemfd < 0) {
+ /*
+ * the sproc failed to create a polled semaphore and exited
+ */
+ _MD_ATOMIC_INCREMENT(&_pr_md_irix_sprocs_failed);
+ rv = PR_FAILURE;
+ } else {
+ _MD_ATOMIC_INCREMENT(&_pr_md_irix_sprocs_created);
+ _MD_ATOMIC_INCREMENT(&_pr_md_irix_sprocs);
+ rv = PR_SUCCESS;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_FAST_INTSON(is);
+ return rv;
+ } else {
+ _MD_ATOMIC_INCREMENT(&_pr_md_irix_sprocs_failed);
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_FAST_INTSON(is);
+ return PR_FAILURE;
+ }
+}
+
+void _MD_CleanThread(PRThread *thread)
+{
+ if (thread->flags & _PR_GLOBAL_SCOPE) {
+ close(thread->md.cvar_pollsemfd);
+ thread->md.cvar_pollsemfd = -1;
+ free_poll_sem(&thread->md);
+ thread->md.cvar_pollsem = NULL;
+ }
+}
+
+void _MD_SetPriority(_MDThread *thread, PRThreadPriority newPri)
+{
+ return;
+}
+
+extern void _MD_unix_terminate_waitpid_daemon(void);
+
+void
+_MD_CleanupBeforeExit(void)
+{
+ extern PRInt32 _pr_cpus_exit;
+
+ _MD_unix_terminate_waitpid_daemon();
+
+ _pr_irix_exit_now = 1;
+ if (_pr_numCPU > 1) {
+ /*
+ * Set a global flag, and wakeup all cpus which will notice the flag
+ * and exit.
+ */
+ _pr_cpus_exit = getpid();
+ _MD_Wakeup_CPUs();
+ while(_pr_numCPU > 1) {
+ _PR_WAIT_SEM(_pr_irix_exit_sem);
+ _pr_numCPU--;
+ }
+ }
+ /*
+ * cause global threads on the recycle list to exit
+ */
+ _PR_DEADQ_LOCK;
+ if (_PR_NUM_DEADNATIVE != 0) {
+ PRThread *thread;
+ PRCList *ptr;
+
+ ptr = _PR_DEADNATIVEQ.next;
+ while( ptr != &_PR_DEADNATIVEQ ) {
+ thread = _PR_THREAD_PTR(ptr);
+ _MD_CVAR_POST_SEM(thread);
+ ptr = ptr->next;
+ }
+ }
+ _PR_DEADQ_UNLOCK;
+ while(_PR_NUM_DEADNATIVE > 1) {
+ _PR_WAIT_SEM(_pr_irix_exit_sem);
+ _PR_DEC_DEADNATIVE;
+ }
+}
+
+#ifndef IRIX5_3
+extern void __sgi_prda_procmask(int);
+#endif
+
+PRStatus
+_MD_InitThread(PRThread *thread, PRBool wakeup_parent)
+{
+ struct sigaction sigact;
+ PRStatus rv = PR_SUCCESS;
+
+ if (thread->flags & _PR_GLOBAL_SCOPE) {
+ /*
+ * create a polled semaphore
+ *
+ * NOTE: On Irix there is a bug which requires the sproc that
+ * created a polled semaphore to not exit for that semaphore
+ * to be useable by other sprocs.
+ */
+ thread->md.id = getpid();
+ thread->md.cvar_pollsemfd = -1;
+ if (new_poll_sem(&thread->md,0) == PR_FAILURE) {
+ if (wakeup_parent == PR_TRUE)
+ unblockproc(getppid());
+ rv = PR_FAILURE;
+ }
+ thread->md.cvar_pollsemfd =
+ _PR_OPEN_POLL_SEM(thread->md.cvar_pollsem);
+ if ((thread->md.cvar_pollsemfd < 0)) {
+ free_poll_sem(&thread->md);
+ if (wakeup_parent == PR_TRUE)
+ unblockproc(getppid());
+ rv = PR_FAILURE;
+ }
+ setblockproccnt(thread->md.id, 0);
+ _MD_SET_SPROC_PID(getpid());
+#ifndef IRIX5_3
+ /*
+ * enable user-level processing of sigprocmask(); this is an
+ * undocumented feature available in Irix 6.2, 6.3, 6.4 and 6.5
+ */
+ __sgi_prda_procmask(USER_LEVEL);
+#endif
+ /*
+ * set up SIGUSR1 handler; this is used to save state
+ * during PR_SuspendAll
+ */
+ sigact.sa_handler = save_context_and_block;
+ sigact.sa_flags = SA_RESTART;
+ /*
+ * Must mask clock interrupts
+ */
+ sigact.sa_mask = timer_set;
+ sigaction(SIGUSR1, &sigact, 0);
+
+
+ if (_nspr_terminate_on_error) {
+ /*
+ * PR_SETABORTSIG is a new command implemented in a patch to
+ * Irix 6.2, 6.3 and 6.4. This causes a signal to be sent to all
+ * sprocs in the process when one of them terminates abnormally
+ *
+ */
+
+ if (prctl(PR_SETABORTSIG, SIGKILL) < 0) {
+ /*
+ * if (errno == EINVAL)
+ *
+ * PR_SETABORTSIG not supported under this OS.
+ * You may want to get a recent kernel rollup patch that
+ * supports this feature.
+ */
+ /*
+ * SIGCLD handler for detecting abormally-terminating
+ * sprocs
+ */
+ sigact.sa_handler = sigchld_handler;
+ sigact.sa_flags = SA_RESTART;
+ sigact.sa_mask = ints_off;
+ sigaction(SIGCLD, &sigact, NULL);
+ }
+ }
+ /*
+ * unblock the parent sproc
+ */
+ if (wakeup_parent == PR_TRUE)
+ unblockproc(getppid());
+ }
+ return rv;
+}
+
+PR_EXTERN(void ) _MD_exit(PRIntn status)
+{
+ /*
+ * Cause SIGKILL to be sent to other sprocs, if any, in the application
+ */
+ prctl(PR_SETEXITSIG, SIGKILL);
+ exit(status);
+}
+
+void
+_MD_InitRunningCPU(_PRCPU *cpu)
+{
+ extern int _pr_md_pipefd[2];
+
+ _MD_unix_init_running_cpu(cpu);
+ cpu->md.id = getpid();
+ _MD_SET_SPROC_PID(getpid());
+ if (_pr_md_pipefd[0] >= 0) {
+ _PR_IOQ_MAX_OSFD(cpu) = _pr_md_pipefd[0];
+ FD_SET(_pr_md_pipefd[0], &_PR_FD_READ_SET(cpu));
+ }
+}
+
+void
+_MD_ExitThread(PRThread *thread)
+{
+ if (thread->flags & _PR_GLOBAL_SCOPE) {
+ _MD_ATOMIC_DECREMENT(&_pr_md_irix_sprocs);
+ _MD_CLEAN_THREAD(thread);
+ _MD_SET_CURRENT_THREAD(NULL);
+ }
+}
+
+void
+_MD_SuspendCPU(_PRCPU *cpu)
+{
+ PRInt32 rv;
+
+ cpu->md.suspending_id = getpid();
+ rv = kill(cpu->md.id, SIGUSR1);
+ PR_ASSERT(rv == 0);
+ /*
+ * now, block the current thread/cpu until woken up by the suspended
+ * thread from it's SIGUSR1 signal handler
+ */
+ blockproc(getpid());
+
+}
+
+void
+_MD_ResumeCPU(_PRCPU *cpu)
+{
+ unblockproc(cpu->md.id);
+}
+
+#if 0
+/*
+ * save the register context of a suspended sproc
+ */
+void get_context(PRThread *thr)
+{
+ int len, fd;
+ char pidstr[24];
+ char path[24];
+
+ /*
+ * open the file corresponding to this process in procfs
+ */
+ sprintf(path,"/proc/%s","00000");
+ len = strlen(path);
+ sprintf(pidstr,"%d",thr->md.id);
+ len -= strlen(pidstr);
+ sprintf(path + len,"%s",pidstr);
+ fd = open(path,O_RDONLY);
+ if (fd >= 0) {
+ (void) ioctl(fd, PIOCGREG, thr->md.gregs);
+ close(fd);
+ }
+ return;
+}
+#endif /* 0 */
+
+void
+_MD_SuspendThread(PRThread *thread)
+{
+ PRInt32 rv;
+
+ PR_ASSERT((thread->flags & _PR_GLOBAL_SCOPE) &&
+ (thread->flags & _PR_GCABLE_THREAD));
+
+ thread->md.suspending_id = getpid();
+ rv = kill(thread->md.id, SIGUSR1);
+ PR_ASSERT(rv == 0);
+ /*
+ * now, block the current thread/cpu until woken up by the suspended
+ * thread from it's SIGUSR1 signal handler
+ */
+ blockproc(getpid());
+}
+
+void
+_MD_ResumeThread(PRThread *thread)
+{
+ PRInt32 rv;
+
+ PR_ASSERT((thread->flags & _PR_GLOBAL_SCOPE) &&
+ (thread->flags & _PR_GCABLE_THREAD));
+ rv = unblockproc(thread->md.id);
+}
+
+/*
+ * return the set of processors available for scheduling procs in the
+ * "mask" argument
+ */
+PRInt32 _MD_GetThreadAffinityMask(PRThread *unused, PRUint32 *mask)
+{
+ PRInt32 nprocs, rv;
+ struct pda_stat *pstat;
+#define MAX_PROCESSORS 32
+
+ nprocs = sysmp(MP_NPROCS);
+ if (nprocs < 0)
+ return(-1);
+ pstat = PR_MALLOC(sizeof(struct pda_stat) * nprocs);
+ if (pstat == NULL)
+ return(-1);
+ rv = sysmp(MP_STAT, pstat);
+ if (rv < 0) {
+ PR_DELETE(pstat);
+ return(-1);
+ }
+ /*
+ * look at the first 32 cpus
+ */
+ nprocs = (nprocs > MAX_PROCESSORS) ? MAX_PROCESSORS : nprocs;
+ *mask = 0;
+ while (nprocs) {
+ if ((pstat->p_flags & PDAF_ENABLED) &&
+ !(pstat->p_flags & PDAF_ISOLATED)) {
+ *mask |= (1 << pstat->p_cpuid);
+ }
+ nprocs--;
+ pstat++;
+ }
+ return 0;
+}
+
+static char *_thr_state[] = {
+ "UNBORN",
+ "RUNNABLE",
+ "RUNNING",
+ "LOCK_WAIT",
+ "COND_WAIT",
+ "JOIN_WAIT",
+ "IO_WAIT",
+ "SUSPENDED",
+ "DEAD"
+};
+
+_PR_List_Threads()
+{
+ PRThread *thr;
+ void *handle;
+ struct _PRCPU *cpu;
+ PRCList *qp;
+ int len, status, rv, fd;
+ char pidstr[24];
+ char path[24];
+ prpsinfo_t pinfo;
+
+
+ printf("\n%s %-s\n"," ","LOCAL Threads");
+ printf("%s %-s\n"," ","----- -------");
+ printf("%s %-14s %-10s %-12s %-3s %-10s %-10s %-12s\n\n"," ",
+ "Thread", "State", "Wait-Handle",
+ "Cpu","Stk-Base","Stk-Sz","SP");
+ for (qp = _PR_ACTIVE_LOCAL_THREADQ().next;
+ qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp->next) {
+ thr = _PR_ACTIVE_THREAD_PTR(qp);
+ printf("%s 0x%-12x %-10s "," ",thr,_thr_state[thr->state]);
+ if (thr->state == _PR_LOCK_WAIT)
+ handle = thr->wait.lock;
+ else if (thr->state == _PR_COND_WAIT)
+ handle = thr->wait.cvar;
+ else
+ handle = NULL;
+ if (handle)
+ printf("0x%-10x ",handle);
+ else
+ printf("%-12s "," ");
+ printf("%-3d ",thr->cpu->id);
+ printf("0x%-8x ",thr->stack->stackBottom);
+ printf("0x%-8x ",thr->stack->stackSize);
+ printf("0x%-10x\n",thr->md.jb[JB_SP]);
+ }
+
+ printf("\n%s %-s\n"," ","GLOBAL Threads");
+ printf("%s %-s\n"," ","------ -------");
+ printf("%s %-14s %-6s %-12s %-12s %-12s %-12s\n\n"," ","Thread",
+ "Pid","State","Wait-Handle",
+ "Stk-Base","Stk-Sz");
+
+ for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next;
+ qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp->next) {
+ thr = _PR_ACTIVE_THREAD_PTR(qp);
+ if (thr->cpu != NULL)
+ continue; /* it is a cpu thread */
+ printf("%s 0x%-12x %-6d "," ",thr,thr->md.id);
+ /*
+ * check if the sproc is still running
+ * first call prctl(PR_GETSHMASK,pid) to check if
+ * the process is part of the share group (the pid
+ * could have been recycled by the OS)
+ */
+ if (prctl(PR_GETSHMASK,thr->md.id) < 0) {
+ printf("%-12s\n","TERMINATED");
+ continue;
+ }
+ /*
+ * Now, check if the sproc terminated and is in zombie
+ * state
+ */
+ sprintf(path,"/proc/pinfo/%s","00000");
+ len = strlen(path);
+ sprintf(pidstr,"%d",thr->md.id);
+ len -= strlen(pidstr);
+ sprintf(path + len,"%s",pidstr);
+ fd = open(path,O_RDONLY);
+ if (fd >= 0) {
+ if (ioctl(fd, PIOCPSINFO, &pinfo) < 0)
+ printf("%-12s ","TERMINATED");
+ else if (pinfo.pr_zomb)
+ printf("%-12s ","TERMINATED");
+ else
+ printf("%-12s ",_thr_state[thr->state]);
+ close(fd);
+ } else {
+ printf("%-12s ","TERMINATED");
+ }
+
+ if (thr->state == _PR_LOCK_WAIT)
+ handle = thr->wait.lock;
+ else if (thr->state == _PR_COND_WAIT)
+ handle = thr->wait.cvar;
+ else
+ handle = NULL;
+ if (handle)
+ printf("%-12x ",handle);
+ else
+ printf("%-12s "," ");
+ printf("0x%-10x ",thr->stack->stackBottom);
+ printf("0x%-10x\n",thr->stack->stackSize);
+ }
+
+ printf("\n%s %-s\n"," ","CPUs");
+ printf("%s %-s\n"," ","----");
+ printf("%s %-14s %-6s %-12s \n\n"," ","Id","Pid","State");
+
+
+ for (qp = _PR_CPUQ().next; qp != &_PR_CPUQ(); qp = qp->next) {
+ cpu = _PR_CPU_PTR(qp);
+ printf("%s %-14d %-6d "," ",cpu->id,cpu->md.id);
+ /*
+ * check if the sproc is still running
+ * first call prctl(PR_GETSHMASK,pid) to check if
+ * the process is part of the share group (the pid
+ * could have been recycled by the OS)
+ */
+ if (prctl(PR_GETSHMASK,cpu->md.id) < 0) {
+ printf("%-12s\n","TERMINATED");
+ continue;
+ }
+ /*
+ * Now, check if the sproc terminated and is in zombie
+ * state
+ */
+ sprintf(path,"/proc/pinfo/%s","00000");
+ len = strlen(path);
+ sprintf(pidstr,"%d",cpu->md.id);
+ len -= strlen(pidstr);
+ sprintf(path + len,"%s",pidstr);
+ fd = open(path,O_RDONLY);
+ if (fd >= 0) {
+ if (ioctl(fd, PIOCPSINFO, &pinfo) < 0)
+ printf("%-12s\n","TERMINATED");
+ else if (pinfo.pr_zomb)
+ printf("%-12s\n","TERMINATED");
+ else
+ printf("%-12s\n","RUNNING");
+ close(fd);
+ } else {
+ printf("%-12s\n","TERMINATED");
+ }
+
+ }
+ fflush(stdout);
+}
+#endif /* defined(_PR_PTHREADS) */
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+#if !defined(_PR_PTHREADS)
+ if (isCurrent) {
+ (void) setjmp(t->md.jb);
+ }
+ *np = sizeof(t->md.jb) / sizeof(PRWord);
+ return (PRWord *) (t->md.jb);
+#else
+ *np = 0;
+ return NULL;
+#endif
+}
+
+void _MD_EarlyInit(void)
+{
+#if !defined(_PR_PTHREADS)
+ char *eval;
+ int fd;
+
+ sigemptyset(&ints_off);
+ sigaddset(&ints_off, SIGALRM);
+ sigaddset(&ints_off, SIGIO);
+ sigaddset(&ints_off, SIGCLD);
+
+ if (eval = getenv("_NSPR_TERMINATE_ON_ERROR"))
+ _nspr_terminate_on_error = (0 == atoi(eval) == 0) ? PR_FALSE : PR_TRUE;
+
+ fd = open("/dev/zero",O_RDWR , 0);
+ if (fd < 0) {
+ perror("open /dev/zero failed");
+ exit(1);
+ }
+ /*
+ * Set up the sproc private data area.
+ * This region exists at the same address, _nspr_sproc_private, for
+ * every sproc, but each sproc gets a private copy of the region.
+ */
+ _nspr_sproc_private = mmap(0, _pr_pageSize, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE| MAP_LOCAL, fd, 0);
+ if (_nspr_sproc_private == (void*)-1) {
+ perror("mmap /dev/zero failed");
+ exit(1);
+ }
+ close(fd);
+#endif
+ _MD_IntervalInit();
+} /* _MD_EarlyInit */
+
+void _MD_IrixInit()
+{
+#if !defined(_PR_PTHREADS)
+ struct sigaction sigact;
+ rlim_t stack_max_limit;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+#ifndef IRIX5_3
+ /*
+ * enable user-level processing of sigprocmask(); this is an undocumented
+ * feature available in Irix 6.2, 6.3, 6.4 and 6.5
+ */
+ __sgi_prda_procmask(USER_LEVEL);
+#endif
+
+ /*
+ * set up SIGUSR1 handler; this is used to save state
+ * during PR_SuspendAll
+ */
+ sigact.sa_handler = save_context_and_block;
+ sigact.sa_flags = SA_RESTART;
+ sigact.sa_mask = ints_off;
+ sigaction(SIGUSR1, &sigact, 0);
+
+ /*
+ * Change the name of the core file from core to core.pid,
+ * This is inherited by the sprocs created by this process
+ */
+#ifndef IRIX5_3
+ prctl(PR_COREPID, 0, 1);
+#endif
+ /*
+ * Irix-specific terminate on error processing
+ */
+ if (_nspr_terminate_on_error) {
+ /*
+ * PR_SETABORTSIG is a new command implemented in a patch to
+ * Irix 6.2, 6.3 and 6.4. This causes a signal to be sent to all
+ * sprocs in the process when one of them terminates abnormally
+ *
+ */
+ if (prctl(PR_SETABORTSIG, SIGKILL) < 0) {
+ /*
+ * if (errno == EINVAL)
+ *
+ * PR_SETABORTSIG not supported under this OS.
+ * You may want to get a recent kernel rollup patch that
+ * supports this feature.
+ *
+ */
+ /*
+ * PR_SETEXITSIG - send the SIGCLD signal to the parent
+ * sproc when any sproc terminates
+ *
+ * This is used to cause the entire application to
+ * terminate when any sproc terminates abnormally by
+ * receipt of a SIGSEGV, SIGBUS or SIGABRT signal.
+ * If this is not done, the application may seem
+ * "hung" to the user because the other sprocs may be
+ * waiting for resources held by the
+ * abnormally-terminating sproc.
+ */
+ prctl(PR_SETEXITSIG, 0);
+
+ sigact.sa_handler = sigchld_handler;
+ sigact.sa_flags = SA_RESTART;
+ sigact.sa_mask = ints_off;
+ sigaction(SIGCLD, &sigact, NULL);
+ }
+ }
+
+ /*
+ * setup stack fields for the primordial thread
+ */
+ me->stack->stackSize = prctl(PR_GETSTACKSIZE);
+ me->stack->stackBottom = me->stack->stackTop - me->stack->stackSize;
+#endif /* _PR_PTHREADS */
+
+ _PR_UnixInit();
+}
+
+/**************************************************************************/
+/************** code and such for NSPR 2.0's interval times ***************/
+/**************************************************************************/
+
+#define PR_CLOCK_GRANULARITY 10000UL
+
+#ifndef SGI_CYCLECNTR_SIZE
+#define SGI_CYCLECNTR_SIZE 165 /* Size user needs to use to read CC */
+#endif
+
+static PRUintn mmem_fd = -1;
+static PRIntn clock_width = 0;
+static void *iotimer_addr = NULL;
+static PRUint32 pr_clock_mask = 0;
+static PRUint32 pr_clock_shift = 0;
+static PRIntervalTime pr_ticks = 0;
+static PRUint32 pr_clock_granularity = 1;
+static PRUint32 pr_previous = 0, pr_residual = 0;
+
+extern PRIntervalTime _PR_UNIX_GetInterval(void);
+extern PRIntervalTime _PR_UNIX_TicksPerSecond(void);
+
+void _MD_IntervalInit(void)
+{
+ /*
+ * As much as I would like, the service available through this
+ * interface on R3000's (aka, IP12) just isn't going to make it.
+ * The register is only 24 bits wide, and rolls over at a verocious
+ * rate.
+ */
+ PRUint32 one_tick = 0;
+ struct utsname utsinfo;
+ uname(&utsinfo);
+ if ((strncmp("IP12", utsinfo.machine, 4) != 0)
+ && ((mmem_fd = open("/dev/mmem", O_RDONLY)) != -1))
+ {
+ int poffmask = getpagesize() - 1;
+ __psunsigned_t phys_addr, raddr, cycleval;
+
+ phys_addr = syssgi(SGI_QUERY_CYCLECNTR, &cycleval);
+ raddr = phys_addr & ~poffmask;
+ iotimer_addr = mmap(
+ 0, poffmask, PROT_READ, MAP_PRIVATE, mmem_fd, (__psint_t)raddr);
+
+ clock_width = syssgi(SGI_CYCLECNTR_SIZE);
+ if (clock_width < 0)
+ {
+ /*
+ * We must be executing on a 6.0 or earlier system, since the
+ * SGI_CYCLECNTR_SIZE call is not supported.
+ *
+ * The only pre-6.1 platforms with 64-bit counters are
+ * IP19 and IP21 (Challenge, PowerChallenge, Onyx).
+ */
+ if (!strncmp(utsinfo.machine, "IP19", 4) ||
+ !strncmp(utsinfo.machine, "IP21", 4))
+ clock_width = 64;
+ else
+ clock_width = 32;
+ }
+
+ /*
+ * 'cycleval' is picoseconds / increment of the counter.
+ * I'm pushing for a tick to be 100 microseconds, 10^(-4).
+ * That leaves 10^(-8) left over, or 10^8 / cycleval.
+ * Did I do that right?
+ */
+
+ one_tick = 100000000UL / cycleval ; /* 100 microseconds */
+
+ while (0 != one_tick)
+ {
+ pr_clock_shift += 1;
+ one_tick = one_tick >> 1;
+ pr_clock_granularity = pr_clock_granularity << 1;
+ }
+ pr_clock_mask = pr_clock_granularity - 1; /* to make a mask out of it */
+
+ iotimer_addr = (void*)
+ ((__psunsigned_t)iotimer_addr + (phys_addr & poffmask));
+ }
+
+} /* _PR_MD_INTERVAL_INIT */
+
+PRIntervalTime _MD_IntervalPerSec()
+{
+ return pr_clock_granularity;
+}
+
+PRIntervalTime _MD_GetInterval()
+{
+ if (mmem_fd != -1)
+ {
+ if (64 == clock_width)
+ {
+ PRUint64 temp = *(PRUint64*)iotimer_addr;
+ pr_ticks = (PRIntervalTime)(temp >> pr_clock_shift);
+ }
+ else
+ {
+ PRIntervalTime ticks = pr_ticks;
+ PRUint32 now = *(PRUint32*)iotimer_addr, temp;
+ PRUint32 residual = pr_residual, previous = pr_previous;
+
+ temp = now - previous + residual;
+ residual = temp & pr_clock_mask;
+ ticks += temp >> pr_clock_shift;
+
+ pr_previous = now;
+ pr_residual = residual;
+ pr_ticks = ticks;
+ }
+ }
+ else
+ {
+ /*
+ * No fast access. Use the time of day clock. This isn't the
+ * right answer since this clock can get set back, tick at odd
+ * rates, and it's expensive to acqurie.
+ */
+ pr_ticks = _PR_UNIX_GetInterval();
+ PR_ASSERT(PR_CLOCK_GRANULARITY > _PR_UNIX_TicksPerSecond());
+ pr_ticks *= (PR_CLOCK_GRANULARITY / _PR_UNIX_TicksPerSecond());
+ }
+ return pr_ticks;
+} /* _MD_GetInterval */
+
diff --git a/pr/src/md/unix/linux.c b/pr/src/md/unix/linux.c
new file mode 100644
index 00000000..a653f47b
--- /dev/null
+++ b/pr/src/md/unix/linux.c
@@ -0,0 +1,230 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+void _MD_EarlyInit(void)
+{
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+#ifndef _PR_PTHREADS
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+#else
+ *np = 0;
+ return NULL;
+#endif
+}
+
+#ifdef _PR_PTHREADS
+
+extern void _MD_unix_terminate_waitpid_daemon(void);
+
+void _MD_CleanupBeforeExit(void)
+{
+ _MD_unix_terminate_waitpid_daemon();
+}
+
+#else /* ! _PR_PTHREADS */
+
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for OSF1 */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for OSF1.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for OSF1.");
+ return PR_FAILURE;
+}
+#endif /* ! _PR_PTHREADS */
+
+#if defined(_PR_NEED_FAKE_POLL)
+
+#include <fcntl.h>
+
+int poll(struct pollfd *filedes, unsigned long nfds, int timeout)
+{
+ int i;
+ int rv;
+ int maxfd;
+ fd_set rd, wr, ex;
+ struct timeval tv, *tvp;
+
+ if (timeout < 0 && timeout != -1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (timeout == -1) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+ tvp = &tv;
+ }
+
+ maxfd = -1;
+ FD_ZERO(&rd);
+ FD_ZERO(&wr);
+ FD_ZERO(&ex);
+
+ for (i = 0; i < nfds; i++) {
+ int osfd = filedes[i].fd;
+ int events = filedes[i].events;
+ PRBool fdHasEvent = PR_FALSE;
+
+ if (osfd < 0) {
+ continue; /* Skip this osfd. */
+ }
+
+ /*
+ * Map the native poll flags to nspr poll flags.
+ * POLLIN, POLLRDNORM ===> PR_POLL_READ
+ * POLLOUT, POLLWRNORM ===> PR_POLL_WRITE
+ * POLLPRI, POLLRDBAND ===> PR_POLL_EXCEPTION
+ * POLLNORM, POLLWRBAND (and POLLMSG on some platforms)
+ * are ignored.
+ *
+ * The output events POLLERR and POLLHUP are never turned on.
+ * POLLNVAL may be turned on.
+ */
+
+ if (events & (POLLIN | POLLRDNORM)) {
+ FD_SET(osfd, &rd);
+ fdHasEvent = PR_TRUE;
+ }
+ if (events & (POLLOUT | POLLWRNORM)) {
+ FD_SET(osfd, &wr);
+ fdHasEvent = PR_TRUE;
+ }
+ if (events & (POLLPRI | POLLRDBAND)) {
+ FD_SET(osfd, &ex);
+ fdHasEvent = PR_TRUE;
+ }
+ if (fdHasEvent && osfd > maxfd) {
+ maxfd = osfd;
+ }
+ }
+
+ rv = select(maxfd + 1, &rd, &wr, &ex, tvp);
+
+ /* Compute poll results */
+ if (rv > 0) {
+ rv = 0;
+ for (i = 0; i < nfds; i++) {
+ PRBool fdHasEvent = PR_FALSE;
+
+ filedes[i].revents = 0;
+ if (filedes[i].fd < 0) {
+ continue;
+ }
+ if (FD_ISSET(filedes[i].fd, &rd)) {
+ if (filedes[i].events & POLLIN) {
+ filedes[i].revents |= POLLIN;
+ }
+ if (filedes[i].events & POLLRDNORM) {
+ filedes[i].revents |= POLLRDNORM;
+ }
+ fdHasEvent = PR_TRUE;
+ }
+ if (FD_ISSET(filedes[i].fd, &wr)) {
+ if (filedes[i].events & POLLOUT) {
+ filedes[i].revents |= POLLOUT;
+ }
+ if (filedes[i].events & POLLWRNORM) {
+ filedes[i].revents |= POLLWRNORM;
+ }
+ fdHasEvent = PR_TRUE;
+ }
+ if (FD_ISSET(filedes[i].fd, &ex)) {
+ if (filedes[i].events & POLLPRI) {
+ filedes[i].revents |= POLLPRI;
+ }
+ if (filedes[i].events & POLLRDBAND) {
+ filedes[i].revents |= POLLRDBAND;
+ }
+ fdHasEvent = PR_TRUE;
+ }
+ if (fdHasEvent) {
+ rv++;
+ }
+ }
+ PR_ASSERT(rv > 0);
+ } else if (rv == -1 && errno == EBADF) {
+ rv = 0;
+ for (i = 0; i < nfds; i++) {
+ filedes[i].revents = 0;
+ if (filedes[i].fd < 0) {
+ continue;
+ }
+ if (fcntl(filedes[i].fd, F_GETFL, 0) == -1) {
+ filedes[i].revents = POLLNVAL;
+ rv++;
+ }
+ }
+ PR_ASSERT(rv > 0);
+ }
+ PR_ASSERT(-1 != timeout || rv != 0);
+
+ return rv;
+}
+#endif /* _PR_NEED_FAKE_POLL */
diff --git a/pr/src/md/unix/ncr.c b/pr/src/md/unix/ncr.c
new file mode 100644
index 00000000..1560cc2c
--- /dev/null
+++ b/pr/src/md/unix/ncr.c
@@ -0,0 +1,367 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * NCR 3.0 - cloned from UnixWare by ruslan
+ */
+#include "primpl.h"
+
+#include <setjmp.h>
+
+void _MD_EarlyInit(void)
+{
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+}
+
+#ifdef ALARMS_BREAK_TCP /* I don't think they do */
+
+PRInt32 _MD_connect(PRInt32 osfd, const PRNetAddr *addr, PRInt32 addrlen,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv;
+
+ _MD_BLOCK_CLOCK_INTERRUPTS();
+ rv = _connect(osfd,addr,addrlen);
+ _MD_UNBLOCK_CLOCK_INTERRUPTS();
+}
+
+PRInt32 _MD_accept(PRInt32 osfd, PRNetAddr *addr, PRInt32 addrlen,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv;
+
+ _MD_BLOCK_CLOCK_INTERRUPTS();
+ rv = _accept(osfd,addr,addrlen);
+ _MD_UNBLOCK_CLOCK_INTERRUPTS();
+ return(rv);
+}
+#endif
+
+/*
+ * These are also implemented in pratom.c using NSPR locks. Any reason
+ * this might be better or worse? If you like this better, define
+ * _PR_HAVE_ATOMIC_OPS in include/md/unixware.h
+ */
+#ifdef _PR_HAVE_ATOMIC_OPS
+/* Atomic operations */
+#include <stdio.h>
+static FILE *_uw_semf;
+
+void
+_MD_INIT_ATOMIC(void)
+{
+ /* Sigh. Sure wish SYSV semaphores weren't such a pain to use */
+ if ((_uw_semf = tmpfile()) == NULL)
+ PR_ASSERT(0);
+
+ return;
+}
+
+void
+_MD_ATOMIC_INCREMENT(PRInt32 *val)
+{
+ flockfile(_uw_semf);
+ (*val)++;
+ unflockfile(_uw_semf);
+}
+
+void
+_MD_ATOMIC_DECREMENT(PRInt32 *val)
+{
+ flockfile(_uw_semf);
+ (*val)--;
+ unflockfile(_uw_semf);
+}
+
+void
+_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
+{
+ flockfile(_uw_semf);
+ *val = newval;
+ unflockfile(_uw_semf);
+}
+#endif
+
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for Unixware */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for Unixware.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRUintn priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for Unixware.");
+ return PR_FAILURE;
+}
+
+/*
+ This is temp. replacement for localtime_r. Normally PR_ExplodeTime should
+ be used as to my understanding
+*/
+
+/*
+** $$$$$ THEN WHY ARE WE DOING THIS? - AOF $$$$$
+*/
+
+#define NEED_LOCALTIME_R
+#define NEED_GMTIME_R
+#define NEED_ASCTIME_R
+#define NEED_STRTOK_R
+#define NEED_CTIME_R
+
+#if defined (NEED_LOCALTIME_R) || defined (NEED_CTIME_R) || defined (NEED_ASCTIME_R) || defined (NEED_GMTIME_R) || defined (NEED_STRTOK_R)
+#include "prlock.h"
+#endif
+
+#if defined (NEED_LOCALTIME_R)
+
+static PRLock *localtime_r_monitor = NULL;
+
+struct tm *localtime_r (const time_t *clock, struct tm *result)
+{
+ struct tm *tmPtr;
+ int needLock = PR_Initialized(); /* We need to use a lock to protect
+ * against NSPR threads only when the
+ * NSPR thread system is activated. */
+
+ if (needLock) {
+ if (localtime_r_monitor == NULL) {
+
+ localtime_r_monitor = PR_NewLock();
+ }
+ PR_Lock(localtime_r_monitor);
+ }
+
+ /*
+ * On Windows, localtime() returns a NULL pointer if 'clock'
+ * represents a time before midnight January 1, 1970. In
+ * that case, we also return a NULL pointer and the struct tm
+ * object pointed to by 'result' is not modified.
+ */
+
+ tmPtr = localtime(clock);
+ if (tmPtr) {
+ *result = *tmPtr;
+ } else {
+ result = NULL;
+ }
+
+ if (needLock) PR_Unlock(localtime_r_monitor);
+
+ return result;
+}
+
+#endif
+
+#if defined (NEED_GMTIME_R)
+
+static PRLock *gmtime_r_monitor = NULL;
+
+struct tm *gmtime_r (const time_t *clock, struct tm *result)
+{
+ struct tm *tmPtr;
+ int needLock = PR_Initialized(); /* We need to use a lock to protect
+ * against NSPR threads only when the
+ * NSPR thread system is activated. */
+
+ if (needLock) {
+ if (gmtime_r_monitor == NULL) {
+ gmtime_r_monitor = PR_NewLock();
+ }
+ PR_Lock(gmtime_r_monitor);
+ }
+
+ tmPtr = gmtime(clock);
+ if (tmPtr) {
+ *result = *tmPtr;
+ } else {
+ result = NULL;
+ }
+
+ if (needLock) PR_Unlock(gmtime_r_monitor);
+
+ return result;
+}
+
+#endif
+
+#if defined (NEED_CTIME_R)
+
+static PRLock *ctime_r_monitor = NULL;
+
+char *ctime_r (const time_t *clock, char *buf, int buflen)
+{
+ char *cbuf;
+ int needLock = PR_Initialized(); /* We need to use a lock to protect
+ * against NSPR threads only when the
+ * NSPR thread system is activated. */
+
+ if (needLock) {
+
+ if (ctime_r_monitor == NULL) {
+ ctime_r_monitor = PR_NewLock();
+ }
+ PR_Lock(ctime_r_monitor);
+ }
+
+ cbuf = ctime (clock);
+ if (cbuf) {
+ strncpy (buf, cbuf, buflen - 1);
+ buf[buflen - 1] = 0;
+ }
+
+ if (needLock) PR_Unlock(ctime_r_monitor);
+
+ return cbuf;
+}
+
+#endif
+
+#if defined (NEED_ASCTIME_R)
+
+static PRLock *asctime_r_monitor = NULL;
+
+
+char *asctime_r (const struct tm *tm, char *buf, int buflen)
+{
+ char *cbuf;
+ int needLock = PR_Initialized(); /* We need to use a lock to protect
+ * against NSPR threads only when the
+ * NSPR thread system is activated. */
+
+ if (needLock) {
+ if (asctime_r_monitor == NULL) {
+ asctime_r_monitor = PR_NewLock();
+ }
+ PR_Lock(asctime_r_monitor);
+ }
+
+ cbuf = asctime (tm);
+ if (cbuf) {
+ strncpy (buf, cbuf, buflen - 1);
+ buf[buflen - 1] = 0;
+ }
+
+ if (needLock) PR_Unlock(asctime_r_monitor);
+
+ return cbuf;
+
+}
+#endif
+
+#if defined (NEED_STRTOK_R)
+
+char *
+strtok_r (s, delim, last)
+ register char *s;
+ register const char *delim;
+ register char **last;
+{
+ register char *spanp;
+ register int c, sc;
+ char *tok;
+
+
+ if (s == NULL && (s = *last) == NULL)
+ return (NULL);
+
+ /*
+ * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
+ */
+cont:
+
+ c = *s++;
+ for (spanp = (char *)delim; (sc = *spanp++) != 0;) {
+ if (c == sc)
+ goto cont;
+ }
+
+ if (c == 0) { /* no non-delimiter characters */
+ *last = NULL;
+ return (NULL);
+ }
+ tok = s - 1;
+
+ /*
+ * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
+ * Note that delim must have one NUL; we stop if we see that, too.
+ */
+ for (;;) {
+ c = *s++;
+ spanp = (char *)delim;
+ do {
+ if ((sc = *spanp++) == c) {
+ if (c == 0)
+ s = NULL;
+
+ else
+ s[-1] = 0;
+ *last = s;
+ return (tok);
+ }
+ } while (sc != 0);
+ }
+ /* NOTREACHED */
+}
+
+#endif
diff --git a/pr/src/md/unix/nec.c b/pr/src/md/unix/nec.c
new file mode 100644
index 00000000..3b466cfa
--- /dev/null
+++ b/pr/src/md/unix/nec.c
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+void _MD_EarlyInit(void)
+{
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+}
+
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for NEC */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for NEC.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for NEC.");
+ return PR_FAILURE;
+}
diff --git a/pr/src/md/unix/objs.mk b/pr/src/md/unix/objs.mk
new file mode 100644
index 00000000..7882ce27
--- /dev/null
+++ b/pr/src/md/unix/objs.mk
@@ -0,0 +1,176 @@
+#
+# The contents of this file are subject to the Netscape Public License
+# Version 1.0 (the "NPL"); you may not use this file except in
+# compliance with the NPL. You may obtain a copy of the NPL at
+# http://www.mozilla.org/NPL/
+#
+# Software distributed under the NPL is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+# for the specific language governing rights and limitations under the
+# NPL.
+#
+# The Initial Developer of this code under the NPL is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1998 Netscape Communications Corporation. All Rights
+# Reserved.
+#
+
+# This makefile appends to the variable OBJS the platform-dependent
+# object modules that will be part of the nspr20 library.
+
+CSRCS = \
+ unix.c \
+ unix_errors.c \
+ uxproces.c \
+ uxwrap.c \
+ $(NULL)
+
+PTH_USER_CSRCS = \
+ pthreads_user.c \
+ $(NULL)
+
+IRIX_CSRCS = \
+ irix.c \
+ $(NULL)
+
+SUNOS4_CSRCS = \
+ sunos4.c \
+ $(NULL)
+
+SOLARIS_CSRCS = \
+ solaris.c \
+ $(NULL)
+
+AIX_CSRCS = \
+ aix.c \
+ $(NULL)
+
+FREEBSD_CSRCS = \
+ freebsd.c \
+ $(NULL)
+
+BSDI_CSRCS = \
+ bsdi.c \
+ $(NULL)
+
+HPUX_CSRCS = \
+ hpux.c \
+ $(NULL)
+
+OSF1_CSRCS = \
+ osf1.c \
+ $(NULL)
+
+LINUX_CSRCS = \
+ linux.c \
+ $(NULL)
+
+UNIXWARE_CSRCS = \
+ unixware.c \
+ $(NULL)
+
+RELIANTUNIX_CSRCS = \
+ reliantunix.c \
+ $(NULL)
+
+NEC_CSRCS = \
+ nec.c \
+ $(NULL)
+
+SONY_CSRCS = \
+ sony.c \
+ $(NULL)
+
+NCR_CSRCS = \
+ ncr.c \
+ $(NULL)
+
+SCOOS_CSRCS = \
+ scoos.c \
+ $(NULL)
+
+
+ifeq ($(PTHREADS_USER),1)
+CSRCS += $(PTH_USER_CSRCS)
+endif
+
+ifeq ($(OS_ARCH),IRIX)
+CSRCS += $(IRIX_CSRCS)
+endif
+
+ifeq ($(OS_ARCH),SunOS)
+ifeq ($(OS_RELEASE),4.1.3_U1)
+CSRCS += $(SUNOS4_CSRCS)
+else
+CSRCS += $(SOLARIS_CSRCS)
+endif
+endif
+
+ifeq ($(OS_ARCH),AIX)
+CSRCS += $(AIX_CSRCS)
+endif
+ifeq ($(OS_ARCH),FreeBSD)
+CSRCS += $(FREEBSD_CSRCS)
+endif
+ifeq ($(OS_ARCH),BSD_386)
+CSRCS += $(BSDI_CSRCS)
+endif
+ifeq ($(OS_ARCH),HP-UX)
+CSRCS += $(HPUX_CSRCS)
+endif
+ifeq ($(OS_ARCH),OSF1)
+CSRCS += $(OSF1_CSRCS)
+endif
+ifeq ($(OS_ARCH),Linux)
+CSRCS += $(LINUX_CSRCS)
+endif
+ifeq ($(OS_ARCH),UNIXWARE)
+CSRCS += $(UNIXWARE_CSRCS)
+endif
+ifeq ($(OS_ARCH),ReliantUNIX)
+CSRCS += $(RELIANTUNIX_CSRCS)
+endif
+ifeq ($(OS_ARCH),NEC)
+CSRCS += $(NEC_CSRCS)
+endif
+ifeq ($(OS_ARCH),NEWS-OS)
+CSRCS += $(SONY_CSRCS)
+endif
+ifeq ($(OS_ARCH),NCR)
+CSRCS += $(NCR_CSRCS)
+endif
+ifeq ($(OS_ARCH),SCO_SV)
+CSRCS += $(SCOOS_CSRCS)
+endif
+
+#
+# Some Unix platforms have an assembly language file.
+# E.g., AIX 3.2, Solaris (both sparc and x86).
+#
+ifeq ($(OS_ARCH), AIX)
+ ifeq ($(OS_RELEASE), 3.2)
+ ASFILES = os_$(OS_ARCH).s
+ endif
+endif
+
+ifeq ($(OS_ARCH),SunOS)
+ ifeq ($(OS_TEST),i86pc)
+ ASFILES = os_$(OS_ARCH)_x86.s
+ else
+ ifneq ($(OS_RELEASE),4.1.3_U1)
+ ASFILES = os_$(OS_ARCH).s
+ endif
+ endif
+endif
+
+ifeq ($(OS_ARCH), ReliantUNIX)
+ ASFILES = os_$(OS_ARCH).s
+endif
+
+ifeq ($(OS_ARCH)$(OS_RELEASE),BSD_3862.1)
+ ASFILES = os_BSD_386_2.s
+endif
+
+OBJS += $(addprefix md/unix/$(OBJDIR)/,$(CSRCS:.c=.o)) \
+ $(addprefix md/unix/$(OBJDIR)/,$(ASFILES:.s=.o))
+
diff --git a/pr/src/md/unix/os_AIX.s b/pr/src/md/unix/os_AIX.s
new file mode 100644
index 00000000..63ca4e78
--- /dev/null
+++ b/pr/src/md/unix/os_AIX.s
@@ -0,0 +1,102 @@
+# -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+#
+# The contents of this file are subject to the Netscape Public License
+# Version 1.0 (the "NPL"); you may not use this file except in
+# compliance with the NPL. You may obtain a copy of the NPL at
+# http://www.mozilla.org/NPL/
+#
+# Software distributed under the NPL is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+# for the specific language governing rights and limitations under the
+# NPL.
+#
+# The Initial Developer of this code under the NPL is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1998 Netscape Communications Corporation. All Rights
+# Reserved.
+#
+.set r0,0; .set SP,1; .set RTOC,2; .set r3,3; .set r4,4
+.set r5,5; .set r6,6; .set r7,7; .set r8,8; .set r9,9
+.set r10,10; .set r11,11; .set r12,12; .set r13,13; .set r14,14
+.set r15,15; .set r16,16; .set r17,17; .set r18,18; .set r19,19
+.set r20,20; .set r21,21; .set r22,22; .set r23,23; .set r24,24
+.set r25,25; .set r26,26; .set r27,27; .set r28,28; .set r29,29
+.set r30,30; .set r31,31
+
+
+ .rename H.10.NO_SYMBOL{PR},""
+ .rename H.18.longjmp{TC},"longjmp"
+
+ .lglobl H.10.NO_SYMBOL{PR}
+ .globl .longjmp
+ .globl longjmp{DS}
+ .extern .sigcleanup
+ .extern .jmprestfpr
+
+# .text section
+
+ .csect H.10.NO_SYMBOL{PR}
+.longjmp:
+ mr r13,r3
+ mr r14,r4
+ stu SP,-56(SP)
+ bl .sigcleanup
+ l RTOC,0x14(SP)
+ cal SP,0x38(SP)
+ mr r3,r13
+ mr r4,r14
+ l r5,0x8(r3)
+ l SP,0xc(r3)
+ l r7,0xf8(r3)
+ st r7,0x0(SP)
+ l RTOC,0x10(r3)
+ bl .jmprestfpr
+# 1 == cr0 in disassembly
+ cmpi 1,r4,0x0
+ mtlr r5
+ lm r13,0x14(r3)
+ l r5,0x60(r3)
+ mtcrf 0x38,r5
+ mr r3,r4
+ bne __L1
+ lil r3,0x1
+__L1:
+ br
+
+# traceback table
+ .long 0x00000000
+ .byte 0x00 # VERSION=0
+ .byte 0x00 # LANG=TB_C
+ .byte 0x20 # IS_GL=0,IS_EPROL=0,HAS_TBOFF=1
+ # INT_PROC=0,HAS_CTL=0,TOCLESS=0
+ # FP_PRESENT=0,LOG_ABORT=0
+ .byte 0x40 # INT_HNDL=0,NAME_PRESENT=1
+ # USES_ALLOCA=0,CL_DIS_INV=WALK_ONCOND
+ # SAVES_CR=0,SAVES_LR=0
+ .byte 0x80 # STORES_BC=1,FPR_SAVED=0
+ .byte 0x00 # GPR_SAVED=0
+ .byte 0x02 # FIXEDPARMS=2
+ .byte 0x01 # FLOATPARMS=0,PARMSONSTK=1
+ .long 0x00000000 #
+ .long 0x00000014 # TB_OFFSET
+ .short 7 # NAME_LEN
+ .byte "longjmp"
+ .byte 0 # padding
+ .byte 0 # padding
+ .byte 0 # padding
+# End of traceback table
+ .long 0x00000000 # "\0\0\0\0"
+
+# .data section
+
+ .toc # 0x00000038
+T.18.longjmp:
+ .tc H.18.longjmp{TC},longjmp{DS}
+
+ .csect longjmp{DS}
+ .long .longjmp # "\0\0\0\0"
+ .long TOC{TC0} # "\0\0\0008"
+ .long 0x00000000 # "\0\0\0\0"
+# End csect longjmp{DS}
+
+# .bss section
diff --git a/pr/src/md/unix/os_BSD_386_2.s b/pr/src/md/unix/os_BSD_386_2.s
new file mode 100644
index 00000000..971ac1b2
--- /dev/null
+++ b/pr/src/md/unix/os_BSD_386_2.s
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+/*
+ * os_BSD_386_2.s
+ * We need to define our own setjmp/longjmp on BSDI 2.x because libc's
+ * implementation does some sanity checking that defeats user level threads.
+ * This should no longer be necessary in BSDI 3.0.
+ */
+
+.globl _setjmp
+.align 2
+_setjmp:
+ movl 4(%esp),%eax
+ movl 0(%esp),%edx
+ movl %edx, 0(%eax) /* rta */
+ movl %ebx, 4(%eax)
+ movl %esp, 8(%eax)
+ movl %ebp,12(%eax)
+ movl %esi,16(%eax)
+ movl %edi,20(%eax)
+ movl $0,%eax
+ ret
+
+.globl _longjmp
+.align 2
+_longjmp:
+ movl 4(%esp),%edx
+ movl 8(%esp),%eax
+ movl 0(%edx),%ecx
+ movl 4(%edx),%ebx
+ movl 8(%edx),%esp
+ movl 12(%edx),%ebp
+ movl 16(%edx),%esi
+ movl 20(%edx),%edi
+ cmpl $0,%eax
+ jne 1f
+ movl $1,%eax
+1: movl %ecx,0(%esp)
+ ret
diff --git a/pr/src/md/unix/os_ReliantUNIX.s b/pr/src/md/unix/os_ReliantUNIX.s
new file mode 100644
index 00000000..4ac8b0c1
--- /dev/null
+++ b/pr/src/md/unix/os_ReliantUNIX.s
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+/* We want position independent code */
+#define PIC
+
+#include <sys/asm.h>
+#include <sys/regdef.h>
+#include <sys/syscall.h>
+
+ .file 1 "os_ReliantUNIX.s"
+ .option pic2
+ .text
+
+ .align 2
+ .globl getcxt
+ .ent getcxt
+getcxt:
+ .frame sp,0,$31 # vars= 0, regs= 0/0, args= 0, extra= 0
+ # saved integer regs
+ sw ra,180(a0) # gpregs[CXT_EPC]
+ sw gp,152(a0) # gpregs[CXT_GP]
+ sw sp,156(a0) # gpregs[CXT_SP]
+ sw s8,160(a0) # gpregs[CXT_S8]
+ sw s0,104(a0) # gpregs[CXT_S0]
+ sw s1,108(a0) # gpregs[CXT_S1]
+ sw s2,112(a0) # gpregs[CXT_S2]
+ sw s3,116(a0) # gpregs[CXT_S3]
+ sw s4,120(a0) # gpregs[CXT_S4]
+ sw s5,124(a0) # gpregs[CXT_S5]
+ sw s6,128(a0) # gpregs[CXT_S6]
+ sw s7,132(a0) # gpregs[CXT_S7]
+ # csr
+ cfc1 v0,$31
+ # saved float regs
+ s.d $f20,264(a0) # fpregs.fp_r.fp_dregs[10]
+ s.d $f22,272(a0) # fpregs.fp_r.fp_dregs[11]
+ s.d $f24,280(a0) # fpregs.fp_r.fp_dregs[12]
+ s.d $f26,288(a0) # fpregs.fp_r.fp_dregs[13]
+ s.d $f28,296(a0) # fpregs.fp_r.fp_dregs[14]
+ s.d $f30,304(a0) # fpregs.fp_r.fp_dregs[15]
+ sw v0,312(a0) # fpregs.fp_csr
+
+ # give no illusions about the contents
+ li v0,0x0c # UC_CPU | UC_MAU
+ sw v0,0(a0) # uc_flags
+
+ move v0,zero
+ j ra
+ .end getcxt
+
+ .align 2
+ .globl setcxt
+ .ent setcxt
+setcxt:
+ .frame sp,0,$31 # vars= 0, regs= 0/0, args= 0, extra= 0
+ lw v0,312(a0) # fpregs.fp_csr
+ li v1,0xfffc0fff # mask out exception cause bits
+ and v0,v0,v1
+ # saved integer regs
+ lw t9,180(a0) # gpregs[CXT_EPC]
+ lw ra,180(a0) # gpregs[CXT_EPC]
+ lw gp,152(a0) # gpregs[CXT_GP]
+ lw sp,156(a0) # gpregs[CXT_SP]
+ ctc1 v0,$31 # fp_csr
+ lw s8,160(a0) # gpregs[CXT_S8]
+ lw s0,104(a0) # gpregs[CXT_S0]
+ lw s1,108(a0) # gpregs[CXT_S1]
+ lw s2,112(a0) # gpregs[CXT_S2]
+ lw s3,116(a0) # gpregs[CXT_S3]
+ lw s4,120(a0) # gpregs[CXT_S4]
+ lw s5,124(a0) # gpregs[CXT_S5]
+ lw s6,128(a0) # gpregs[CXT_S6]
+ lw s7,132(a0) # gpregs[CXT_S7]
+ # saved float regs
+ l.d $f20,264(a0) # fpregs.fp_r.fp_dregs[10]
+ l.d $f22,272(a0) # fpregs.fp_r.fp_dregs[11]
+ l.d $f24,280(a0) # fpregs.fp_r.fp_dregs[12]
+ l.d $f26,288(a0) # fpregs.fp_r.fp_dregs[13]
+ l.d $f28,296(a0) # fpregs.fp_r.fp_dregs[14]
+ l.d $f30,304(a0) # fpregs.fp_r.fp_dregs[15]
+
+ # load these, too
+ # they were not saved, but maybe the user modified them...
+ lw v0,48(a0)
+ lw v1,52(a0)
+ lw a1,60(a0)
+ lw a2,64(a0)
+ lw a3,68(a0)
+ lw a0,56(a0) # there is no way back
+
+ j ra
+
+ .end setcxt
diff --git a/pr/src/md/unix/os_SunOS.s b/pr/src/md/unix/os_SunOS.s
new file mode 100644
index 00000000..2edf276f
--- /dev/null
+++ b/pr/src/md/unix/os_SunOS.s
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+ .text
+
+/*
+ * sol_getsp()
+ *
+ * Return the current sp (for debugging)
+ */
+ .global sol_getsp
+sol_getsp:
+ retl
+ mov %sp, %o0
+
+
+/*
+ * sol_curthread()
+ *
+ * Return a unique identifier for the currently active thread.
+ */
+ .global sol_curthread
+sol_curthread:
+ retl
+ mov %g7, %o0
+
+
+ .global __MD_FlushRegisterWindows
+ .global _MD_FlushRegisterWindows
+
+__MD_FlushRegisterWindows:
+_MD_FlushRegisterWindows:
+
+ ta 3
+ ret
+ restore
+
diff --git a/pr/src/md/unix/os_SunOS_ultrasparc.s b/pr/src/md/unix/os_SunOS_ultrasparc.s
new file mode 100644
index 00000000..b98b820f
--- /dev/null
+++ b/pr/src/md/unix/os_SunOS_ultrasparc.s
@@ -0,0 +1,157 @@
+! -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+!
+! The contents of this file are subject to the Netscape Public License
+! Version 1.0 (the "NPL"); you may not use this file except in
+! compliance with the NPL. You may obtain a copy of the NPL at
+! http://www.mozilla.org/NPL/
+!
+! Software distributed under the NPL is distributed on an "AS IS" basis,
+! WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+! for the specific language governing rights and limitations under the
+! NPL.
+!
+! The Initial Developer of this code under the NPL is Netscape
+! Communications Corporation. Portions created by Netscape are
+! Copyright (C) 1998 Netscape Communications Corporation. All Rights
+! Reserved.
+!
+!
+! atomic increment, decrement and swap routines for V8+ sparc (ultrasparc)
+! using CAS (compare-and-swap) atomic instructions
+!
+! this MUST be compiled with an ultrasparc-aware assembler
+!
+! standard asm linkage macros; this module must be compiled
+! with the -P option (use C preprocessor)
+
+#include <sys/asm_linkage.h>
+
+! ======================================================================
+!
+! Perform the sequence a = a + 1 atomically with respect to other
+! fetch-and-adds to location a in a wait-free fashion.
+!
+! usage : val = PR_AtomicIncrement(address)
+! return: current value (you'd think this would be old val)
+!
+! -----------------------
+! Note on REGISTER USAGE:
+! as this is a LEAF procedure, a new stack frame is not created;
+! we use the caller's stack frame so what would normally be %i (input)
+! registers are actually %o (output registers). Also, we must not
+! overwrite the contents of %l (local) registers as they are not
+! assumed to be volatile during calls.
+!
+! So, the registers used are:
+! %o0 [input] - the address of the value to increment
+! %o1 [local] - work register
+! %o2 [local] - work register
+! %o3 [local] - work register
+! -----------------------
+
+ ENTRY(PR_AtomicIncrement) ! standard assembler/ELF prologue
+
+retryAI:
+ ld [%o0], %o2 ! set o2 to the current value
+ add %o2, 0x1, %o3 ! calc the new value
+ mov %o3, %o1 ! save the return value
+ cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed
+ cmp %o2, %o3 ! see if we set the value
+ bne retryAI ! if not, try again
+ nop ! empty out the branch pipeline
+ retl ! return back to the caller
+ mov %o1, %o0 ! set the return code to the new value
+
+ SET_SIZE(PR_AtomicIncrement) ! standard assembler/ELF epilogue
+
+!
+! end
+!
+! ======================================================================
+!
+
+! ======================================================================
+!
+! Perform the sequence a = a - 1 atomically with respect to other
+! fetch-and-decs to location a in a wait-free fashion.
+!
+! usage : val = PR_AtomicDecrement(address)
+! return: current value (you'd think this would be old val)
+!
+! -----------------------
+! Note on REGISTER USAGE:
+! as this is a LEAF procedure, a new stack frame is not created;
+! we use the caller's stack frame so what would normally be %i (input)
+! registers are actually %o (output registers). Also, we must not
+! overwrite the contents of %l (local) registers as they are not
+! assumed to be volatile during calls.
+!
+! So, the registers used are:
+! %o0 [input] - the address of the value to increment
+! %o1 [local] - work register
+! %o2 [local] - work register
+! %o3 [local] - work register
+! -----------------------
+
+ ENTRY(PR_AtomicDecrement) ! standard assembler/ELF prologue
+
+retryAD:
+ ld [%o0], %o2 ! set o2 to the current value
+ sub %o2, 0x1, %o3 ! calc the new value
+ mov %o3, %o1 ! save the return value
+ cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed
+ cmp %o2, %o3 ! see if we set the value
+ bne retryAD ! if not, try again
+ nop ! empty out the branch pipeline
+ retl ! return back to the caller
+ mov %o1, %o0 ! set the return code to the new value
+
+ SET_SIZE(PR_AtomicDecrement) ! standard assembler/ELF epilogue
+
+!
+! end
+!
+! ======================================================================
+!
+
+! ======================================================================
+!
+! Perform the sequence a = b atomically with respect to other
+! fetch-and-stores to location a in a wait-free fashion.
+!
+! usage : old_val = PR_AtomicSet(address, newval)
+!
+! -----------------------
+! Note on REGISTER USAGE:
+! as this is a LEAF procedure, a new stack frame is not created;
+! we use the caller's stack frame so what would normally be %i (input)
+! registers are actually %o (output registers). Also, we must not
+! overwrite the contents of %l (local) registers as they are not
+! assumed to be volatile during calls.
+!
+! So, the registers used are:
+! %o0 [input] - the address of the value to increment
+! %o1 [input] - the new value to set for [%o0]
+! %o2 [local] - work register
+! %o3 [local] - work register
+! -----------------------
+
+ ENTRY(PR_AtomicSet) ! standard assembler/ELF prologue
+
+retryAS:
+ ld [%o0], %o2 ! set o2 to the current value
+ mov %o1, %o3 ! set up the new value
+ cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed
+ cmp %o2, %o3 ! see if we set the value
+ bne retryAS ! if not, try again
+ nop ! empty out the branch pipeline
+ retl ! return back to the caller
+ mov %o3, %o0 ! set the return code to the prev value
+
+ SET_SIZE(PR_AtomicSet) ! standard assembler/ELF epilogue
+
+!
+! end
+!
+! ======================================================================
+!
diff --git a/pr/src/md/unix/os_SunOS_x86.s b/pr/src/md/unix/os_SunOS_x86.s
new file mode 100644
index 00000000..867fd2d3
--- /dev/null
+++ b/pr/src/md/unix/os_SunOS_x86.s
@@ -0,0 +1,60 @@
+/ -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+/
+/ The contents of this file are subject to the Netscape Public License
+/ Version 1.0 (the "NPL"); you may not use this file except in
+/ compliance with the NPL. You may obtain a copy of the NPL at
+/ http://www.mozilla.org/NPL/
+/
+/ Software distributed under the NPL is distributed on an "AS IS" basis,
+/ WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+/ for the specific language governing rights and limitations under the
+/ NPL.
+/
+/ The Initial Developer of this code under the NPL is Netscape
+/ Communications Corporation. Portions created by Netscape are
+/ Copyright (C) 1998 Netscape Communications Corporation. All Rights
+/ Reserved.
+/
+ .text
+
+ .globl getedi
+getedi:
+ movl %edi,%eax
+ ret
+ .type getedi,@function
+ .size getedi,.-getedi
+
+ .globl setedi
+setedi:
+ movl 4(%esp),%edi
+ ret
+ .type setedi,@function
+ .size setedi,.-setedi
+
+ .globl __MD_FlushRegisterWindows
+ .globl _MD_FlushRegisterWindows
+
+__MD_FlushRegisterWindows:
+_MD_FlushRegisterWindows:
+
+ ret
+
+/
+/ sol_getsp()
+/
+/ Return the current sp (for debugging)
+/
+ .globl sol_getsp
+sol_getsp:
+ movl %esp, %eax
+ ret
+
+/
+/ sol_curthread()
+/
+/ Return a unique identifier for the currently active thread.
+/
+ .globl sol_curthread
+sol_curthread:
+ movl %ecx, %eax
+ ret
diff --git a/pr/src/md/unix/osf1.c b/pr/src/md/unix/osf1.c
new file mode 100644
index 00000000..e79e6607
--- /dev/null
+++ b/pr/src/md/unix/osf1.c
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+void _MD_EarlyInit(void)
+{
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+#ifndef _PR_PTHREADS
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+#else
+ *np = 0;
+ return NULL;
+#endif
+}
+
+#ifndef _PR_PTHREADS
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for OSF1 */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for OSF1.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for OSF1.");
+ return PR_FAILURE;
+}
+#endif /* ! _PR_PTHREADS */
diff --git a/pr/src/md/unix/pthreads_user.c b/pr/src/md/unix/pthreads_user.c
new file mode 100644
index 00000000..efc3ff70
--- /dev/null
+++ b/pr/src/md/unix/pthreads_user.c
@@ -0,0 +1,465 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include <signal.h>
+#include <pthread.h>
+
+
+sigset_t ints_off;
+pthread_mutex_t _pr_heapLock;
+pthread_key_t current_thread_key;
+pthread_key_t current_cpu_key;
+pthread_key_t last_thread_key;
+pthread_key_t intsoff_key;
+
+
+PRInt32 _pr_md_pthreads_created, _pr_md_pthreads_failed;
+PRInt32 _pr_md_pthreads = 1;
+
+void _MD_EarlyInit(void)
+{
+extern PRInt32 _nspr_noclock;
+
+#ifdef HPUX
+ _MD_hpux_install_sigfpe_handler();
+#endif
+
+ if (pthread_key_create(&current_thread_key, NULL) != 0) {
+ perror("pthread_key_create failed");
+ exit(1);
+ }
+ if (pthread_key_create(&current_cpu_key, NULL) != 0) {
+ perror("pthread_key_create failed");
+ exit(1);
+ }
+ if (pthread_key_create(&last_thread_key, NULL) != 0) {
+ perror("pthread_key_create failed");
+ exit(1);
+ }
+ if (pthread_key_create(&intsoff_key, NULL) != 0) {
+ perror("pthread_key_create failed");
+ exit(1);
+ }
+
+ sigemptyset(&ints_off);
+ sigaddset(&ints_off, SIGALRM);
+ sigaddset(&ints_off, SIGIO);
+ sigaddset(&ints_off, SIGCLD);
+
+ /*
+ * disable clock interrupts
+ */
+ _nspr_noclock = 1;
+
+}
+
+void _MD_InitLocks()
+{
+ if (pthread_mutex_init(&_pr_heapLock, NULL) != 0) {
+ perror("pthread_mutex_init failed");
+ exit(1);
+ }
+}
+
+PR_IMPLEMENT(void) _MD_FREE_LOCK(struct _MDLock *lockp)
+{
+ PRIntn _is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (me && !_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(_is);
+ pthread_mutex_destroy(&lockp->mutex);
+ if (me && !_PR_IS_NATIVE_THREAD(me))
+ _PR_FAST_INTSON(_is);
+}
+
+
+
+PR_IMPLEMENT(PRStatus) _MD_NEW_LOCK(struct _MDLock *lockp)
+{
+ PRStatus rv;
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (me && !_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(is);
+ rv = pthread_mutex_init(&lockp->mutex, NULL);
+ if (me && !_PR_IS_NATIVE_THREAD(me))
+ _PR_FAST_INTSON(is);
+ return (rv == 0) ? PR_SUCCESS : PR_FAILURE;
+}
+
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+}
+
+PR_IMPLEMENT(void)
+_MD_SetPriority(_MDThread *thread, PRThreadPriority newPri)
+{
+ /*
+ * XXX - to be implemented
+ */
+ return;
+}
+
+PR_IMPLEMENT(PRStatus) _MD_InitThread(struct PRThread *thread)
+{
+ struct sigaction sigact;
+
+ if (thread->flags & _PR_GLOBAL_SCOPE) {
+ thread->md.pthread = pthread_self();
+#if 0
+ /*
+ * set up SIGUSR1 handler; this is used to save state
+ * during PR_SuspendAll
+ */
+ sigact.sa_handler = save_context_and_block;
+ sigact.sa_flags = SA_RESTART;
+ /*
+ * Must mask clock interrupts
+ */
+ sigact.sa_mask = timer_set;
+ sigaction(SIGUSR1, &sigact, 0);
+#endif
+ }
+
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(void) _MD_ExitThread(struct PRThread *thread)
+{
+ if (thread->flags & _PR_GLOBAL_SCOPE) {
+ _MD_CLEAN_THREAD(thread);
+ _MD_SET_CURRENT_THREAD(NULL);
+ }
+}
+
+PR_IMPLEMENT(void) _MD_CleanThread(struct PRThread *thread)
+{
+ if (thread->flags & _PR_GLOBAL_SCOPE) {
+ pthread_mutex_destroy(&thread->md.pthread_mutex);
+ pthread_cond_destroy(&thread->md.pthread_cond);
+ }
+}
+
+PR_IMPLEMENT(void) _MD_SuspendThread(struct PRThread *thread)
+{
+ PRInt32 rv;
+
+ PR_ASSERT((thread->flags & _PR_GLOBAL_SCOPE) &&
+ (thread->flags & _PR_GCABLE_THREAD));
+#if 0
+ thread->md.suspending_id = getpid();
+ rv = kill(thread->md.id, SIGUSR1);
+ PR_ASSERT(rv == 0);
+ /*
+ * now, block the current thread/cpu until woken up by the suspended
+ * thread from it's SIGUSR1 signal handler
+ */
+ blockproc(getpid());
+#endif
+}
+
+PR_IMPLEMENT(void) _MD_ResumeThread(struct PRThread *thread)
+{
+ PRInt32 rv;
+
+ PR_ASSERT((thread->flags & _PR_GLOBAL_SCOPE) &&
+ (thread->flags & _PR_GCABLE_THREAD));
+#if 0
+ rv = unblockproc(thread->md.id);
+#endif
+}
+
+PR_IMPLEMENT(void) _MD_SuspendCPU(struct _PRCPU *thread)
+{
+ PRInt32 rv;
+
+#if 0
+ cpu->md.suspending_id = getpid();
+ rv = kill(cpu->md.id, SIGUSR1);
+ PR_ASSERT(rv == 0);
+ /*
+ * now, block the current thread/cpu until woken up by the suspended
+ * thread from it's SIGUSR1 signal handler
+ */
+ blockproc(getpid());
+#endif
+}
+
+PR_IMPLEMENT(void) _MD_ResumeCPU(struct _PRCPU *thread)
+{
+#if 0
+ unblockproc(cpu->md.id);
+#endif
+}
+
+
+#define PT_NANOPERMICRO 1000UL
+#define PT_BILLION 1000000000UL
+
+PR_IMPLEMENT(PRStatus)
+_pt_wait(PRThread *thread, PRIntervalTime timeout)
+{
+int rv;
+struct timeval now;
+struct timespec tmo;
+PRUint32 ticks = PR_TicksPerSecond();
+
+
+ if (timeout != PR_INTERVAL_NO_TIMEOUT) {
+ tmo.tv_sec = timeout / ticks;
+ tmo.tv_nsec = timeout - (tmo.tv_sec * ticks);
+ tmo.tv_nsec = PR_IntervalToMicroseconds(PT_NANOPERMICRO *
+ tmo.tv_nsec);
+
+ /* pthreads wants this in absolute time, off we go ... */
+#if defined(SOLARIS) && defined(_SVID_GETTOD)
+ (void)gettimeofday(&now);
+#else
+ (void)gettimeofday(&now, NULL);
+#endif
+ /* that one's usecs, this one's nsecs - grrrr! */
+ tmo.tv_sec += now.tv_sec;
+ tmo.tv_nsec += (PT_NANOPERMICRO * now.tv_usec);
+ tmo.tv_sec += tmo.tv_nsec / PT_BILLION;
+ tmo.tv_nsec %= PT_BILLION;
+ }
+
+ pthread_mutex_lock(&thread->md.pthread_mutex);
+ thread->md.wait--;
+ if (thread->md.wait < 0) {
+ if (timeout != PR_INTERVAL_NO_TIMEOUT) {
+ rv = pthread_cond_timedwait(&thread->md.pthread_cond,
+ &thread->md.pthread_mutex, &tmo);
+ }
+ else
+ rv = pthread_cond_wait(&thread->md.pthread_cond,
+ &thread->md.pthread_mutex);
+ if (rv != 0) {
+ thread->md.wait++;
+ }
+ } else
+ rv = 0;
+ pthread_mutex_unlock(&thread->md.pthread_mutex);
+
+ return (rv == 0) ? PR_SUCCESS : PR_FAILURE;
+}
+
+PR_IMPLEMENT(PRStatus)
+_MD_wait(PRThread *thread, PRIntervalTime ticks)
+{
+ if ( thread->flags & _PR_GLOBAL_SCOPE ) {
+ _MD_CHECK_FOR_EXIT();
+ if (_pt_wait(thread, ticks) == PR_FAILURE) {
+ _MD_CHECK_FOR_EXIT();
+ /*
+ * wait timed out
+ */
+ _PR_THREAD_LOCK(thread);
+ if (thread->wait.cvar) {
+ /*
+ * The thread will remove itself from the waitQ
+ * of the cvar in _PR_WaitCondVar
+ */
+ thread->wait.cvar = NULL;
+ thread->state = _PR_RUNNING;
+ _PR_THREAD_UNLOCK(thread);
+ } else {
+ _pt_wait(thread, PR_INTERVAL_NO_TIMEOUT);
+ _PR_THREAD_UNLOCK(thread);
+ }
+ }
+ } else {
+ _PR_MD_SWITCH_CONTEXT(thread);
+ }
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus)
+_MD_WakeupWaiter(PRThread *thread)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 pid, rv;
+ PRIntn is;
+
+ PR_ASSERT(_pr_md_idle_cpus >= 0);
+ if (thread == NULL) {
+ if (_pr_md_idle_cpus)
+ _MD_Wakeup_CPUs();
+ } else if (!_PR_IS_NATIVE_THREAD(thread)) {
+ /*
+ * If the thread is on my cpu's runq there is no need to
+ * wakeup any cpus
+ */
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ if (me->cpu != thread->cpu) {
+ if (_pr_md_idle_cpus)
+ _MD_Wakeup_CPUs();
+ }
+ } else {
+ if (_pr_md_idle_cpus)
+ _MD_Wakeup_CPUs();
+ }
+ } else {
+ PR_ASSERT(_PR_IS_NATIVE_THREAD(thread));
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(is);
+
+ pthread_mutex_lock(&thread->md.pthread_mutex);
+ thread->md.wait++;
+ rv = pthread_cond_signal(&thread->md.pthread_cond);
+ PR_ASSERT(rv == 0);
+ pthread_mutex_unlock(&thread->md.pthread_mutex);
+
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_FAST_INTSON(is);
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for AIX */
+PR_IMPLEMENT(void)
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for AIX.");
+}
+
+PR_IMPLEMENT(PRStatus)
+_MD_CreateThread(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ pthread_attr_t attr;
+
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(is);
+
+ if (pthread_mutex_init(&thread->md.pthread_mutex, NULL) != 0) {
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_FAST_INTSON(is);
+ return PR_FAILURE;
+ }
+
+ if (pthread_cond_init(&thread->md.pthread_cond, NULL) != 0) {
+ pthread_mutex_destroy(&thread->md.pthread_mutex);
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_FAST_INTSON(is);
+ return PR_FAILURE;
+ }
+ thread->flags |= _PR_GLOBAL_SCOPE;
+
+ pthread_attr_init(&attr); /* initialize attr with default attributes */
+ if (pthread_attr_setstacksize(&attr, (size_t) stackSize) != 0) {
+ pthread_mutex_destroy(&thread->md.pthread_mutex);
+ pthread_cond_destroy(&thread->md.pthread_cond);
+ pthread_attr_destroy(&attr);
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_FAST_INTSON(is);
+ return PR_FAILURE;
+ }
+
+ thread->md.wait = 0;
+ if (pthread_create(&thread->md.pthread, &attr, start, (void *)thread)
+ == 0) {
+ _MD_ATOMIC_INCREMENT(&_pr_md_pthreads_created);
+ _MD_ATOMIC_INCREMENT(&_pr_md_pthreads);
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_FAST_INTSON(is);
+ return PR_SUCCESS;
+ } else {
+ pthread_mutex_destroy(&thread->md.pthread_mutex);
+ pthread_cond_destroy(&thread->md.pthread_cond);
+ pthread_attr_destroy(&attr);
+ _MD_ATOMIC_INCREMENT(&_pr_md_pthreads_failed);
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_FAST_INTSON(is);
+ return PR_FAILURE;
+ }
+}
+
+PR_IMPLEMENT(void)
+_MD_InitRunningCPU(struct _PRCPU *cpu)
+{
+ extern int _pr_md_pipefd[2];
+
+ _MD_unix_init_running_cpu(cpu);
+ cpu->md.pthread = pthread_self();
+ if (_pr_md_pipefd[0] >= 0) {
+ _PR_IOQ_MAX_OSFD(cpu) = _pr_md_pipefd[0];
+ FD_SET(_pr_md_pipefd[0], &_PR_FD_READ_SET(cpu));
+ }
+}
+
+
+void
+_MD_CleanupBeforeExit(void)
+{
+#if 0
+ extern PRInt32 _pr_cpus_exit;
+
+ _pr_irix_exit_now = 1;
+ if (_pr_numCPU > 1) {
+ /*
+ * Set a global flag, and wakeup all cpus which will notice the flag
+ * and exit.
+ */
+ _pr_cpus_exit = getpid();
+ _MD_Wakeup_CPUs();
+ while(_pr_numCPU > 1) {
+ _PR_WAIT_SEM(_pr_irix_exit_sem);
+ _pr_numCPU--;
+ }
+ }
+ /*
+ * cause global threads on the recycle list to exit
+ */
+ _PR_DEADQ_LOCK;
+ if (_PR_NUM_DEADNATIVE != 0) {
+ PRThread *thread;
+ PRCList *ptr;
+
+ ptr = _PR_DEADNATIVEQ.next;
+ while( ptr != &_PR_DEADNATIVEQ ) {
+ thread = _PR_THREAD_PTR(ptr);
+ _MD_CVAR_POST_SEM(thread);
+ ptr = ptr->next;
+ }
+ }
+ _PR_DEADQ_UNLOCK;
+ while(_PR_NUM_DEADNATIVE > 1) {
+ _PR_WAIT_SEM(_pr_irix_exit_sem);
+ _PR_DEC_DEADNATIVE;
+ }
+#endif
+}
diff --git a/pr/src/md/unix/reliantunix.c b/pr/src/md/unix/reliantunix.c
new file mode 100644
index 00000000..755d7f8c
--- /dev/null
+++ b/pr/src/md/unix/reliantunix.c
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * ReliantUNIX5.4 - copied from unixware.c by chrisk 040497
+ */
+#include "primpl.h"
+
+#include <ucontext.h>
+
+void _MD_EarlyInit(void)
+{
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ if (isCurrent) {
+ (void) _GETCONTEXT(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+}
+
+#ifdef ALARMS_BREAK_TCP /* I don't think they do */
+
+PRInt32 _MD_connect(PRInt32 osfd, PRNetAddr *addr, PRInt32 addrlen,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv;
+
+ _MD_BLOCK_CLOCK_INTERRUPTS();
+ rv = _connect(osfd,addr,addrlen);
+ _MD_UNBLOCK_CLOCK_INTERRUPTS();
+}
+
+PRInt32 _MD_accept(PRInt32 osfd, PRNetAddr *addr, PRInt32 addrlen,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv;
+
+ _MD_BLOCK_CLOCK_INTERRUPTS();
+ rv = _accept(osfd,addr,addrlen);
+ _MD_UNBLOCK_CLOCK_INTERRUPTS();
+ return(rv);
+}
+#endif
+
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for ReliantUNIX */
+/* Why? Just copied it from UNIXWARE... flying-by-night, chrisk 040497 */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for ReliantUNIX.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRUintn priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for ReliantUNIX.");
+#if defined(SNI) && !defined(__GNUC__)
+ /* make compiler happy */
+ return (PRStatus)NULL;
+#endif
+}
diff --git a/pr/src/md/unix/scoos.c b/pr/src/md/unix/scoos.c
new file mode 100644
index 00000000..14365e8e
--- /dev/null
+++ b/pr/src/md/unix/scoos.c
@@ -0,0 +1,154 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * SCO ODT 5.0 - originally created by mikep
+ */
+#include "primpl.h"
+
+#include <setjmp.h>
+
+void _MD_EarlyInit(void)
+{
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+}
+
+#ifdef ALARMS_BREAK_TCP /* I don't think they do */
+
+PRInt32 _MD_connect(PRInt32 osfd, PRNetAddr *addr, PRInt32 addrlen,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv;
+
+ _MD_BLOCK_CLOCK_INTERRUPTS();
+ rv = _connect(osfd,addr,addrlen);
+ _MD_UNBLOCK_CLOCK_INTERRUPTS();
+}
+
+PRInt32 _MD_accept(PRInt32 osfd, PRNetAddr *addr, PRInt32 addrlen,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv;
+
+ _MD_BLOCK_CLOCK_INTERRUPTS();
+ rv = _accept(osfd,addr,addrlen);
+ _MD_UNBLOCK_CLOCK_INTERRUPTS();
+ return(rv);
+}
+#endif
+
+/*
+ * These are also implemented in pratom.c using NSPR locks. Any reason
+ * this might be better or worse? If you like this better, define
+ * _PR_HAVE_ATOMIC_OPS in include/md/unixware.h
+ */
+#ifdef _PR_HAVE_ATOMIC_OPS
+/* Atomic operations */
+#include <stdio.h>
+static FILE *_uw_semf;
+
+void
+_MD_INIT_ATOMIC(void)
+{
+ /* Sigh. Sure wish SYSV semaphores weren't such a pain to use */
+ if ((_uw_semf = tmpfile()) == NULL)
+ PR_ASSERT(0);
+
+ return;
+}
+
+void
+_MD_ATOMIC_INCREMENT(PRInt32 *val)
+{
+ flockfile(_uw_semf);
+ (*val)++;
+ unflockfile(_uw_semf);
+}
+
+void
+_MD_ATOMIC_DECREMENT(PRInt32 *val)
+{
+ flockfile(_uw_semf);
+ (*val)--;
+ unflockfile(_uw_semf);
+}
+
+void
+_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
+{
+ flockfile(_uw_semf);
+ *val = newval;
+ unflockfile(_uw_semf);
+}
+#endif
+
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for SCO */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for SCO.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for SCO.");
+}
diff --git a/pr/src/md/unix/solaris.c b/pr/src/md/unix/solaris.c
new file mode 100644
index 00000000..8658ebbb
--- /dev/null
+++ b/pr/src/md/unix/solaris.c
@@ -0,0 +1,808 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+
+extern PRBool suspendAllOn;
+extern PRThread *suspendAllThread;
+
+extern void _MD_SET_PRIORITY(_MDThread *md, PRThreadPriority newPri);
+
+PRIntervalTime _MD_Solaris_TicksPerSecond(void)
+{
+ /*
+ * Ticks have a 10-microsecond resolution. So there are
+ * 100000 ticks per second.
+ */
+ return 100000UL;
+}
+
+/* Interval timers, implemented using gethrtime() */
+
+PRIntervalTime _MD_Solaris_GetInterval(void)
+{
+ union {
+ hrtime_t hrt; /* hrtime_t is a 64-bit (long long) integer */
+ PRInt64 pr64;
+ } time;
+ PRInt64 resolution;
+ PRIntervalTime ticks;
+
+ time.hrt = gethrtime(); /* in nanoseconds */
+ /*
+ * Convert from nanoseconds to ticks. A tick's resolution is
+ * 10 microseconds, or 10000 nanoseconds.
+ */
+ LL_I2L(resolution, 10000);
+ LL_DIV(time.pr64, time.pr64, resolution);
+ LL_L2UI(ticks, time.pr64);
+ return ticks;
+}
+
+#ifdef _PR_PTHREADS
+void _MD_EarlyInit(void)
+{
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, PRIntn isCurrent, PRIntn *np)
+{
+ *np = 0;
+ return NULL;
+}
+#endif /* _PR_PTHREADS */
+
+#if defined(_PR_HAVE_ATOMIC_OPS)
+/* NOTE:
+ * SPARC v9 (Ultras) do have an atomic test-and-set operation. But
+ * SPARC v8 doesn't. We should detect in the init if we are running on
+ * v8 or v9, and then use assembly where we can.
+ *
+ * This code uses the Solaris threads API. It can be used in both the
+ * pthreads and Solaris threads versions of nspr20 because "POSIX threads
+ * and Solaris threads are fully compatible even within the same process",
+ * to quote from pthread_create(3T).
+ */
+
+#include <thread.h>
+#include <synch.h>
+
+static mutex_t _solaris_atomic = DEFAULTMUTEX;
+
+PRInt32
+_MD_AtomicIncrement(PRInt32 *val)
+{
+ PRInt32 rv;
+ if (mutex_lock(&_solaris_atomic) != 0)
+ PR_ASSERT(0);
+
+ rv = ++(*val);
+
+ if (mutex_unlock(&_solaris_atomic) != 0)\
+ PR_ASSERT(0);
+
+ return rv;
+}
+
+PRInt32
+_MD_AtomicDecrement(PRInt32 *val)
+{
+ PRInt32 rv;
+ if (mutex_lock(&_solaris_atomic) != 0)
+ PR_ASSERT(0);
+
+ rv = --(*val);
+
+ if (mutex_unlock(&_solaris_atomic) != 0)\
+ PR_ASSERT(0);
+
+ return rv;
+}
+
+PRInt32
+_MD_AtomicSet(PRInt32 *val, PRInt32 newval)
+{
+ PRInt32 rv;
+ if (mutex_lock(&_solaris_atomic) != 0)
+ PR_ASSERT(0);
+
+ rv = *val;
+ *val = newval;
+
+ if (mutex_unlock(&_solaris_atomic) != 0)\
+ PR_ASSERT(0);
+
+ return rv;
+}
+#endif /* _PR_HAVE_ATOMIC_OPS */
+
+#if defined(_PR_GLOBAL_THREADS_ONLY)
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <thread.h>
+
+#include <sys/lwp.h>
+#include <sys/procfs.h>
+#include <sys/syscall.h>
+extern int syscall(); /* not declared in sys/syscall.h */
+
+static sigset_t old_mask; /* store away original gc thread sigmask */
+static PRIntn gcprio; /* store away original gc thread priority */
+
+THREAD_KEY_T threadid_key;
+THREAD_KEY_T cpuid_key;
+THREAD_KEY_T last_thread_key;
+static sigset_t set, oldset;
+
+void _MD_EarlyInit(void)
+{
+ THR_KEYCREATE(&threadid_key, NULL);
+ THR_KEYCREATE(&cpuid_key, NULL);
+ THR_KEYCREATE(&last_thread_key, NULL);
+ sigemptyset(&set);
+ sigaddset(&set, SIGALRM);
+}
+
+PRStatus _MD_CreateThread(PRThread *thread,
+ void (*start)(void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PRInt32 flags;
+
+ /* mask out SIGALRM for native thread creation */
+ thr_sigsetmask(SIG_BLOCK, &set, &oldset);
+
+ flags = (state == PR_JOINABLE_THREAD) ?
+ THR_SUSPENDED : THR_SUSPENDED|THR_DETACHED;
+ if (thread->flags & (_PR_GCABLE_THREAD|_PR_BOUND_THREAD))
+ flags |= THR_BOUND;
+
+ if (thr_create(NULL, thread->stack->stackSize,
+ (void *(*)(void *)) start, (void *) thread,
+ flags,
+ &thread->md.handle)) {
+ thr_sigsetmask(SIG_SETMASK, &oldset, NULL);
+ return PR_FAILURE;
+ }
+
+ /* When the thread starts running, then the lwpid is set to the right
+ * value. Until then we want to mark this as 'uninit' so that
+ * its register state is initialized properly for GC */
+
+ thread->md.lwpid = -1;
+ thr_sigsetmask(SIG_SETMASK, &oldset, NULL);
+ _MD_NEW_SEM(&thread->md.waiter_sem, 0);
+
+ if (scope == PR_GLOBAL_THREAD) {
+ thread->flags |= _PR_GLOBAL_SCOPE;
+ }
+
+ _MD_SET_PRIORITY(&(thread->md), priority);
+
+ /* Activate the thread */
+ if (thr_continue( thread->md.handle ) ) {
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+void _MD_cleanup_thread(PRThread *thread)
+{
+ thread_t hdl;
+
+ hdl = thread->md.handle;
+
+ /*
+ ** First, suspend the thread (unless it's the active one)
+ ** Because we suspend it first, we don't have to use LOCK_SCHEDULER to
+ ** prevent both of us modifying the thread structure at the same time.
+ */
+ if ( thread != _PR_MD_CURRENT_THREAD() ) {
+ thr_suspend(hdl);
+ }
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("(0X%x)[DestroyThread]\n", thread));
+
+ _MD_DESTROY_SEM(&thread->md.waiter_sem);
+}
+
+void _MD_exit_thread(PRThread *thread)
+{
+ _MD_CLEAN_THREAD(thread);
+ _MD_SET_CURRENT_THREAD(NULL);
+}
+
+void _MD_SET_PRIORITY(_MDThread *md_thread,
+ PRThreadPriority newPri)
+{
+ PRIntn nativePri;
+
+ if (newPri < PR_PRIORITY_FIRST) {
+ newPri = PR_PRIORITY_FIRST;
+ } else if (newPri > PR_PRIORITY_LAST) {
+ newPri = PR_PRIORITY_LAST;
+ }
+ /* Solaris priorities are from 0 to 127 */
+ nativePri = newPri * 127 / PR_PRIORITY_LAST;
+ if(thr_setprio((thread_t)md_thread->handle, nativePri)) {
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("_PR_SetThreadPriority: can't set thread priority\n"));
+ }
+}
+
+void _MD_WAIT_CV(
+ struct _MDCVar *md_cv, struct _MDLock *md_lock, PRIntervalTime timeout)
+{
+ struct timespec tt;
+ PRUint32 msec;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT((!suspendAllOn) || (suspendAllThread != me));
+
+ if (PR_INTERVAL_NO_TIMEOUT == timeout) {
+ COND_WAIT(&md_cv->cv, &md_lock->lock);
+ } else {
+ msec = PR_IntervalToMilliseconds(timeout);
+
+ GETTIME(&tt);
+ tt.tv_sec += msec / PR_MSEC_PER_SEC;
+ tt.tv_nsec += (msec % PR_MSEC_PER_SEC) * PR_NSEC_PER_MSEC;
+ /* Check for nsec overflow - otherwise we'll get an EINVAL */
+ if (tt.tv_nsec >= PR_NSEC_PER_SEC) {
+ tt.tv_sec++;
+ tt.tv_nsec -= PR_NSEC_PER_SEC;
+ }
+ COND_TIMEDWAIT(&md_cv->cv, &md_lock->lock, &tt);
+ }
+}
+
+void _MD_lock(struct _MDLock *md_lock)
+{
+#ifdef DEBUG
+ /* This code was used for GC testing to make sure that we didn't attempt
+ * to grab any locks while threads are suspended.
+ */
+ PRLock *lock;
+
+ if ((suspendAllOn) && (suspendAllThread == _PR_MD_CURRENT_THREAD())) {
+ lock = ((PRLock *) ((char*) (md_lock) - offsetof(PRLock,ilock)));
+ PR_ASSERT(lock->owner == NULL);
+ return;
+ }
+#endif /* DEBUG */
+
+ mutex_lock(&md_lock->lock);
+}
+
+PRThread *_pr_current_thread_tls()
+{
+ PRThread *ret;
+
+ thr_getspecific(threadid_key, (void **)&ret);
+ return ret;
+}
+
+PRStatus
+_MD_wait(PRThread *thread, PRIntervalTime ticks)
+{
+ _MD_WAIT_SEM(&thread->md.waiter_sem);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WakeupWaiter(PRThread *thread)
+{
+ if (thread == NULL) {
+ return PR_SUCCESS;
+ }
+ _MD_POST_SEM(&thread->md.waiter_sem);
+ return PR_SUCCESS;
+}
+
+_PRCPU *_pr_current_cpu_tls()
+{
+ _PRCPU *ret;
+
+ thr_getspecific(cpuid_key, (void **)&ret);
+ return ret;
+}
+
+PRThread *_pr_last_thread_tls()
+{
+ PRThread *ret;
+
+ thr_getspecific(last_thread_key, (void **)&ret);
+ return ret;
+}
+
+_MDLock _pr_ioq_lock;
+
+void
+_MD_InitIO(void)
+{
+ _MD_NEW_LOCK(&_pr_ioq_lock);
+}
+
+PRStatus _MD_InitializeThread(PRThread *thread)
+{
+ if (!_PR_IS_NATIVE_THREAD(thread))
+ return PR_SUCCESS;
+ /* sol_curthread is an asm routine which grabs GR7; GR7 stores an internal
+ * thread structure ptr used by solaris. We'll use this ptr later
+ * with suspend/resume to find which threads are running on LWPs.
+ */
+ thread->md.threadID = sol_curthread();
+ /* prime the sp; substract 4 so we don't hit the assert that
+ * curr sp > base_stack
+ */
+ thread->md.sp = (uint_t) thread->stack->allocBase - sizeof(long);
+ thread->md.lwpid = _lwp_self();
+ thread->md.handle = THR_SELF();
+
+ /* all threads on Solaris are global threads from NSPR's perspective
+ * since all of them are mapped to Solaris threads.
+ */
+ thread->flags |= _PR_GLOBAL_SCOPE;
+
+ /* For primordial/attached thread, we don't create an underlying native thread.
+ * So, _MD_CREATE_THREAD() does not get called. We need to do initialization
+ * like allocating thread's synchronization variables and set the underlying
+ * native thread's priority.
+ */
+ if (thread->flags & (_PR_PRIMORDIAL | _PR_ATTACHED)) {
+ _MD_NEW_SEM(&thread->md.waiter_sem, 0);
+ _MD_SET_PRIORITY(&(thread->md), thread->priority);
+ }
+ return PR_SUCCESS;
+}
+
+/* Sleep for n milliseconds, n < 1000 */
+void solaris_msec_sleep(int n)
+{
+ struct timespec ts;
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000000*n;
+ if (syscall(SYS_nanosleep, &ts, 0, 0) < 0) {
+ PR_ASSERT(0);
+ }
+}
+
+#define VALID_SP(sp, bottom, top) \
+ (((uint_t)(sp)) > ((uint_t)(bottom)) && ((uint_t)(sp)) < ((uint_t)(top)))
+
+void solaris_record_regs(PRThread *t, prstatus_t *lwpstatus)
+{
+#ifdef sparc
+ long *regs = (long *)&t->md.context.uc_mcontext.gregs[0];
+
+ PR_ASSERT(t->flags & _PR_GCABLE_THREAD);
+ PR_ASSERT(t->md.threadID == lwpstatus->pr_reg[REG_G7]);
+
+ t->md.sp = lwpstatus->pr_reg[REG_SP];
+ PR_ASSERT(VALID_SP(t->md.sp, t->stack->stackBottom, t->stack->stackTop));
+
+ regs[0] = lwpstatus->pr_reg[R_G1];
+ regs[1] = lwpstatus->pr_reg[R_G2];
+ regs[2] = lwpstatus->pr_reg[R_G3];
+ regs[3] = lwpstatus->pr_reg[R_G4];
+ regs[4] = lwpstatus->pr_reg[R_O0];
+ regs[5] = lwpstatus->pr_reg[R_O1];
+ regs[6] = lwpstatus->pr_reg[R_O2];
+ regs[7] = lwpstatus->pr_reg[R_O3];
+ regs[8] = lwpstatus->pr_reg[R_O4];
+ regs[9] = lwpstatus->pr_reg[R_O5];
+ regs[10] = lwpstatus->pr_reg[R_O6];
+ regs[11] = lwpstatus->pr_reg[R_O7];
+#elif defined(i386)
+ /*
+ * To be implemented and tested
+ */
+ PR_ASSERT(0);
+ PR_ASSERT(t->md.threadID == lwpstatus->pr_reg[GS]);
+ t->md.sp = lwpstatus->pr_reg[UESP];
+#endif
+}
+
+void solaris_preempt_off()
+{
+ sigset_t set;
+
+ (void)sigfillset(&set);
+ syscall(SYS_sigprocmask, SIG_SETMASK, &set, &old_mask);
+}
+
+void solaris_preempt_on()
+{
+ syscall(SYS_sigprocmask, SIG_SETMASK, &old_mask, NULL);
+}
+
+int solaris_open_main_proc_fd()
+{
+ char buf[30];
+ int fd;
+
+ /* Not locked, so must be created while threads coming up */
+ PR_snprintf(buf, sizeof(buf), "/proc/%ld", getpid());
+ if ( (fd = syscall(SYS_open, buf, O_RDONLY)) < 0) {
+ return -1;
+ }
+ return fd;
+}
+
+/* Return a file descriptor for the /proc entry corresponding to the
+ * given lwp.
+ */
+int solaris_open_lwp(lwpid_t id, int lwp_main_proc_fd)
+{
+ int result;
+
+ if ( (result = syscall(SYS_ioctl, lwp_main_proc_fd, PIOCOPENLWP, &id)) <0)
+ return -1; /* exited??? */
+
+ return result;
+}
+void _MD_Begin_SuspendAll()
+{
+ solaris_preempt_off();
+
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin_SuspendAll\n"));
+ /* run at highest prio so I cannot be preempted */
+ thr_getprio(thr_self(), &gcprio);
+ thr_setprio(thr_self(), 0x7fffffff);
+ suspendAllOn = PR_TRUE;
+ suspendAllThread = _PR_MD_CURRENT_THREAD();
+}
+
+void _MD_End_SuspendAll()
+{
+}
+
+void _MD_End_ResumeAll()
+{
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End_ResumeAll\n"));
+ thr_setprio(thr_self(), gcprio);
+ solaris_preempt_on();
+ suspendAllThread = NULL;
+ suspendAllOn = PR_FALSE;
+}
+
+void _MD_Suspend(PRThread *thr)
+{
+ int lwp_fd, result;
+ prstatus_t lwpstatus;
+ int lwp_main_proc_fd = 0;
+
+ if (!(thr->flags & _PR_GCABLE_THREAD) || !suspendAllOn){
+ /*XXX When the suspendAllOn is set, we will be trying to do lwp_suspend
+ * during that time we can't call any thread lib or libc calls. Hence
+ * make sure that no suspension is requested for Non gcable thread
+ * during suspendAllOn */
+ PR_ASSERT(!suspendAllOn);
+ thr_suspend(thr->md.handle);
+ return;
+ }
+
+ /* XXX Primordial thread can't be bound to an lwp, hence there is no
+ * way we can assume that we can get the lwp status for primordial
+ * thread reliably. Hence we skip this for primordial thread, hoping
+ * that the SP is saved during lock and cond. wait.
+ * XXX - Again this is concern only for java interpreter, not for the
+ * server, 'cause primordial thread in the server does not do java work
+ */
+ if (thr->flags & _PR_PRIMORDIAL)
+ return;
+
+ /* XXX Important Note: If the start function of a thread is not called,
+ * lwpid is -1. Then, skip this thread. This thread will get caught
+ * in PR_NativeRunThread before calling the start function, because
+ * we hold the pr_activeLock during suspend/resume */
+
+ /* if the thread is not started yet then don't do anything */
+ if (!suspendAllOn || thr->md.lwpid == -1)
+ return;
+
+ if (_lwp_suspend(thr->md.lwpid) < 0) {
+ PR_ASSERT(0);
+ return;
+ }
+
+ if ( (lwp_main_proc_fd = solaris_open_main_proc_fd()) < 0) {
+ PR_ASSERT(0);
+ return; /* XXXMB ARGH, we're hosed! */
+ }
+
+ if ( (lwp_fd = solaris_open_lwp(thr->md.lwpid, lwp_main_proc_fd)) < 0) {
+ PR_ASSERT(0);
+ close(lwp_main_proc_fd);
+ return;
+ }
+ if ( (result = syscall(SYS_ioctl, lwp_fd, PIOCSTATUS, &lwpstatus)) < 0) {
+ /* Hopefully the thread just died... */
+ close(lwp_fd);
+ close(lwp_main_proc_fd);
+ return;
+ }
+ while ( !(lwpstatus.pr_flags & PR_STOPPED) ) {
+ if ( (result = syscall(SYS_ioctl, lwp_fd, PIOCSTATUS, &lwpstatus)) < 0) {
+ PR_ASSERT(0); /* ARGH SOMETHING WRONG! */
+ break;
+ }
+ solaris_msec_sleep(1);
+ }
+ solaris_record_regs(thr, &lwpstatus);
+ close(lwp_fd);
+ close(lwp_main_proc_fd);
+}
+
+#ifdef OLD_CODE
+
+void _MD_SuspendAll()
+{
+ /* On solaris there are threads, and there are LWPs.
+ * Calling _PR_DoSingleThread would freeze all of the threads bound to LWPs
+ * but not necessarily stop all LWPs (for example if someone did
+ * an attachthread of a thread which was not bound to an LWP).
+ * So now go through all the LWPs for this process and freeze them.
+ *
+ * Note that if any thread which is capable of having the GC run on it must
+ * had better be a LWP with a single bound thread on it. Otherwise, this
+ * might not stop that thread from being run.
+ */
+ PRThread *current = _PR_MD_CURRENT_THREAD();
+ prstatus_t status, lwpstatus;
+ int result, index, lwp_fd;
+ lwpid_t me = _lwp_self();
+ int err;
+ int lwp_main_proc_fd;
+
+ solaris_preempt_off();
+
+ /* run at highest prio so I cannot be preempted */
+ thr_getprio(thr_self(), &gcprio);
+ thr_setprio(thr_self(), 0x7fffffff);
+
+ current->md.sp = (uint_t)&me; /* set my own stack pointer */
+
+ if ( (lwp_main_proc_fd = solaris_open_main_proc_fd()) < 0) {
+ PR_ASSERT(0);
+ solaris_preempt_on();
+ return; /* XXXMB ARGH, we're hosed! */
+ }
+
+ if ( (result = syscall(SYS_ioctl, lwp_main_proc_fd, PIOCSTATUS, &status)) < 0) {
+ err = errno;
+ PR_ASSERT(0);
+ goto failure; /* XXXMB ARGH, we're hosed! */
+ }
+
+ num_lwps = status.pr_nlwp;
+
+ if ( (all_lwps = (lwpid_t *)PR_MALLOC((num_lwps+1) * sizeof(lwpid_t)))==NULL) {
+ PR_ASSERT(0);
+ goto failure; /* XXXMB ARGH, we're hosed! */
+ }
+
+ if ( (result = syscall(SYS_ioctl, lwp_main_proc_fd, PIOCLWPIDS, all_lwps)) < 0) {
+ PR_ASSERT(0);
+ PR_DELETE(all_lwps);
+ goto failure; /* XXXMB ARGH, we're hosed! */
+ }
+
+ for (index=0; index< num_lwps; index++) {
+ if (all_lwps[index] != me) {
+ if (_lwp_suspend(all_lwps[index]) < 0) {
+ /* could happen if lwp exited */
+ all_lwps[index] = me; /* dummy it up */
+ }
+ }
+ }
+
+ /* Turns out that lwp_suspend is not a blocking call.
+ * Go through the list and make sure they are all stopped.
+ */
+ for (index=0; index< num_lwps; index++) {
+ if (all_lwps[index] != me) {
+ if ( (lwp_fd = solaris_open_lwp(all_lwps[index], lwp_main_proc_fd)) < 0) {
+ PR_ASSERT(0);
+ PR_DELETE(all_lwps);
+ all_lwps = NULL;
+ goto failure; /* XXXMB ARGH, we're hosed! */
+ }
+
+ if ( (result = syscall(SYS_ioctl, lwp_fd, PIOCSTATUS, &lwpstatus)) < 0) {
+ /* Hopefully the thread just died... */
+ close(lwp_fd);
+ continue;
+ }
+ while ( !(lwpstatus.pr_flags & PR_STOPPED) ) {
+ if ( (result = syscall(SYS_ioctl, lwp_fd, PIOCSTATUS, &lwpstatus)) < 0) {
+ PR_ASSERT(0); /* ARGH SOMETHING WRONG! */
+ break;
+ }
+ solaris_msec_sleep(1);
+ }
+ solaris_record_regs(&lwpstatus);
+ close(lwp_fd);
+ }
+ }
+
+ close(lwp_main_proc_fd);
+
+ return;
+failure:
+ solaris_preempt_on();
+ thr_setprio(thr_self(), gcprio);
+ close(lwp_main_proc_fd);
+ return;
+}
+
+void _MD_ResumeAll()
+{
+ int i;
+ lwpid_t me = _lwp_self();
+
+ for (i=0; i < num_lwps; i++) {
+ if (all_lwps[i] == me)
+ continue;
+ if ( _lwp_continue(all_lwps[i]) < 0) {
+ PR_ASSERT(0); /* ARGH, we are hosed! */
+ }
+ }
+
+ /* restore priority and sigmask */
+ thr_setprio(thr_self(), gcprio);
+ solaris_preempt_on();
+ PR_DELETE(all_lwps);
+ all_lwps = NULL;
+}
+#endif /* OLD_CODE */
+
+#ifdef USE_SETJMP
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+}
+#else
+PRWord *_MD_HomeGCRegisters(PRThread *t, PRIntn isCurrent, PRIntn *np)
+{
+ if (isCurrent) {
+ (void) getcontext(CONTEXT(t));
+ }
+ *np = NGREG;
+ return (PRWord*) &t->md.context.uc_mcontext.gregs[0];
+}
+#endif /* USE_SETJMP */
+
+#else /* _PR_GLOBAL_THREADS_ONLY */
+
+#if defined(_PR_LOCAL_THREADS_ONLY)
+
+void _MD_EarlyInit(void)
+{
+}
+
+void _MD_SolarisInit()
+{
+ _PR_UnixInit();
+}
+
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRThreadPriority newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ PR_ASSERT((thread == NULL) || (!(thread->flags & _PR_GLOBAL_SCOPE)));
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for Solaris */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for Solaris");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for Solaris");
+ return(PR_FAILURE);
+}
+
+#ifdef USE_SETJMP
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+}
+#else
+PRWord *_MD_HomeGCRegisters(PRThread *t, PRIntn isCurrent, PRIntn *np)
+{
+ if (isCurrent) {
+ (void) getcontext(CONTEXT(t));
+ }
+ *np = NGREG;
+ return (PRWord*) &t->md.context.uc_mcontext.gregs[0];
+}
+#endif /* USE_SETJMP */
+
+#endif /* _PR_LOCAL_THREADS_ONLY */
+
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+
+#ifndef _PR_PTHREADS
+#if defined(i386) && defined(SOLARIS2_4)
+/*
+ * Because clock_gettime() on Solaris/x86 2.4 always generates a
+ * segmentation fault, we use an emulated version _pr_solx86_clock_gettime(),
+ * which is implemented using gettimeofday().
+ */
+
+int
+_pr_solx86_clock_gettime(clockid_t clock_id, struct timespec *tp)
+{
+ struct timeval tv;
+
+ if (clock_id != CLOCK_REALTIME) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ gettimeofday(&tv);
+ tp->tv_sec = tv.tv_sec;
+ tp->tv_nsec = tv.tv_usec * 1000;
+ return 0;
+}
+#endif /* i386 && SOLARIS2_4 */
+#endif /* _PR_PTHREADS */
diff --git a/pr/src/md/unix/sony.c b/pr/src/md/unix/sony.c
new file mode 100644
index 00000000..d3ce90c4
--- /dev/null
+++ b/pr/src/md/unix/sony.c
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+#include <signal.h>
+
+void _MD_EarlyInit(void)
+{
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+#ifndef _PR_PTHREADS
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t), 1);
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+#else
+ *np = 0;
+ return NULL;
+#endif
+}
+
+#ifndef _PR_PTHREADS
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for Sony */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for SONY.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for SONY.");
+ return PR_FAILURE;
+}
+#endif /* ! _PR_PTHREADS */
diff --git a/pr/src/md/unix/sunos4.c b/pr/src/md/unix/sunos4.c
new file mode 100644
index 00000000..0b4b9b4f
--- /dev/null
+++ b/pr/src/md/unix/sunos4.c
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include <setjmp.h>
+#include "primpl.h"
+
+void _MD_EarlyInit(void)
+{
+}
+
+PRStatus _MD_CREATE_THREAD(PRThread *thread,
+ void (*start)(void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for SunOS 4.1.3.");
+ return PR_FAILURE;
+}
+
+void _MD_SET_PRIORITY(_MDThread *md_thread, PRUintn newPri)
+{
+ PR_NOT_REACHED("_MD_SET_PRIORITY should not be called for user-level threads.");
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+PRStatus _MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for SunOS 4.1.3.");
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+}
diff --git a/pr/src/md/unix/unix.c b/pr/src/md/unix/unix.c
new file mode 100644
index 00000000..4148fb4d
--- /dev/null
+++ b/pr/src/md/unix/unix.c
@@ -0,0 +1,3210 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+#include <signal.h>
+#include <unistd.h>
+#include <memory.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+/* To get FIONREAD */
+#if defined(NCR) || defined(UNIXWARE) || defined(NEC) || defined(SNI) \
+ || defined(SONY)
+#include <sys/filio.h>
+#endif
+
+/*
+ * Make sure _PRSockLen_t is 32-bit, because we will cast a PRUint32* or
+ * PRInt32* pointer to a _PRSockLen_t* pointer.
+ */
+#if defined(IRIX) || defined(HPUX) || defined(OSF1) || defined(SOLARIS) \
+ || defined(AIX4_1) || defined(LINUX) || defined(SONY) \
+ || defined(BSDI) || defined(SCO) || defined(NEC) || defined(SNI) \
+ || defined(SUNOS4)
+#define _PRSockLen_t int
+#elif (defined(AIX) && !defined(AIX4_1)) || defined(FREEBSD) \
+ || defined(UNIXWARE)
+#define _PRSockLen_t size_t
+#else
+#error "Cannot determine architecture"
+#endif
+
+/*
+** Global lock variable used to bracket calls into rusty libraries that
+** aren't thread safe (like libc, libX, etc).
+*/
+static PRLock *_pr_rename_lock = NULL;
+static PRMonitor *_pr_Xfe_mon = NULL;
+
+/*
+ * Variables used by the GC code, initialized in _MD_InitSegs().
+ * _pr_zero_fd should be a static variable. Unfortunately, there is
+ * still some Unix-specific code left in function PR_GrowSegment()
+ * in file memory/prseg.c that references it, so it needs
+ * to be a global variable for now.
+ */
+PRInt32 _pr_zero_fd = -1;
+static PRLock *_pr_md_lock = NULL;
+
+sigset_t timer_set;
+
+#if !defined(_PR_PTHREADS)
+
+static sigset_t empty_set;
+
+#ifdef SOLARIS
+#include <sys/file.h>
+#include <sys/filio.h>
+#endif
+
+#ifndef PIPE_BUF
+#define PIPE_BUF 512
+#endif
+
+#ifndef PROT_NONE
+#define PROT_NONE 0
+#endif
+
+/*
+ * _nspr_noclock - if set clock interrupts are disabled
+ */
+int _nspr_noclock = 0;
+
+#ifdef IRIX
+extern PRInt32 _nspr_terminate_on_error;
+#endif
+
+/*
+ * There is an assertion in this code that NSPR's definition of PRIOVec
+ * is bit compatible with UNIX' definition of a struct iovec. This is
+ * applicable to the 'writev()' operations where the types are casually
+ * cast to avoid warnings.
+ */
+
+int _pr_md_pipefd[2] = { -1, -1 };
+static char _pr_md_pipebuf[PIPE_BUF];
+
+_PRInterruptTable _pr_interruptTable[] = {
+ {
+ "clock", _PR_MISSED_CLOCK, _PR_ClockInterrupt, },
+ {
+ 0 }
+};
+
+PR_IMPLEMENT(void) _MD_unix_init_running_cpu(_PRCPU *cpu)
+{
+ PR_INIT_CLIST(&(cpu->md.md_unix.ioQ));
+ cpu->md.md_unix.ioq_max_osfd = -1;
+ cpu->md.md_unix.ioq_timeout = PR_INTERVAL_NO_TIMEOUT;
+}
+
+PRStatus _MD_open_dir(_MDDir *d, const char *name)
+{
+int err;
+
+ d->d = opendir(name);
+ if (!d->d) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_OPENDIR_ERROR(err);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+PRInt32 _MD_close_dir(_MDDir *d)
+{
+int rv = 0, err;
+
+ if (d->d) {
+ rv = closedir(d->d);
+ if (rv == -1) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_CLOSEDIR_ERROR(err);
+ }
+ }
+ return rv;
+}
+
+char * _MD_read_dir(_MDDir *d, PRIntn flags)
+{
+struct dirent *de;
+int err;
+
+ for (;;) {
+ /*
+ * XXX: readdir() is not MT-safe. There is an MT-safe version
+ * readdir_r() on some systems.
+ */
+ de = readdir(d->d);
+ if (!de) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_READDIR_ERROR(err);
+ return 0;
+ }
+ if ((flags & PR_SKIP_DOT) &&
+ (de->d_name[0] == '.') && (de->d_name[1] == 0))
+ continue;
+ if ((flags & PR_SKIP_DOT_DOT) &&
+ (de->d_name[0] == '.') && (de->d_name[1] == '.') &&
+ (de->d_name[2] == 0))
+ continue;
+ if ((flags & PR_SKIP_HIDDEN) && (de->d_name[0] == '.'))
+ continue;
+ break;
+ }
+ return de->d_name;
+}
+
+PRInt32 _MD_delete(const char *name)
+{
+PRInt32 rv, err;
+#ifdef UNIXWARE
+ sigset_t set, oset;
+#endif
+
+#ifdef UNIXWARE
+ sigfillset(&set);
+ sigprocmask(SIG_SETMASK, &set, &oset);
+#endif
+ rv = unlink(name);
+#ifdef UNIXWARE
+ sigprocmask(SIG_SETMASK, &oset, NULL);
+#endif
+ if (rv == -1) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_UNLINK_ERROR(err);
+ }
+ return(rv);
+}
+
+PRInt32 _MD_getfileinfo(const char *fn, PRFileInfo *info)
+{
+ struct stat sb;
+ PRInt64 s, s2us;
+ PRInt32 rv, err;
+
+ rv = stat(fn, &sb);
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_STAT_ERROR(err);
+ } else if (info) {
+ if (S_IFREG & sb.st_mode)
+ info->type = PR_FILE_FILE ;
+ else if (S_IFDIR & sb.st_mode)
+ info->type = PR_FILE_DIRECTORY;
+ else
+ info->type = PR_FILE_OTHER;
+ info->size = sb.st_size;
+ LL_I2L(s, sb.st_mtime);
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_MUL(s, s, s2us);
+ info->modifyTime = s;
+ LL_I2L(s, sb.st_ctime);
+ LL_MUL(s, s, s2us);
+ info->creationTime = s;
+ }
+ return rv;
+}
+
+PRInt32 _MD_getfileinfo64(const char *fn, PRFileInfo64 *info)
+{
+ PRFileInfo info32;
+ PRInt32 rv = _MD_getfileinfo(fn, &info32);
+ if (rv >= 0)
+ {
+ info->type = info32.type;
+ LL_I2L(info->size, info32.size);
+ info->modifyTime = info32.modifyTime;
+ info->creationTime = info32.creationTime;
+ }
+ return rv;
+}
+
+PRInt32 _MD_getopenfileinfo(const PRFileDesc *fd, PRFileInfo *info)
+{
+ struct stat sb;
+ PRInt64 s, s2us;
+ PRInt32 rv, err;
+
+ rv = fstat(fd->secret->md.osfd, &sb);
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_FSTAT_ERROR(err);
+ } else if (info) {
+ if (info) {
+ if (S_IFREG & sb.st_mode)
+ info->type = PR_FILE_FILE ;
+ else if (S_IFDIR & sb.st_mode)
+ info->type = PR_FILE_DIRECTORY;
+ else
+ info->type = PR_FILE_OTHER;
+ info->size = sb.st_size;
+ LL_I2L(s, sb.st_mtime);
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_MUL(s, s, s2us);
+ info->modifyTime = s;
+ LL_I2L(s, sb.st_ctime);
+ LL_MUL(s, s, s2us);
+ info->creationTime = s;
+ }
+ }
+ return rv;
+}
+
+PRInt32 _MD_getopenfileinfo64(const PRFileDesc *fd, PRFileInfo64 *info)
+{
+ PRFileInfo info32;
+ PRInt32 rv = _MD_getopenfileinfo(fd, &info32);
+ if (rv >= 0)
+ {
+ info->type = info32.type;
+ LL_I2L(info->size, info32.size);
+ info->modifyTime = info32.modifyTime;
+ info->creationTime = info32.creationTime;
+ }
+ return rv;
+}
+
+PRInt32 _MD_rename(const char *from, const char *to)
+{
+ PRInt32 rv = -1, err;
+
+ /*
+ ** This is trying to enforce the semantics of WINDOZE' rename
+ ** operation. That means one is not allowed to rename over top
+ ** of an existing file. Holding a lock across these two function
+ ** and the open function is known to be a bad idea, but ....
+ */
+ if (NULL != _pr_rename_lock)
+ PR_Lock(_pr_rename_lock);
+ if (0 == access(to, F_OK))
+ PR_SetError(PR_FILE_EXISTS_ERROR, 0);
+ else
+ {
+ rv = rename(from, to);
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_RENAME_ERROR(err);
+ }
+ }
+ if (NULL != _pr_rename_lock)
+ PR_Unlock(_pr_rename_lock);
+ return rv;
+}
+
+PRInt32 _MD_access(const char *name, PRIntn how)
+{
+PRInt32 rv, err;
+int amode;
+
+ switch (how) {
+ case PR_ACCESS_WRITE_OK:
+ amode = W_OK;
+ break;
+ case PR_ACCESS_READ_OK:
+ amode = R_OK;
+ break;
+ case PR_ACCESS_EXISTS:
+ amode = F_OK;
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ rv = access(name, amode);
+
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_ACCESS_ERROR(err);
+ }
+
+done:
+ return(rv);
+}
+
+PRInt32 _MD_mkdir(const char *name, PRIntn mode)
+{
+int rv, err;
+
+ /*
+ ** This lock is used to enforce rename semantics as described
+ ** in PR_Rename. Look there for more fun details.
+ */
+ if (NULL !=_pr_rename_lock)
+ PR_Lock(_pr_rename_lock);
+ rv = mkdir(name, mode);
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_MKDIR_ERROR(err);
+ }
+ if (NULL !=_pr_rename_lock)
+ PR_Unlock(_pr_rename_lock);
+ return rv;
+}
+
+PRInt32 _MD_rmdir(const char *name)
+{
+int rv, err;
+
+ rv = rmdir(name);
+ if (rv == -1) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_RMDIR_ERROR(err);
+ }
+ return rv;
+}
+
+PRInt32 _MD_read(PRFileDesc *fd, void *buf, PRInt32 amount)
+{
+PRThread *me = _PR_MD_CURRENT_THREAD();
+PRInt32 rv, err;
+fd_set rd;
+PRInt32 osfd = fd->secret->md.osfd;
+
+ FD_ZERO(&rd);
+ FD_SET(osfd, &rd);
+ while ((rv = read(osfd,buf,amount)) == -1) {
+ err = _MD_ERRNO();
+ if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_WaitForFD(osfd, PR_POLL_READ, PR_INTERVAL_NO_TIMEOUT);
+ } else {
+ while ((rv = _MD_SELECT(osfd + 1, &rd, NULL, NULL, NULL))
+ == -1 && (err = _MD_ERRNO()) == EINTR) {
+ /* retry _MD_SELECT() if it is interrupted */
+ }
+ if (rv == -1) {
+ break;
+ }
+ }
+ if (_PR_PENDING_INTERRUPT(me))
+ break;
+ } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))){
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ _PR_MD_MAP_READ_ERROR(err);
+ }
+ }
+ return(rv);
+}
+
+PRInt32 _MD_write(PRFileDesc *fd, const void *buf, PRInt32 amount)
+{
+PRThread *me = _PR_MD_CURRENT_THREAD();
+PRInt32 rv, err;
+fd_set wd;
+PRInt32 osfd = fd->secret->md.osfd;
+
+ FD_ZERO(&wd);
+ FD_SET(osfd, &wd);
+ while ((rv = write(osfd,buf,amount)) == -1) {
+ err = _MD_ERRNO();
+ if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ _PR_WaitForFD(osfd, PR_POLL_WRITE, PR_INTERVAL_NO_TIMEOUT);
+ } else {
+ while ((rv = _MD_SELECT(osfd + 1, NULL, &wd, NULL, NULL))
+ == -1 && (err = _MD_ERRNO()) == EINTR) {
+ /* retry _MD_SELECT() if it is interrupted */
+ }
+ if (rv == -1) {
+ break;
+ }
+ }
+ if (_PR_PENDING_INTERRUPT(me))
+ break;
+ } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))){
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ _PR_MD_MAP_WRITE_ERROR(err);
+ }
+ }
+ return(rv);
+}
+
+PRInt32 _MD_lseek(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence)
+{
+PRInt32 rv, where, err;
+
+ switch (whence) {
+ case PR_SEEK_SET:
+ where = SEEK_SET;
+ break;
+ case PR_SEEK_CUR:
+ where = SEEK_CUR;
+ break;
+ case PR_SEEK_END:
+ where = SEEK_END;
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ rv = lseek(fd->secret->md.osfd,offset,where);
+ if (rv == -1) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_LSEEK_ERROR(err);
+ }
+done:
+ return(rv);
+}
+
+PRInt64 _MD_lseek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence)
+{
+ PRInt32 off, rv;
+ PRInt64 on, result = LL_MININT;
+ LL_L2I(off, offset);
+ LL_I2L(on, off);
+ if (LL_EQ(offset, on))
+ {
+ rv = _MD_lseek(fd, off, whence);
+ if (rv >= 0) LL_I2L(result, rv);
+ }
+ else PR_SetError(PR_FILE_TOO_BIG_ERROR, 0); /* overflow */
+ return result;
+} /* _MD_lseek64 */
+
+PRInt32 _MD_fsync(PRFileDesc *fd)
+{
+PRInt32 rv, err;
+
+ rv = fsync(fd->secret->md.osfd);
+ if (rv == -1) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_FSYNC_ERROR(err);
+ }
+ return(rv);
+}
+
+PRInt32 _MD_close(PRInt32 osfd)
+{
+PRInt32 rv, err;
+
+ rv = close(osfd);
+ if (rv == -1) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_CLOSE_ERROR(err);
+ }
+ return(rv);
+}
+
+PRInt32 _MD_socket(PRInt32 domain, PRInt32 type, PRInt32 proto)
+{
+ PRInt32 osfd, err;
+
+ osfd = socket(domain, type, proto);
+
+ if (osfd == -1) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_SOCKET_ERROR(err);
+ return(osfd);
+ }
+
+ return(osfd);
+}
+
+PRInt32 _MD_socketavailable(PRFileDesc *fd)
+{
+ PRInt32 result;
+
+ if (ioctl(fd->secret->md.osfd, FIONREAD, &result) < 0) {
+ _PR_MD_MAP_SOCKETAVAILABLE_ERROR(_MD_ERRNO());
+ return -1;
+ }
+ return result;
+}
+
+PRInt64 _MD_socketavailable64(PRFileDesc *fd)
+{
+ PRInt64 result;
+ LL_I2L(result, _MD_socketavailable(fd));
+ return result;
+} /* _MD_socketavailable64 */
+
+#define READ_FD 1
+#define WRITE_FD 2
+
+/*
+ * wait for socket i/o, periodically checking for interrupt
+ */
+
+static PRInt32 socket_io_wait(PRInt32 osfd, PRInt32 fd_type,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv = -1;
+ struct timeval tv, *tvp;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRIntervalTime epoch, now, elapsed, remaining;
+ PRInt32 syserror;
+ fd_set rd_wr;
+
+ switch (timeout) {
+ case PR_INTERVAL_NO_WAIT:
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ break;
+ case PR_INTERVAL_NO_TIMEOUT:
+ /*
+ * This is a special case of the 'default' case below.
+ * Please see the comments there.
+ */
+ tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ FD_ZERO(&rd_wr);
+ do {
+ FD_SET(osfd, &rd_wr);
+ if (fd_type == READ_FD)
+ rv = _MD_SELECT(osfd + 1, &rd_wr, NULL, NULL, tvp);
+ else
+ rv = _MD_SELECT(osfd + 1, NULL, &rd_wr, NULL, tvp);
+ if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) {
+ if (syserror == EBADF) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, EBADF);
+ } else {
+ PR_SetError(PR_UNKNOWN_ERROR, syserror);
+ }
+ break;
+ }
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ } while (rv == 0 || (rv == -1 && syserror == EINTR));
+ break;
+ default:
+ now = epoch = PR_IntervalNow();
+ remaining = timeout;
+ tvp = &tv;
+ FD_ZERO(&rd_wr);
+ do {
+ /*
+ * We block in _MD_SELECT for at most
+ * _PR_INTERRUPT_CHECK_INTERVAL_SECS seconds,
+ * so that there is an upper limit on the delay
+ * before the interrupt bit is checked.
+ */
+ tv.tv_sec = PR_IntervalToSeconds(remaining);
+ if (tv.tv_sec > _PR_INTERRUPT_CHECK_INTERVAL_SECS) {
+ tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS;
+ tv.tv_usec = 0;
+ } else {
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ remaining -
+ PR_SecondsToInterval(tv.tv_sec));
+ }
+ FD_SET(osfd, &rd_wr);
+ if (fd_type == READ_FD)
+ rv = _MD_SELECT(osfd + 1, &rd_wr, NULL, NULL, tvp);
+ else
+ rv = _MD_SELECT(osfd + 1, NULL, &rd_wr, NULL, tvp);
+ /*
+ * we don't consider EINTR a real error
+ */
+ if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) {
+ if (syserror == EBADF) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, EBADF);
+ } else {
+ PR_SetError(PR_UNKNOWN_ERROR, syserror);
+ }
+ break;
+ }
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ /*
+ * We loop again if _MD_SELECT timed out or got interrupted
+ * by a signal, and the timeout deadline has not passed yet.
+ */
+ if (rv == 0 || (rv == -1 && syserror == EINTR)) {
+ /*
+ * If _MD_SELECT timed out, we know how much time
+ * we spent in blocking, so we can avoid a
+ * PR_IntervalNow() call.
+ */
+ if (rv == 0) {
+ now += PR_SecondsToInterval(tv.tv_sec)
+ + PR_MicrosecondsToInterval(tv.tv_usec);
+ } else {
+ now = PR_IntervalNow();
+ }
+ elapsed = (PRIntervalTime) (now - epoch);
+ if (elapsed >= timeout) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ break;
+ } else {
+ remaining = timeout - elapsed;
+ }
+ }
+ } while (rv == 0 || (rv == -1 && syserror == EINTR));
+ break;
+ }
+ return(rv);
+}
+
+PRInt32 _MD_recv(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRInt32 flags, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+/*
+ * Many OS's (Solaris, Unixware) have a broken recv which won't read
+ * from socketpairs. As long as we don't use flags on socketpairs, this
+ * is a decent fix. - mikep
+ */
+#if defined(UNIXWARE) || defined(SOLARIS) || defined(NCR)
+ while ((rv = read(osfd,buf,amount)) == -1) {
+/*
+ while ((rv = recv(osfd,buf,amount,flags)) == -1) {
+*/
+#else
+ while ((rv = recv(osfd,buf,amount,flags)) == -1) {
+#endif
+ err = _MD_ERRNO();
+ if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ if (_PR_WaitForFD(osfd, PR_POLL_READ, timeout) == 0) {
+ rv = -1;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ } else
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ goto done;
+ } else if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ } else {
+ if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0)
+ goto done;
+ }
+ } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))){
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_RECV_ERROR(err);
+ }
+done:
+ return(rv);
+}
+
+PRInt32 _MD_recvfrom(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRNetAddr *addr, PRUint32 *addrlen,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ while ((*addrlen = PR_NETADDR_SIZE(addr)),
+ ((rv = recvfrom(osfd, buf, amount, flags,
+ (struct sockaddr *) addr, (_PRSockLen_t *)addrlen)) == -1)) {
+ err = _MD_ERRNO();
+ if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ if (_PR_WaitForFD(osfd, PR_POLL_READ, timeout) == 0) {
+ rv = -1;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ } else
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ goto done;
+ } else if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ } else {
+ if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0)
+ goto done;
+ }
+ } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))){
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_RECVFROM_ERROR(err);
+ }
+done:
+#ifdef AIX
+ if (rv != -1) {
+ /* mask off the first byte of struct sockaddr (the length field) */
+ if (addr) {
+ addr->inet.family &= 0x00ff;
+ }
+ }
+#endif
+ return(rv);
+}
+
+PRInt32 _MD_send(PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRInt32 flags, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ while ((rv = send(osfd,buf,amount,flags)) == -1) {
+ err = _MD_ERRNO();
+ if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ if (_PR_WaitForFD(osfd, PR_POLL_WRITE, timeout) == 0) {
+ rv = -1;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ } else
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ goto done;
+ } else if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ } else {
+ if ((rv = socket_io_wait(osfd, WRITE_FD, timeout))< 0)
+ goto done;
+ }
+ } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))){
+ continue;
+ } else {
+ break;
+ }
+ }
+ /*
+ * optimization; if bytes sent is less than "amount" call
+ * select before returning. This is because it is likely that
+ * the next send() call will return EWOULDBLOCK.
+ */
+ if ((!fd->secret->nonblocking) && (rv > 0) && (rv < amount)
+ && (timeout != PR_INTERVAL_NO_WAIT)) {
+ if (_PR_IS_NATIVE_THREAD(me)) {
+ if (socket_io_wait(osfd, WRITE_FD, timeout)< 0)
+ goto done;
+ } else {
+ if (_PR_WaitForFD(osfd, PR_POLL_WRITE, timeout) == 0) {
+ rv = -1;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ } else
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ goto done;
+ }
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_SEND_ERROR(err);
+ }
+done:
+ return(rv);
+}
+
+PRInt32 _MD_sendto(
+ PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ while ((rv = sendto(osfd, buf, amount, flags,
+ (struct sockaddr *) addr, addrlen)) == -1) {
+ err = _MD_ERRNO();
+ if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ if (_PR_WaitForFD(osfd, PR_POLL_WRITE, timeout) == 0) {
+ rv = -1;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ } else
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ goto done;
+ } else if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ } else {
+ if ((rv = socket_io_wait(osfd, WRITE_FD, timeout))< 0)
+ goto done;
+ }
+ } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))){
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_SENDTO_ERROR(err);
+ }
+done:
+ return(rv);
+}
+
+PRInt32 _MD_writev(PRFileDesc *fd, PRIOVec *iov,
+ PRInt32 iov_size, PRIntervalTime timeout)
+{
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 index, amount = 0;
+ PRInt32 osfd = fd->secret->md.osfd;
+
+ /*
+ * Calculate the total number of bytes to be sent; needed for
+ * optimization later.
+ * We could avoid this if this number was passed in; but it is
+ * probably not a big deal because iov_size is usually small (less than
+ * 3)
+ */
+ if (!fd->secret->nonblocking) {
+ for (index=0; index<iov_size; index++) {
+ amount += iov[index].iov_len;
+ }
+ }
+
+ while ((rv = writev(osfd, (const struct iovec*)iov, iov_size)) == -1) {
+ err = _MD_ERRNO();
+ if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ if (_PR_WaitForFD(osfd, PR_POLL_WRITE, timeout) == 0) {
+ rv = -1;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ } else
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ goto done;
+ } else if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ } else {
+ if ((rv = socket_io_wait(osfd, WRITE_FD, timeout))<0)
+ goto done;
+ }
+ } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))){
+ continue;
+ } else {
+ break;
+ }
+ }
+ /*
+ * optimization; if bytes sent is less than "amount" call
+ * select before returning. This is because it is likely that
+ * the next writev() call will return EWOULDBLOCK.
+ */
+ if ((!fd->secret->nonblocking) && (rv > 0) && (rv < amount)
+ && (timeout != PR_INTERVAL_NO_WAIT)) {
+ if (_PR_IS_NATIVE_THREAD(me)) {
+ if (socket_io_wait(osfd, WRITE_FD, timeout) < 0)
+ goto done;
+ } else {
+ if (_PR_WaitForFD(osfd, PR_POLL_WRITE, timeout) == 0) {
+ rv = -1;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ } else
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ goto done;
+ }
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_WRITEV_ERROR(err);
+ }
+done:
+ return(rv);
+}
+
+PRInt32 _MD_accept(PRFileDesc *fd, PRNetAddr *addr,
+ PRUint32 *addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ while ((rv = accept(osfd, (struct sockaddr *) addr,
+ (_PRSockLen_t *)addrlen)) == -1) {
+ err = _MD_ERRNO();
+ if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ if (_PR_WaitForFD(osfd, PR_POLL_READ, timeout) == 0) {
+ rv = -1;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ } else
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ goto done;
+ } else if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ } else {
+ if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0)
+ goto done;
+ }
+ } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))){
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ }
+done:
+#ifdef AIX
+ if (rv != -1) {
+ /* mask off the first byte of struct sockaddr (the length field) */
+ if (addr) {
+ addr->inet.family &= 0x00ff;
+ }
+ }
+#endif
+ return(rv);
+}
+
+extern int _connect (int s, const struct sockaddr *name, int namelen);
+PRInt32 _MD_connect(
+ PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
+{
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 osfd = fd->secret->md.osfd;
+#ifdef IRIX
+extern PRInt32 _MD_irix_connect(
+ PRInt32 osfd, const PRNetAddr *addr, PRInt32 addrlen, PRIntervalTime timeout);
+#endif
+
+ /*
+ * We initiate the connection setup by making a nonblocking connect()
+ * call. If the connect() call fails, there are two cases we handle
+ * specially:
+ * 1. The connect() call was interrupted by a signal. In this case
+ * we simply retry connect().
+ * 2. The NSPR socket is nonblocking and connect() fails with
+ * EINPROGRESS. We first wait until the socket becomes writable.
+ * Then we try to find out whether the connection setup succeeded
+ * or failed.
+ */
+
+retry:
+#ifdef IRIX
+ if ((rv = _MD_irix_connect(osfd, addr, addrlen, timeout)) == -1) {
+#else
+ if ((rv = connect(osfd, (struct sockaddr *)addr, addrlen)) == -1) {
+#endif
+ err = _MD_ERRNO();
+
+ if (err == EINTR) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ goto retry;
+ }
+
+ if (!fd->secret->nonblocking && (err == EINPROGRESS)) {
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ /*
+ * _PR_WaitForFD() may return 0 (timeout or interrupt) or 1.
+ */
+
+ rv = _PR_WaitForFD(osfd, PR_POLL_WRITE, timeout);
+ if (rv == 0) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ return -1;
+ }
+ } else {
+ /*
+ * socket_io_wait() may return -1 or 1.
+ */
+
+ rv = socket_io_wait(osfd, WRITE_FD, timeout);
+ if (rv == -1) {
+ return -1;
+ }
+ }
+
+ PR_ASSERT(rv == 1);
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ err = _MD_unix_get_nonblocking_connect_error(osfd);
+ if (err != 0) {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ return -1;
+ }
+ return 0;
+ }
+
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ }
+
+ return rv;
+} /* _MD_connect */
+
+PRInt32 _MD_bind(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen)
+{
+ PRInt32 rv, err;
+
+ rv = bind(fd->secret->md.osfd, (struct sockaddr *) addr, (int )addrlen);
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_BIND_ERROR(err);
+ }
+ return(rv);
+}
+
+PRInt32 _MD_listen(PRFileDesc *fd, PRIntn backlog)
+{
+ PRInt32 rv, err;
+
+ rv = listen(fd->secret->md.osfd, backlog);
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_LISTEN_ERROR(err);
+ }
+ return(rv);
+}
+
+PRInt32 _MD_shutdown(PRFileDesc *fd, PRIntn how)
+{
+ PRInt32 rv, err;
+
+ rv = shutdown(fd->secret->md.osfd, how);
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_SHUTDOWN_ERROR(err);
+ }
+ return(rv);
+}
+
+PRInt32 _MD_socketpair(int af, int type, int flags,
+ PRInt32 *osfd)
+{
+ PRInt32 rv, err;
+
+ rv = socketpair(af, type, flags, osfd);
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_SOCKETPAIR_ERROR(err);
+ }
+ return rv;
+}
+
+PRStatus _MD_getsockname(PRFileDesc *fd, PRNetAddr *addr,
+ PRUint32 *addrlen)
+{
+ PRInt32 rv, err;
+
+ rv = getsockname(fd->secret->md.osfd,
+ (struct sockaddr *) addr, (_PRSockLen_t *)addrlen);
+#ifdef AIX
+ if (rv == 0) {
+ /* mask off the first byte of struct sockaddr (the length field) */
+ if (addr) {
+ addr->inet.family &= 0x00ff;
+ }
+ }
+#endif
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_GETSOCKNAME_ERROR(err);
+ }
+ return rv==0?PR_SUCCESS:PR_FAILURE;
+}
+
+PRStatus _MD_getpeername(PRFileDesc *fd, PRNetAddr *addr,
+ PRUint32 *addrlen)
+{
+ PRInt32 rv, err;
+
+ rv = getpeername(fd->secret->md.osfd,
+ (struct sockaddr *) addr, (_PRSockLen_t *)addrlen);
+#ifdef AIX
+ if (rv == 0) {
+ /* mask off the first byte of struct sockaddr (the length field) */
+ if (addr) {
+ addr->inet.family &= 0x00ff;
+ }
+ }
+#endif
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_GETPEERNAME_ERROR(err);
+ }
+ return rv==0?PR_SUCCESS:PR_FAILURE;
+}
+
+PRStatus _MD_getsockopt(PRFileDesc *fd, PRInt32 level,
+ PRInt32 optname, char* optval, PRInt32* optlen)
+{
+ PRInt32 rv, err;
+
+ rv = getsockopt(fd->secret->md.osfd, level, optname, optval, (_PRSockLen_t *)optlen);
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_GETSOCKOPT_ERROR(err);
+ }
+ return rv==0?PR_SUCCESS:PR_FAILURE;
+}
+
+PRStatus _MD_setsockopt(PRFileDesc *fd, PRInt32 level,
+ PRInt32 optname, const char* optval, PRInt32 optlen)
+{
+ PRInt32 rv, err;
+
+ rv = setsockopt(fd->secret->md.osfd, level, optname, optval, optlen);
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_SETSOCKOPT_ERROR(err);
+ }
+ return rv==0?PR_SUCCESS:PR_FAILURE;
+}
+
+PR_IMPLEMENT(PRInt32) _MD_pr_poll(PRPollDesc *pds, PRIntn npds,
+ PRIntervalTime timeout)
+{
+ PRPollDesc *pd, *epd;
+ PRPollQueue pq;
+ PRInt32 n, err, pdcnt;
+ PRIntn is;
+ _PRUnixPollDesc *unixpds, *unixpd;
+ _PRCPU *io_cpu;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_PR_IS_NATIVE_THREAD(me)) {
+ fd_set rd, wt, ex;
+ struct timeval tv, *tvp = NULL;
+ int maxfd = -1;
+ /*
+ * For restarting _MD_SELECT() if it is interrupted by a signal.
+ * We use these variables to figure out how much time has elapsed
+ * and how much of the timeout still remains.
+ */
+ PRIntervalTime start, elapsed, remaining;
+
+ FD_ZERO(&rd);
+ FD_ZERO(&wt);
+ FD_ZERO(&ex);
+
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ PRInt32 osfd;
+ PRInt16 in_flags = pd->in_flags;
+ PRFileDesc *bottom = pd->fd;
+
+ if ((NULL == bottom) || (in_flags == 0)) {
+ continue;
+ }
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ osfd = bottom->secret->md.osfd;
+
+ if (osfd > maxfd) {
+ maxfd = osfd;
+ }
+ if (in_flags & PR_POLL_READ) {
+ FD_SET(osfd, &rd);
+ }
+ if (in_flags & PR_POLL_WRITE) {
+ FD_SET(osfd, &wt);
+ }
+ if (in_flags & PR_POLL_EXCEPT) {
+ FD_SET(osfd, &ex);
+ }
+ }
+ if (timeout != PR_INTERVAL_NO_TIMEOUT) {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(timeout) % PR_USEC_PER_SEC;
+ tvp = &tv;
+ start = PR_IntervalNow();
+ }
+
+retry:
+ n = _MD_SELECT(maxfd + 1, &rd, &wt, &ex, tvp);
+ if (n == -1 && errno == EINTR) {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ goto retry;
+ } else {
+ elapsed = (PRIntervalTime) (PR_IntervalNow() - start);
+ if (elapsed > timeout) {
+ n = 0; /* timed out */
+ } else {
+ remaining = timeout - elapsed;
+ tv.tv_sec = PR_IntervalToSeconds(remaining);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ remaining - PR_SecondsToInterval(tv.tv_sec));
+ goto retry;
+ }
+ }
+ }
+
+ if (n > 0) {
+ n = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ PRInt32 osfd;
+ PRInt16 in_flags = pd->in_flags;
+ PRInt16 out_flags = 0;
+ PRFileDesc *bottom = pd->fd;
+
+ if ((NULL == bottom) || (in_flags == 0)) {
+ pd->out_flags = 0;
+ continue;
+ }
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ osfd = bottom->secret->md.osfd;
+
+ if ((in_flags & PR_POLL_READ) && FD_ISSET(osfd, &rd)) {
+ out_flags |= PR_POLL_READ;
+ }
+ if ((in_flags & PR_POLL_WRITE) && FD_ISSET(osfd, &wt)) {
+ out_flags |= PR_POLL_WRITE;
+ }
+ if ((in_flags & PR_POLL_EXCEPT) && FD_ISSET(osfd, &ex)) {
+ out_flags |= PR_POLL_EXCEPT;
+ }
+ pd->out_flags = out_flags;
+ if (out_flags) {
+ n++;
+ }
+ }
+ PR_ASSERT(n > 0);
+ } else if (n < 0) {
+ err = _MD_ERRNO();
+ if (err == EBADF) {
+ /* Find the bad fds */
+ n = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ PRFileDesc *bottom = pd->fd;
+ pd->out_flags = 0;
+ if ((NULL == bottom) || (pd->in_flags == 0)) {
+ continue;
+ }
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ if (fcntl(bottom->secret->md.osfd, F_GETFL, 0) == -1) {
+ pd->out_flags = PR_POLL_NVAL;
+ n++;
+ }
+ }
+ PR_ASSERT(n > 0);
+ } else {
+ PR_ASSERT(err != EINTR); /* should have been handled above */
+ _PR_MD_MAP_SELECT_ERROR(err);
+ }
+ }
+
+ return n;
+ }
+
+ /*
+ * XXX
+ * PRPollDesc has a PRFileDesc field, fd, while the IOQ
+ * is a list of PRPollQueue structures, each of which contains
+ * a _PRUnixPollDesc. A _PRUnixPollDesc struct contains
+ * the OS file descriptor, osfd, and not a PRFileDesc.
+ * So, we have allocate memory for _PRUnixPollDesc structures,
+ * copy the flags information from the pds list and have pq
+ * point to this list of _PRUnixPollDesc structures.
+ *
+ * It would be better if the memory allocation can be avoided.
+ */
+
+ unixpds = (_PRUnixPollDesc*) PR_MALLOC(npds * sizeof(_PRUnixPollDesc));
+ if (!unixpds) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+ unixpd = unixpds;
+
+ _PR_INTSOFF(is);
+ _PR_MD_IOQ_LOCK();
+ _PR_THREAD_LOCK(me);
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ _PR_MD_IOQ_UNLOCK();
+ _PR_FAST_INTSON(is);
+ PR_DELETE(unixpds);
+ return -1;
+ }
+
+ pdcnt = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ PRInt32 osfd;
+ PRInt16 in_flags = pd->in_flags;
+ PRFileDesc *bottom = pd->fd;
+
+ if ((NULL == bottom) || (in_flags == 0)) {
+ continue;
+ }
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ osfd = bottom->secret->md.osfd;
+
+ PR_ASSERT(osfd >= 0 || in_flags == 0);
+
+ unixpd->osfd = osfd;
+ unixpd->in_flags = pd->in_flags;
+ unixpd++;
+ pdcnt++;
+
+ if (in_flags & PR_POLL_READ) {
+ FD_SET(osfd, &_PR_FD_READ_SET(me->cpu));
+ _PR_FD_READ_CNT(me->cpu)[osfd]++;
+ }
+ if (in_flags & PR_POLL_WRITE) {
+ FD_SET(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ (_PR_FD_WRITE_CNT(me->cpu))[osfd]++;
+ }
+ if (in_flags & PR_POLL_EXCEPT) {
+ FD_SET(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ (_PR_FD_EXCEPTION_CNT(me->cpu))[osfd]++;
+ }
+ if (osfd > _PR_IOQ_MAX_OSFD(me->cpu))
+ _PR_IOQ_MAX_OSFD(me->cpu) = osfd;
+ }
+ if (timeout < _PR_IOQ_TIMEOUT(me->cpu))
+ _PR_IOQ_TIMEOUT(me->cpu) = timeout;
+
+
+ pq.pds = unixpds;
+ pq.npds = pdcnt;
+
+ pq.thr = me;
+ io_cpu = me->cpu;
+ pq.on_ioq = PR_TRUE;
+ pq.timeout = timeout;
+ _PR_ADD_TO_IOQ(pq, me->cpu);
+ _PR_SLEEPQ_LOCK(me->cpu);
+ _PR_ADD_SLEEPQ(me, timeout);
+ me->state = _PR_IO_WAIT;
+ me->io_pending = PR_TRUE;
+ me->io_suspended = PR_FALSE;
+ _PR_SLEEPQ_UNLOCK(me->cpu);
+ _PR_THREAD_UNLOCK(me);
+ _PR_MD_IOQ_UNLOCK();
+
+ _PR_MD_WAIT(me, timeout);
+
+ me->io_pending = PR_FALSE;
+ me->io_suspended = PR_FALSE;
+
+ /*
+ * This thread should run on the same cpu on which it was blocked; when
+ * the IO request times out the fd sets and fd counts for the
+ * cpu are updated below.
+ */
+ PR_ASSERT(me->cpu == io_cpu);
+ /*
+ * Copy the out_flags from the _PRUnixPollDesc structures to the
+ * user's PRPollDesc structures and free the allocated memory
+ */
+ unixpd = unixpds;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ if ((NULL == pd->fd) || (pd->in_flags == 0)) {
+ pd->out_flags = 0;
+ continue;
+ }
+ pd->out_flags = unixpd->out_flags;
+ unixpd++;
+ }
+ PR_DELETE(unixpds);
+
+ /*
+ ** If we timed out the pollq might still be on the ioq. Remove it
+ ** before continuing.
+ */
+ if (pq.on_ioq) {
+ _PR_MD_IOQ_LOCK();
+ /*
+ * Need to check pq.on_ioq again
+ */
+ if (pq.on_ioq == PR_TRUE) {
+ PR_REMOVE_LINK(&pq.links);
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ PRInt32 osfd;
+ PRInt16 in_flags = pd->in_flags;
+ PRFileDesc *bottom = pd->fd;
+
+ if ((NULL == bottom) || (in_flags == 0)) {
+ continue;
+ }
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ osfd = bottom->secret->md.osfd;
+ PR_ASSERT(osfd >= 0 || in_flags == 0);
+ if (in_flags & PR_POLL_READ) {
+ if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
+ }
+ if (in_flags & PR_POLL_WRITE) {
+ if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ }
+ if (in_flags & PR_POLL_EXCEPT) {
+ if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ }
+ }
+ }
+ _PR_MD_IOQ_UNLOCK();
+ }
+ _PR_INTSON(is);
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ } else {
+ n = 0;
+ if (pq.on_ioq == PR_FALSE) {
+ /* Count the number of ready descriptors */
+ while (--npds >= 0) {
+ if (pds->out_flags) {
+ n++;
+ }
+ pds++;
+ }
+ }
+ return n;
+ }
+}
+
+
+
+
+/************************************************************************/
+
+/*
+** Scan through io queue and find any bad fd's that triggered the error
+** from _MD_SELECT
+*/
+static void FindBadFDs(void)
+{
+ PRCList *q;
+ PRThread *me = _MD_CURRENT_THREAD();
+
+ PR_ASSERT(!_PR_IS_NATIVE_THREAD(me));
+ q = (_PR_IOQ(me->cpu)).next;
+ _PR_IOQ_MAX_OSFD(me->cpu) = -1;
+ _PR_IOQ_TIMEOUT(me->cpu) = PR_INTERVAL_NO_TIMEOUT;
+ while (q != &_PR_IOQ(me->cpu)) {
+ PRPollQueue *pq = _PR_POLLQUEUE_PTR(q);
+ PRBool notify = PR_FALSE;
+ _PRUnixPollDesc *pds = pq->pds;
+ _PRUnixPollDesc *epds = pds + pq->npds;
+ PRInt32 pq_max_osfd = -1;
+
+ q = q->next;
+ for (; pds < epds; pds++) {
+ PRInt32 osfd = pds->osfd;
+ pds->out_flags = 0;
+ PR_ASSERT(osfd >= 0 || pds->in_flags == 0);
+ if (pds->in_flags == 0) {
+ continue; /* skip this fd */
+ }
+ if (fcntl(osfd, F_GETFL, 0) == -1) {
+ /* Found a bad descriptor, remove it from the fd_sets. */
+ PR_LOG(_pr_io_lm, PR_LOG_MAX,
+ ("file descriptor %d is bad", osfd));
+ pds->out_flags = PR_POLL_NVAL;
+ notify = PR_TRUE;
+ }
+ if (osfd > pq_max_osfd) {
+ pq_max_osfd = osfd;
+ }
+ }
+
+ if (notify) {
+ PRIntn pri;
+ PR_REMOVE_LINK(&pq->links);
+ pq->on_ioq = PR_FALSE;
+
+ /*
+ * Decrement the count of descriptors for each desciptor/event
+ * because this I/O request is being removed from the
+ * ioq
+ */
+ pds = pq->pds;
+ for (; pds < epds; pds++) {
+ PRInt32 osfd = pds->osfd;
+ PRInt16 in_flags = pds->in_flags;
+ PR_ASSERT(osfd >= 0 || in_flags == 0);
+ if (in_flags & PR_POLL_READ) {
+ if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
+ }
+ if (in_flags & PR_POLL_WRITE) {
+ if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ }
+ if (in_flags & PR_POLL_EXCEPT) {
+ if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ }
+ }
+
+ _PR_THREAD_LOCK(pq->thr);
+ if (pq->thr->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) {
+ _PRCPU *cpu = pq->thr->cpu;
+ _PR_SLEEPQ_LOCK(pq->thr->cpu);
+ _PR_DEL_SLEEPQ(pq->thr, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(pq->thr->cpu);
+
+ pri = pq->thr->priority;
+ pq->thr->state = _PR_RUNNABLE;
+
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(pq->thr, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ }
+ _PR_THREAD_UNLOCK(pq->thr);
+ } else {
+ if (pq->timeout < _PR_IOQ_TIMEOUT(me->cpu))
+ _PR_IOQ_TIMEOUT(me->cpu) = pq->timeout;
+ if (_PR_IOQ_MAX_OSFD(me->cpu) < pq_max_osfd)
+ _PR_IOQ_MAX_OSFD(me->cpu) = pq_max_osfd;
+ }
+ }
+ if (_PR_IS_NATIVE_THREAD_SUPPORTED()) {
+ if (_PR_IOQ_MAX_OSFD(me->cpu) < _pr_md_pipefd[0])
+ _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0];
+ }
+}
+
+/************************************************************************/
+
+/*
+** Called by the scheduler when there is nothing to do. This means that
+** all threads are blocked on some monitor somewhere.
+**
+** Note: this code doesn't release the scheduler lock.
+*/
+/*
+** Pause the current CPU. longjmp to the cpu's pause stack
+**
+** This must be called with the scheduler locked
+*/
+void _MD_PauseCPU(PRIntervalTime ticks)
+{
+ PRThread *me = _MD_CURRENT_THREAD();
+#ifdef _PR_USE_POLL
+ int timeout;
+ struct pollfd *pollfds; /* an array of pollfd structures */
+ struct pollfd *pollfdPtr; /* a pointer that steps through the array */
+ unsigned long npollfds; /* number of pollfd structures in array */
+ int nfd; /* to hold the return value of poll() */
+#else
+ struct timeval timeout, *tvp;
+ fd_set r, w, e;
+ fd_set *rp, *wp, *ep;
+ PRInt32 max_osfd, nfd;
+#endif /* _PR_USE_POLL */
+ PRInt32 rv;
+ PRCList *q;
+ PRUint32 min_timeout;
+ sigset_t oldset;
+#ifdef IRIX
+extern sigset_t ints_off;
+#endif
+
+ PR_ASSERT(_PR_MD_GET_INTSOFF() != 0);
+
+ _PR_MD_IOQ_LOCK();
+
+#ifdef _PR_USE_POLL
+ /* Build up the pollfd structure array to wait on */
+
+ /* Find out how many pollfd structures are needed */
+ npollfds = 0;
+ for (q = _PR_IOQ(me->cpu).next; q != &_PR_IOQ(me->cpu); q = q->next) {
+ PRPollQueue *pq = _PR_POLLQUEUE_PTR(q);
+
+ npollfds += pq->npds;
+ }
+ /*
+ * We use a pipe to wake up a native thread. An fd is needed
+ * for the pipe and we poll it for reading.
+ */
+ if (_PR_IS_NATIVE_THREAD_SUPPORTED())
+ npollfds++;
+
+ pollfds = (struct pollfd *) PR_MALLOC(npollfds * sizeof(struct pollfd));
+ pollfdPtr = pollfds;
+
+ /*
+ * If we need to poll the pipe for waking up a native thread,
+ * the pipe's fd is the first element in the pollfds array.
+ */
+ if (_PR_IS_NATIVE_THREAD_SUPPORTED()) {
+ pollfdPtr->fd = _pr_md_pipefd[0];
+ pollfdPtr->events = PR_POLL_READ;
+ pollfdPtr++;
+ }
+
+ min_timeout = PR_INTERVAL_NO_TIMEOUT;
+ for (q = _PR_IOQ(me->cpu).next; q != &_PR_IOQ(me->cpu); q = q->next) {
+ PRPollQueue *pq = _PR_POLLQUEUE_PTR(q);
+ _PRUnixPollDesc *pds = pq->pds;
+ _PRUnixPollDesc *epds = pds + pq->npds;
+
+ if (pq->timeout < min_timeout) {
+ min_timeout = pq->timeout;
+ }
+ for (; pds < epds; pds++, pollfdPtr++) {
+ /*
+ * Assert that the pollfdPtr pointer does not go
+ * beyond the end of the pollfds array
+ */
+ PR_ASSERT(pollfdPtr < pollfds + npollfds);
+ pollfdPtr->fd = pds->osfd;
+ /* direct copy of poll flags */
+ pollfdPtr->events = pds->in_flags;
+ }
+ }
+#else
+ /*
+ * assigment of fd_sets
+ */
+ r = _PR_FD_READ_SET(me->cpu);
+ w = _PR_FD_WRITE_SET(me->cpu);
+ e = _PR_FD_EXCEPTION_SET(me->cpu);
+
+ rp = &r;
+ wp = &w;
+ ep = &e;
+
+ max_osfd = _PR_IOQ_MAX_OSFD(me->cpu) + 1;
+ min_timeout = _PR_IOQ_TIMEOUT(me->cpu);
+#endif /* _PR_USE_POLL */
+ /*
+ ** Compute the minimum timeout value: make it the smaller of the
+ ** timeouts specified by the i/o pollers or the timeout of the first
+ ** sleeping thread.
+ */
+ q = _PR_SLEEPQ(me->cpu).next;
+
+ if (q != &_PR_SLEEPQ(me->cpu)) {
+ PRThread *t = _PR_THREAD_PTR(q);
+
+ if (t->sleep < min_timeout) {
+ min_timeout = t->sleep;
+ }
+ }
+ if (min_timeout > ticks) {
+ min_timeout = ticks;
+ }
+
+#ifdef _PR_USE_POLL
+ if (min_timeout == PR_INTERVAL_NO_TIMEOUT)
+ timeout = -1;
+ else
+ timeout = PR_IntervalToMilliseconds(min_timeout);
+#else
+ if (min_timeout == PR_INTERVAL_NO_TIMEOUT) {
+ tvp = NULL;
+ } else {
+ timeout.tv_sec = PR_IntervalToSeconds(min_timeout);
+ timeout.tv_usec = PR_IntervalToMicroseconds(min_timeout)
+ % PR_USEC_PER_SEC;
+ tvp = &timeout;
+ }
+#endif /* _PR_USE_POLL */
+
+ _PR_MD_IOQ_UNLOCK();
+ _MD_CHECK_FOR_EXIT();
+ /*
+ * check for i/o operations
+ */
+#ifndef _PR_NO_CLOCK_TIMER
+ /*
+ * Disable the clock interrupts while we are in select, if clock interrupts
+ * are enabled. Otherwise, when the select/poll calls are interrupted, the
+ * timer value starts ticking from zero again when the system call is restarted.
+ */
+#ifdef IRIX
+ /*
+ * SIGCHLD signal is used on Irix to detect he termination of an
+ * sproc by SIGSEGV, SIGBUS or SIGABRT signals when
+ * _nspr_terminate_on_error is set.
+ */
+ if ((!_nspr_noclock) || (_nspr_terminate_on_error))
+#else
+ if (!_nspr_noclock)
+#endif /* IRIX */
+#ifdef IRIX
+ sigprocmask(SIG_BLOCK, &ints_off, &oldset);
+#else
+ PR_ASSERT(sigismember(&timer_set, SIGALRM));
+ sigprocmask(SIG_BLOCK, &timer_set, &oldset);
+#endif /* IRIX */
+#endif /* !_PR_NO_CLOCK_TIMER */
+
+#ifndef _PR_USE_POLL
+ PR_ASSERT(FD_ISSET(_pr_md_pipefd[0],rp));
+ nfd = _MD_SELECT(max_osfd, rp, wp, ep, tvp);
+#else
+ nfd = _MD_POLL(pollfds, npollfds, timeout);
+#endif /* !_PR_USE_POLL */
+
+#ifndef _PR_NO_CLOCK_TIMER
+#ifdef IRIX
+ if ((!_nspr_noclock) || (_nspr_terminate_on_error))
+#else
+ if (!_nspr_noclock)
+#endif /* IRIX */
+ sigprocmask(SIG_SETMASK, &oldset, 0);
+#endif /* !_PR_NO_CLOCK_TIMER */
+
+ _MD_CHECK_FOR_EXIT();
+ _PR_MD_IOQ_LOCK();
+ /*
+ ** Notify monitors that are associated with the selected descriptors.
+ */
+#ifdef _PR_USE_POLL
+ if (nfd > 0) {
+ pollfdPtr = pollfds;
+ if (_PR_IS_NATIVE_THREAD_SUPPORTED()) {
+ /*
+ * Assert that the pipe is the first element in the
+ * pollfds array.
+ */
+ PR_ASSERT(pollfds[0].fd == _pr_md_pipefd[0]);
+ if ((pollfds[0].revents & PR_POLL_READ) && (nfd == 1)) {
+ /*
+ * woken up by another thread; read all the data
+ * in the pipe to empty the pipe
+ */
+ while ((rv = read(_pr_md_pipefd[0], _pr_md_pipebuf,
+ PIPE_BUF)) == PIPE_BUF){
+ }
+ PR_ASSERT((rv > 0) || ((rv == -1) && (errno == EAGAIN)));
+ }
+ pollfdPtr++;
+ }
+ for (q = _PR_IOQ(me->cpu).next; q != &_PR_IOQ(me->cpu); q = q->next) {
+ PRPollQueue *pq = _PR_POLLQUEUE_PTR(q);
+ PRBool notify = PR_FALSE;
+ _PRUnixPollDesc *pds = pq->pds;
+ _PRUnixPollDesc *epds = pds + pq->npds;
+
+ for (; pds < epds; pds++, pollfdPtr++) {
+ /*
+ * Assert that the pollfdPtr pointer does not go beyond
+ * the end of the pollfds array.
+ */
+ PR_ASSERT(pollfdPtr < pollfds + npollfds);
+ /*
+ * Assert that the fd's in the pollfds array (stepped
+ * through by pollfdPtr) are in the same order as
+ * the fd's in _PR_IOQ() (stepped through by q and pds).
+ * This is how the pollfds array was created earlier.
+ */
+ PR_ASSERT(pollfdPtr->fd == pds->osfd);
+ pds->out_flags = pollfdPtr->revents;
+ /* Negative fd's are ignored by poll() */
+ if (pds->osfd >= 0 && pds->out_flags) {
+ notify = PR_TRUE;
+ }
+ }
+ if (notify) {
+ PRIntn pri;
+ PRThread *thred;
+
+ PR_REMOVE_LINK(&pq->links);
+ pq->on_ioq = PR_FALSE;
+
+ /*
+ * Because this thread can run on a different cpu right
+ * after being added to the run queue, do not dereference
+ * pq
+ */
+ thred = pq->thr;
+ _PR_THREAD_LOCK(thred);
+ if (pq->thr->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) {
+ _PRCPU *cpu = pq->thr->cpu;
+ _PR_SLEEPQ_LOCK(pq->thr->cpu);
+ _PR_DEL_SLEEPQ(pq->thr, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(pq->thr->cpu);
+
+ pri = pq->thr->priority;
+ pq->thr->state = _PR_RUNNABLE;
+
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(pq->thr, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ if (_pr_md_idle_cpus > 1)
+ _PR_MD_WAKEUP_WAITER(thred);
+ }
+ _PR_THREAD_UNLOCK(thred);
+ }
+ }
+ } else if (nfd == -1) {
+ PR_LOG(_pr_io_lm, PR_LOG_MAX, ("poll() failed with errno %d", errno));
+ }
+
+ /* done with pollfds */
+ PR_DELETE(pollfds);
+#else
+ if (nfd > 0) {
+ q = _PR_IOQ(me->cpu).next;
+ _PR_IOQ_MAX_OSFD(me->cpu) = -1;
+ _PR_IOQ_TIMEOUT(me->cpu) = PR_INTERVAL_NO_TIMEOUT;
+ while (q != &_PR_IOQ(me->cpu)) {
+ PRPollQueue *pq = _PR_POLLQUEUE_PTR(q);
+ PRBool notify = PR_FALSE;
+ _PRUnixPollDesc *pds = pq->pds;
+ _PRUnixPollDesc *epds = pds + pq->npds;
+ PRInt32 pq_max_osfd = -1;
+
+ q = q->next;
+ for (; pds < epds; pds++) {
+ PRInt32 osfd = pds->osfd;
+ PRInt16 in_flags = pds->in_flags;
+ PRInt16 out_flags = 0;
+ PR_ASSERT(osfd >= 0 || in_flags == 0);
+ if ((in_flags & PR_POLL_READ) && FD_ISSET(osfd, rp)) {
+ out_flags |= PR_POLL_READ;
+ }
+ if ((in_flags & PR_POLL_WRITE) && FD_ISSET(osfd, wp)) {
+ out_flags |= PR_POLL_WRITE;
+ }
+ if ((in_flags & PR_POLL_EXCEPT) && FD_ISSET(osfd, ep)) {
+ out_flags |= PR_POLL_EXCEPT;
+ }
+ pds->out_flags = out_flags;
+ if (out_flags) {
+ notify = PR_TRUE;
+ }
+ if (osfd > pq_max_osfd) {
+ pq_max_osfd = osfd;
+ }
+ }
+ if (notify == PR_TRUE) {
+ PRIntn pri;
+ PRThread *thred;
+
+ PR_REMOVE_LINK(&pq->links);
+ pq->on_ioq = PR_FALSE;
+
+ /*
+ * Decrement the count of descriptors for each desciptor/event
+ * because this I/O request is being removed from the
+ * ioq
+ */
+ pds = pq->pds;
+ for (; pds < epds; pds++) {
+ PRInt32 osfd = pds->osfd;
+ PRInt16 in_flags = pds->in_flags;
+ PR_ASSERT(osfd >= 0 || in_flags == 0);
+ if (in_flags & PR_POLL_READ) {
+ if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
+ }
+ if (in_flags & PR_POLL_WRITE) {
+ if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ }
+ if (in_flags & PR_POLL_EXCEPT) {
+ if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ }
+ }
+
+ /*
+ * Because this thread can run on a different cpu right
+ * after being added to the run queue, do not dereference
+ * pq
+ */
+ thred = pq->thr;
+ _PR_THREAD_LOCK(thred);
+ if (pq->thr->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) {
+ _PRCPU *cpu = thred->cpu;
+ _PR_SLEEPQ_LOCK(pq->thr->cpu);
+ _PR_DEL_SLEEPQ(pq->thr, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(pq->thr->cpu);
+
+ pri = pq->thr->priority;
+ pq->thr->state = _PR_RUNNABLE;
+
+ pq->thr->cpu = cpu;
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(pq->thr, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ if (_pr_md_idle_cpus > 1)
+ _PR_MD_WAKEUP_WAITER(thred);
+ }
+ _PR_THREAD_UNLOCK(thred);
+ } else {
+ if (pq->timeout < _PR_IOQ_TIMEOUT(me->cpu))
+ _PR_IOQ_TIMEOUT(me->cpu) = pq->timeout;
+ if (_PR_IOQ_MAX_OSFD(me->cpu) < pq_max_osfd)
+ _PR_IOQ_MAX_OSFD(me->cpu) = pq_max_osfd;
+ }
+ }
+ if (_PR_IS_NATIVE_THREAD_SUPPORTED()) {
+ if ((FD_ISSET(_pr_md_pipefd[0], rp)) && (nfd == 1)) {
+ /*
+ * woken up by another thread; read all the data
+ * in the pipe to empty the pipe
+ */
+ while ((rv =
+ read(_pr_md_pipefd[0], _pr_md_pipebuf, PIPE_BUF))
+ == PIPE_BUF){
+ }
+ PR_ASSERT((rv > 0) ||
+ ((rv == -1) && (errno == EAGAIN)));
+ }
+ if (_PR_IOQ_MAX_OSFD(me->cpu) < _pr_md_pipefd[0])
+ _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0];
+ }
+ } else if (nfd < 0) {
+ if (errno == EBADF) {
+ FindBadFDs();
+ } else {
+ PR_LOG(_pr_io_lm, PR_LOG_MAX, ("select() failed with errno %d",
+ errno));
+ }
+ } else {
+ PR_ASSERT(nfd == 0);
+ /*
+ * compute the new value of _PR_IOQ_TIMEOUT
+ */
+ q = _PR_IOQ(me->cpu).next;
+ _PR_IOQ_MAX_OSFD(me->cpu) = -1;
+ _PR_IOQ_TIMEOUT(me->cpu) = PR_INTERVAL_NO_TIMEOUT;
+ while (q != &_PR_IOQ(me->cpu)) {
+ PRPollQueue *pq = _PR_POLLQUEUE_PTR(q);
+ _PRUnixPollDesc *pds = pq->pds;
+ _PRUnixPollDesc *epds = pds + pq->npds;
+ PRInt32 pq_max_osfd = -1;
+
+ q = q->next;
+ for (; pds < epds; pds++) {
+ if (pds->osfd > pq_max_osfd) {
+ pq_max_osfd = pds->osfd;
+ }
+ }
+ if (pq->timeout < _PR_IOQ_TIMEOUT(me->cpu))
+ _PR_IOQ_TIMEOUT(me->cpu) = pq->timeout;
+ if (_PR_IOQ_MAX_OSFD(me->cpu) < pq_max_osfd)
+ _PR_IOQ_MAX_OSFD(me->cpu) = pq_max_osfd;
+ }
+ if (_PR_IS_NATIVE_THREAD_SUPPORTED()) {
+ if (_PR_IOQ_MAX_OSFD(me->cpu) < _pr_md_pipefd[0])
+ _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0];
+ }
+ }
+#endif /* _PR_USE_POLL */
+ _PR_MD_IOQ_UNLOCK();
+}
+
+void _MD_Wakeup_CPUs()
+{
+ PRInt32 rv, data;
+
+ data = 0;
+ rv = write(_pr_md_pipefd[1], &data, 1);
+
+ while ((rv < 0) && (errno == EAGAIN)) {
+ /*
+ * pipe full, read all data in pipe to empty it
+ */
+ while ((rv =
+ read(_pr_md_pipefd[0], _pr_md_pipebuf, PIPE_BUF))
+ == PIPE_BUF) {
+ }
+ PR_ASSERT((rv > 0) ||
+ ((rv == -1) && (errno == EAGAIN)));
+ rv = write(_pr_md_pipefd[1], &data, 1);
+ }
+}
+
+
+void _MD_InitCPUS()
+{
+ PRInt32 rv, flags;
+ PRThread *me = _MD_CURRENT_THREAD();
+
+ rv = pipe(_pr_md_pipefd);
+ PR_ASSERT(rv == 0);
+ _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0];
+ FD_SET(_pr_md_pipefd[0], &_PR_FD_READ_SET(me->cpu));
+
+ flags = fcntl(_pr_md_pipefd[0], F_GETFL, 0);
+ fcntl(_pr_md_pipefd[0], F_SETFL, flags | O_NONBLOCK);
+ flags = fcntl(_pr_md_pipefd[1], F_GETFL, 0);
+ fcntl(_pr_md_pipefd[1], F_SETFL, flags | O_NONBLOCK);
+}
+
+/*
+** Unix SIGALRM (clock) signal handler
+*/
+static void ClockInterruptHandler()
+{
+ int olderrno;
+ PRUintn pri;
+ _PRCPU *cpu = _PR_MD_CURRENT_CPU();
+ PRThread *me = _MD_CURRENT_THREAD();
+
+#ifdef SOLARIS
+ if (!me || _PR_IS_NATIVE_THREAD(me)) {
+ _pr_primordialCPU->u.missed[_pr_primordialCPU->where] |= _PR_MISSED_CLOCK;
+ return;
+ }
+#endif
+
+ if (_PR_MD_GET_INTSOFF() != 0) {
+ cpu->u.missed[cpu->where] |= _PR_MISSED_CLOCK;
+ return;
+ }
+ _PR_MD_SET_INTSOFF(1);
+
+ olderrno = errno;
+ _PR_ClockInterrupt();
+ errno = olderrno;
+
+ /*
+ ** If the interrupt wants a resched or if some other thread at
+ ** the same priority needs the cpu, reschedule.
+ */
+ pri = me->priority;
+ if ((cpu->u.missed[3] || (_PR_RUNQREADYMASK(me->cpu) >> pri))) {
+#ifdef _PR_NO_PREEMPT
+ cpu->resched = PR_TRUE;
+ if (pr_interruptSwitchHook) {
+ (*pr_interruptSwitchHook)(pr_interruptSwitchHookArg);
+ }
+#else /* _PR_NO_PREEMPT */
+ /*
+ ** Re-enable unix interrupts (so that we can use
+ ** setjmp/longjmp for context switching without having to
+ ** worry about the signal state)
+ */
+ sigprocmask(SIG_SETMASK, &empty_set, 0);
+ PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("clock caused context switch"));
+
+ if(!(me->flags & _PR_IDLE_THREAD)) {
+ _PR_THREAD_LOCK(me);
+ me->state = _PR_RUNNABLE;
+ me->cpu = cpu;
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(me, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ _PR_THREAD_UNLOCK(me);
+ } else
+ me->state = _PR_RUNNABLE;
+ _MD_SWITCH_CONTEXT(me);
+ PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("clock back from context switch"));
+#endif /* _PR_NO_PREEMPT */
+ }
+ /*
+ * Because this thread could be running on a different cpu after
+ * a context switch the current cpu should be accessed and the
+ * value of the 'cpu' variable should not be used.
+ */
+ _PR_MD_SET_INTSOFF(0);
+}
+
+/* # of milliseconds per clock tick that we will use */
+#define MSEC_PER_TICK 50
+
+
+void _MD_StartInterrupts()
+{
+ struct itimerval itval;
+ char *eval;
+ struct sigaction vtact;
+
+ vtact.sa_handler = (void (*)()) ClockInterruptHandler;
+ vtact.sa_flags = SA_RESTART;
+ vtact.sa_mask = timer_set;
+ sigaction(SIGALRM, &vtact, 0);
+
+ if ((eval = getenv("NSPR_NOCLOCK")) != NULL) {
+ if (atoi(eval) == 0)
+ _nspr_noclock = 0;
+ else
+ _nspr_noclock = 1;
+ }
+
+#ifndef _PR_NO_CLOCK_TIMER
+ if (!_nspr_noclock) {
+ itval.it_interval.tv_sec = 0;
+ itval.it_interval.tv_usec = MSEC_PER_TICK * PR_USEC_PER_MSEC;
+ itval.it_value = itval.it_interval;
+ setitimer(ITIMER_REAL, &itval, 0);
+ }
+#endif
+}
+
+void _MD_StopInterrupts()
+{
+ sigprocmask(SIG_BLOCK, &timer_set, 0);
+}
+
+void _MD_DisableClockInterrupts()
+{
+ struct itimerval itval;
+ extern PRUintn _pr_numCPU;
+
+ PR_ASSERT(_pr_numCPU == 1);
+ if (!_nspr_noclock) {
+ itval.it_interval.tv_sec = 0;
+ itval.it_interval.tv_usec = 0;
+ itval.it_value = itval.it_interval;
+ setitimer(ITIMER_REAL, &itval, 0);
+ }
+}
+
+void _MD_BlockClockInterrupts()
+{
+ sigprocmask(SIG_BLOCK, &timer_set, 0);
+}
+
+void _MD_UnblockClockInterrupts()
+{
+ sigprocmask(SIG_UNBLOCK, &timer_set, 0);
+}
+
+void _MD_MakeNonblock(PRFileDesc *fd)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ int flags;
+
+ if (osfd <= 2) {
+ /* Don't mess around with stdin, stdout or stderr */
+ return;
+ }
+ flags = fcntl(osfd, F_GETFL, 0);
+
+ /*
+ * Use O_NONBLOCK (POSIX-style non-blocking I/O) whenever possible.
+ * On SunOS 4, we must use FNDELAY (BSD-style non-blocking I/O),
+ * otherwise connect() still blocks and can be interrupted by SIGALRM.
+ */
+
+#ifdef SUNOS4
+ fcntl(osfd, F_SETFL, flags | FNDELAY);
+#else
+ fcntl(osfd, F_SETFL, flags | O_NONBLOCK);
+#endif
+ }
+
+PRInt32 _MD_open(const char *name, PRIntn flags, PRIntn mode)
+{
+ PRInt32 osflags;
+ PRInt32 rv, err;
+
+ if (flags & PR_RDWR) {
+ osflags = O_RDWR;
+ } else if (flags & PR_WRONLY) {
+ osflags = O_WRONLY;
+ } else {
+ osflags = O_RDONLY;
+ }
+
+ if (flags & PR_APPEND)
+ osflags |= O_APPEND;
+ if (flags & PR_TRUNCATE)
+ osflags |= O_TRUNC;
+ if (flags & PR_SYNC) {
+#if defined(FREEBSD)
+ osflags |= O_FSYNC;
+#else
+ osflags |= O_SYNC;
+#endif
+ }
+
+ /*
+ ** On creations we hold the 'create' lock in order to enforce
+ ** the semantics of PR_Rename. (see the latter for more details)
+ */
+ if (flags & PR_CREATE_FILE)
+ {
+ osflags |= O_CREAT ;
+ if (NULL !=_pr_rename_lock)
+ PR_Lock(_pr_rename_lock);
+ }
+
+ rv = open(name, osflags, mode);
+
+ if (rv < 0) {
+ err = _MD_ERRNO();
+ _PR_MD_MAP_OPEN_ERROR(err);
+ }
+
+ if ((flags & PR_CREATE_FILE) && (NULL !=_pr_rename_lock))
+ PR_Unlock(_pr_rename_lock);
+ return rv;
+}
+
+PRIntervalTime intr_timeout_ticks;
+
+static void sigsegvhandler() {
+ fprintf(stderr,"Received SIGSEGV\n");
+ fflush(stderr);
+ pause();
+}
+
+static void sigaborthandler() {
+ fprintf(stderr,"Received SIGABRT\n");
+ fflush(stderr);
+ pause();
+}
+
+static void sigbushandler() {
+ fprintf(stderr,"Received SIGBUS\n");
+ fflush(stderr);
+ pause();
+}
+
+#endif /* !defined(_PR_PTHREADS) */
+
+void _PR_UnixInit()
+{
+ struct sigaction sigact;
+ int rv;
+
+ sigemptyset(&timer_set);
+
+#if !defined(_PR_PTHREADS)
+
+ sigaddset(&timer_set, SIGALRM);
+ sigemptyset(&empty_set);
+ intr_timeout_ticks =
+ PR_SecondsToInterval(_PR_INTERRUPT_CHECK_INTERVAL_SECS);
+
+#if defined(SOLARIS) || defined(IRIX)
+
+ if (getenv("NSPR_SIGSEGV_HANDLE")) {
+ sigact.sa_handler = sigsegvhandler;
+ sigact.sa_flags = SA_RESTART;
+ sigact.sa_mask = timer_set;
+ sigaction(SIGSEGV, &sigact, 0);
+ }
+
+ if (getenv("NSPR_SIGABRT_HANDLE")) {
+ sigact.sa_handler = sigaborthandler;
+ sigact.sa_flags = SA_RESTART;
+ sigact.sa_mask = timer_set;
+ sigaction(SIGABRT, &sigact, 0);
+ }
+
+ if (getenv("NSPR_SIGBUS_HANDLE")) {
+ sigact.sa_handler = sigbushandler;
+ sigact.sa_flags = SA_RESTART;
+ sigact.sa_mask = timer_set;
+ sigaction(SIGBUS, &sigact, 0);
+ }
+
+#endif
+#endif /* !defined(_PR_PTHREADS) */
+
+ /*
+ * Under HP-UX DCE threads, sigaction() installs a per-thread
+ * handler, so we use sigvector() to install a process-wide
+ * handler.
+ */
+#if defined(HPUX) && defined(_PR_DCETHREADS)
+ {
+ struct sigvec vec;
+
+ vec.sv_handler = SIG_IGN;
+ vec.sv_mask = 0;
+ vec.sv_flags = 0;
+ rv = sigvector(SIGPIPE, &vec, NULL);
+ PR_ASSERT(0 == rv);
+ }
+#else
+ sigact.sa_handler = SIG_IGN;
+ sigemptyset(&sigact.sa_mask);
+ sigact.sa_flags = 0;
+ rv = sigaction(SIGPIPE, &sigact, 0);
+ PR_ASSERT(0 == rv);
+#endif /* HPUX && _PR_DCETHREADS */
+
+ _pr_rename_lock = PR_NewLock();
+ PR_ASSERT(NULL != _pr_rename_lock);
+ _pr_Xfe_mon = PR_NewMonitor();
+ PR_ASSERT(NULL != _pr_Xfe_mon);
+
+}
+
+/*
+ * _MD_InitSegs --
+ *
+ * This is Unix's version of _PR_MD_INIT_SEGS(), which is
+ * called by _PR_InitSegs(), which in turn is called by
+ * PR_Init().
+ */
+void _MD_InitSegs()
+{
+#ifdef DEBUG
+ /*
+ ** Disable using mmap(2) if NSPR_NO_MMAP is set
+ */
+ if (getenv("NSPR_NO_MMAP")) {
+ _pr_zero_fd = -2;
+ return;
+ }
+#endif
+ _pr_zero_fd = open("/dev/zero",O_RDWR , 0);
+ _pr_md_lock = PR_NewLock();
+}
+
+PRStatus _MD_AllocSegment(PRSegment *seg, PRUint32 size, void *vaddr)
+{
+ static char *lastaddr = (char*) _PR_STACK_VMBASE;
+ PRStatus retval = PR_SUCCESS;
+ int prot;
+ void *rv;
+
+ PR_ASSERT(seg != 0);
+ PR_ASSERT(size != 0);
+
+ PR_Lock(_pr_md_lock);
+ if (_pr_zero_fd < 0) {
+from_heap:
+ seg->vaddr = PR_MALLOC(size);
+ if (!seg->vaddr) {
+ retval = PR_FAILURE;
+ }
+ else {
+ seg->size = size;
+ seg->access = PR_SEGMENT_RDWR;
+ }
+ goto exit;
+ }
+
+ prot = PROT_READ|PROT_WRITE;
+ rv = mmap((vaddr != 0) ? vaddr : lastaddr, size, prot,
+ _MD_MMAP_FLAGS,
+ _pr_zero_fd, 0);
+ if (rv == (void*)-1) {
+ goto from_heap;
+ }
+ lastaddr += size;
+ seg->vaddr = rv;
+ seg->size = size;
+ seg->access = PR_SEGMENT_RDWR;
+ seg->flags = _PR_SEG_VM;
+
+exit:
+ PR_Unlock(_pr_md_lock);
+ return retval;
+}
+
+void _MD_FreeSegment(PRSegment *seg)
+{
+ if (seg->flags & _PR_SEG_VM)
+ (void) munmap(seg->vaddr, seg->size);
+ else
+ PR_DELETE(seg->vaddr);
+}
+
+/*
+ *-----------------------------------------------------------------------
+ *
+ * PR_Now --
+ *
+ * Returns the current time in microseconds since the epoch.
+ * The epoch is midnight January 1, 1970 GMT.
+ * The implementation is machine dependent. This is the Unix
+ * implementation.
+ * Cf. time_t time(time_t *tp)
+ *
+ *-----------------------------------------------------------------------
+ */
+
+PR_IMPLEMENT(PRTime)
+PR_Now(void)
+{
+ struct timeval tv;
+ PRInt64 s, us, s2us;
+
+#if (defined(SOLARIS) && defined(_SVID_GETTOD)) || defined(SONY)
+ gettimeofday(&tv);
+#else
+ gettimeofday(&tv, 0);
+#endif
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(s, tv.tv_sec);
+ LL_I2L(us, tv.tv_usec);
+ LL_MUL(s, s, s2us);
+ LL_ADD(s, s, us);
+ return s;
+}
+
+PR_IMPLEMENT(PRIntervalTime) _PR_UNIX_GetInterval()
+{
+ struct timeval time;
+ PRIntervalTime ticks;
+
+#if defined(_SVID_GETTOD) || defined(SONY)
+ (void)gettimeofday(&time); /* fallicy of course */
+#else
+ (void)gettimeofday(&time, NULL); /* fallicy of course */
+#endif
+ ticks = (PRUint32)time.tv_sec * PR_MSEC_PER_SEC; /* that's in milliseconds */
+ ticks += (PRUint32)time.tv_usec / PR_USEC_PER_MSEC; /* so's that */
+ return ticks;
+} /* _PR_SUNOS_GetInterval */
+
+PR_IMPLEMENT(PRIntervalTime) _PR_UNIX_TicksPerSecond()
+{
+ return 1000; /* this needs some work :) */
+}
+
+/*
+ * _PR_UnixTransmitFile
+ *
+ * Send file fd across socket sd. If headers is non-NULL, 'hlen'
+ * bytes of headers is sent before sending the file.
+ *
+ * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file
+ *
+ * return number of bytes sent or -1 on error
+ *
+ */
+#define TRANSMITFILE_MMAP_CHUNK (256 * 1024)
+PR_IMPLEMENT(PRInt32) _PR_UnixTransmitFile(PRFileDesc *sd, PRFileDesc *fd,
+const void *headers, PRInt32 hlen, PRTransmitFileFlags flags,
+PRIntervalTime timeout)
+{
+ PRInt32 rv, count = 0;
+ PRInt32 len, index = 0;
+ struct stat statbuf;
+ struct PRIOVec iov[2];
+ void *addr;
+ PRInt32 err;
+
+ /* Get file size */
+ if (fstat(fd->secret->md.osfd, &statbuf) == -1) {
+ err = _MD_ERRNO();
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
+ break;
+ case ETIMEDOUT:
+#ifdef ENOLINK
+ case ENOLINK:
+#endif
+ PR_SetError(PR_REMOTE_FILE_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+ count = -1;
+ goto done;
+ }
+ /*
+ * If the file is large, mmap and send the file in chunks so as
+ * to not consume too much virtual address space
+ */
+ len = statbuf.st_size < TRANSMITFILE_MMAP_CHUNK ? statbuf.st_size :
+ TRANSMITFILE_MMAP_CHUNK;
+ /*
+ * Map in (part of) file. Take care of zero-length files.
+ */
+ if (len) {
+ addr = mmap((caddr_t) 0, len, PROT_READ, MAP_PRIVATE,
+ fd->secret->md.osfd, 0);
+
+ if (addr == (void*)-1) {
+ _PR_MD_MAP_MMAP_ERROR(_MD_ERRNO());
+ count = -1;
+ goto done;
+ }
+ }
+ /*
+ * send headers, first, followed by the file
+ */
+ if (hlen) {
+ iov[index].iov_base = (char *) headers;
+ iov[index].iov_len = hlen;
+ index++;
+ }
+ iov[index].iov_base = (char*)addr;
+ iov[index].iov_len = len;
+ index++;
+ rv = PR_Writev(sd, iov, index, timeout);
+ if (len)
+ munmap(addr,len);
+ if (rv >= 0) {
+ PR_ASSERT(rv == hlen + len);
+ statbuf.st_size -= len;
+ count += rv;
+ } else {
+ count = -1;
+ goto done;
+ }
+ /*
+ * send remaining bytes of the file, if any
+ */
+ len = statbuf.st_size < TRANSMITFILE_MMAP_CHUNK ? statbuf.st_size :
+ TRANSMITFILE_MMAP_CHUNK;
+ while (len > 0) {
+ /*
+ * Map in (part of) file
+ */
+ PR_ASSERT((count - hlen) % TRANSMITFILE_MMAP_CHUNK == 0);
+ addr = mmap((caddr_t) 0, len, PROT_READ, MAP_PRIVATE,
+ fd->secret->md.osfd, count - hlen);
+
+ if (addr == (void*)-1) {
+ _PR_MD_MAP_MMAP_ERROR(_MD_ERRNO());
+ count = -1;
+ goto done;
+ }
+ rv = PR_Send(sd, addr, len, 0, timeout);
+ munmap(addr,len);
+ if (rv >= 0) {
+ PR_ASSERT(rv == len);
+ statbuf.st_size -= rv;
+ count += rv;
+ len = statbuf.st_size < TRANSMITFILE_MMAP_CHUNK ?
+ statbuf.st_size : TRANSMITFILE_MMAP_CHUNK;
+ } else {
+ count = -1;
+ goto done;
+ }
+ }
+done:
+ if ((count >= 0) && (flags & PR_TRANSMITFILE_CLOSE_SOCKET))
+ PR_Close(sd);
+ return count;
+}
+
+#if defined(HPUX11) && !defined(_PR_PTHREADS)
+
+/*
+ * _PR_HPUXTransmitFile
+ *
+ * Send file fd across socket sd. If headers is non-NULL, 'hlen'
+ * bytes of headers is sent before sending the file.
+ *
+ * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file
+ *
+ * return number of bytes sent or -1 on error
+ *
+ * This implementation takes advantage of the sendfile() system
+ * call available in HP-UX B.11.00.
+ *
+ * Known problem: sendfile() does not work with NSPR's malloc()
+ * functions. The reason is unknown. So if you want to use
+ * _PR_HPUXTransmitFile(), you must not override the native malloc()
+ * functions.
+ */
+
+PRInt32
+_PR_HPUXTransmitFile(PRFileDesc *sd, PRFileDesc *fd,
+ const void *headers, PRInt32 hlen, PRTransmitFileFlags flags,
+ PRIntervalTime timeout)
+{
+ struct stat statbuf;
+ PRInt32 nbytes_to_send;
+ off_t offset;
+ struct iovec hdtrl[2]; /* optional header and trailer buffers */
+ int send_flags;
+ PRInt32 count;
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ /* Get file size */
+ if (fstat(fd->secret->md.osfd, &statbuf) == -1) {
+ _PR_MD_MAP_FSTAT_ERROR(errno);
+ return -1;
+ }
+ nbytes_to_send = hlen + statbuf.st_size;
+ offset = 0;
+
+ hdtrl[0].iov_base = (void *) headers; /* cast away the 'const' */
+ hdtrl[0].iov_len = hlen;
+ hdtrl[1].iov_base = NULL;
+ hdtrl[1].iov_base = 0;
+ /*
+ * SF_DISCONNECT seems to disconnect the socket even if sendfile()
+ * only does a partial send on a nonblocking socket. This
+ * would prevent the subsequent sendfile() calls on that socket
+ * from working. So we don't use the SD_DISCONNECT flag.
+ */
+ send_flags = 0;
+ rv = 0;
+
+ while (1) {
+ count = sendfile(sd->secret->md.osfd, fd->secret->md.osfd,
+ offset, 0, hdtrl, send_flags);
+ PR_ASSERT(count <= nbytes_to_send);
+ if (count == -1) {
+ err = errno;
+ if (err == EINTR) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ continue; /* retry */
+ }
+ if (err != EAGAIN && err != EWOULDBLOCK) {
+ _MD_hpux_map_sendfile_error(err);
+ return -1;
+ }
+ count = 0;
+ }
+ rv += count;
+
+ if (count < nbytes_to_send) {
+ /*
+ * Optimization: if bytes sent is less than requested, call
+ * select before returning. This is because it is likely that
+ * the next sendfile() call will return EWOULDBLOCK.
+ */
+ if (!_PR_IS_NATIVE_THREAD(me)) {
+ if (_PR_WaitForFD(sd->secret->md.osfd,
+ PR_POLL_WRITE, timeout) == 0) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ return -1;
+ } else if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+ } else {
+ if (socket_io_wait(sd->secret->md.osfd, WRITE_FD, timeout)< 0) {
+ return -1;
+ }
+ }
+
+ if (hdtrl[0].iov_len == 0) {
+ PR_ASSERT(hdtrl[0].iov_base == NULL);
+ offset += count;
+ } else if (count < hdtrl[0].iov_len) {
+ PR_ASSERT(offset == 0);
+ hdtrl[0].iov_base = (char *) hdtrl[0].iov_base + count;
+ hdtrl[0].iov_len -= count;
+ } else {
+ offset = count - hdtrl[0].iov_len;
+ hdtrl[0].iov_base = NULL;
+ hdtrl[0].iov_len = 0;
+ }
+ nbytes_to_send -= count;
+ } else {
+ break; /* done */
+ }
+ }
+
+ if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) {
+ PR_Close(sd);
+ }
+ return rv;
+}
+
+#endif /* HPUX11 && !_PR_PTHREADS */
+
+#if !defined(_PR_PTHREADS)
+/*
+** Wait for I/O on a single descriptor.
+ *
+ * return 0, if timed-out or interrupted, else return 1
+*/
+PRInt32 _PR_WaitForFD(PRInt32 osfd, PRUintn how, PRIntervalTime timeout)
+{
+ _PRUnixPollDesc pd;
+ PRPollQueue pq;
+ PRIntn is;
+ PRInt32 rv = 1;
+ _PRCPU *io_cpu;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("waiting to %s on osfd=%d",
+ (how == PR_POLL_READ) ? "read" : "write",
+ osfd));
+
+ if (timeout == PR_INTERVAL_NO_WAIT) return 0;
+
+ pd.osfd = osfd;
+ pd.in_flags = how;
+ pd.out_flags = 0;
+
+ pq.pds = &pd;
+ pq.npds = 1;
+
+ _PR_INTSOFF(is);
+ _PR_MD_IOQ_LOCK();
+ _PR_THREAD_LOCK(me);
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ _PR_THREAD_UNLOCK(me);
+ _PR_MD_IOQ_UNLOCK();
+ _PR_FAST_INTSON(is);
+ return 0;
+ }
+
+ pq.thr = me;
+ io_cpu = me->cpu;
+ pq.on_ioq = PR_TRUE;
+ pq.timeout = timeout;
+ _PR_ADD_TO_IOQ(pq, me->cpu);
+ if (how == PR_POLL_READ) {
+ FD_SET(osfd, &_PR_FD_READ_SET(me->cpu));
+ (_PR_FD_READ_CNT(me->cpu))[osfd]++;
+ } else if (how == PR_POLL_WRITE) {
+ FD_SET(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ (_PR_FD_WRITE_CNT(me->cpu))[osfd]++;
+ } else {
+ FD_SET(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ (_PR_FD_EXCEPTION_CNT(me->cpu))[osfd]++;
+ }
+ if (_PR_IOQ_MAX_OSFD(me->cpu) < osfd)
+ _PR_IOQ_MAX_OSFD(me->cpu) = osfd;
+ if (_PR_IOQ_TIMEOUT(me->cpu) > timeout)
+ _PR_IOQ_TIMEOUT(me->cpu) = timeout;
+
+
+ _PR_SLEEPQ_LOCK(me->cpu);
+ _PR_ADD_SLEEPQ(me, timeout);
+ me->state = _PR_IO_WAIT;
+ me->io_pending = PR_TRUE;
+ me->io_suspended = PR_FALSE;
+ _PR_SLEEPQ_UNLOCK(me->cpu);
+ _PR_THREAD_UNLOCK(me);
+ _PR_MD_IOQ_UNLOCK();
+
+ _PR_MD_WAIT(me, timeout);
+ me->io_pending = PR_FALSE;
+ me->io_suspended = PR_FALSE;
+
+ /*
+ * This thread should run on the same cpu on which it was blocked; when
+ * the IO request times out the fd sets and fd counts for the
+ * cpu are updated below.
+ */
+ PR_ASSERT(me->cpu == io_cpu);
+
+ /*
+ ** If we timed out the pollq might still be on the ioq. Remove it
+ ** before continuing.
+ */
+ if (pq.on_ioq) {
+ _PR_MD_IOQ_LOCK();
+ /*
+ * Need to check pq.on_ioq again
+ */
+ if (pq.on_ioq) {
+ PR_REMOVE_LINK(&pq.links);
+ if (how == PR_POLL_READ) {
+ if ((--(_PR_FD_READ_CNT(me->cpu))[osfd]) == 0)
+ FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
+
+ } else if (how == PR_POLL_WRITE) {
+ if ((--(_PR_FD_WRITE_CNT(me->cpu))[osfd]) == 0)
+ FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ } else {
+ if ((--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd]) == 0)
+ FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ }
+ }
+ _PR_MD_IOQ_UNLOCK();
+ rv = 0;
+ }
+ _PR_FAST_INTSON(is);
+ return(rv);
+}
+
+/*
+ * Unblock threads waiting for I/O
+ * used when interrupting threads
+ *
+ * NOTE: The thread lock should held when this function is called.
+ * On return, the thread lock is released.
+ */
+void _PR_Unblock_IO_Wait(PRThread *thr)
+{
+ int pri = thr->priority;
+ _PRCPU *cpu = thr->cpu;
+
+ /*
+ * GLOBAL threads wakeup periodically to check for interrupt
+ */
+ if (_PR_IS_NATIVE_THREAD(thr)) {
+ _PR_THREAD_UNLOCK(thr);
+ return;
+ }
+
+ PR_ASSERT(thr->flags & (_PR_ON_SLEEPQ | _PR_ON_PAUSEQ));
+ _PR_SLEEPQ_LOCK(cpu);
+ _PR_DEL_SLEEPQ(thr, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(cpu);
+
+ PR_ASSERT(!(thr->flags & _PR_IDLE_THREAD));
+ thr->state = _PR_RUNNABLE;
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(thr, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ _PR_THREAD_UNLOCK(thr);
+ _PR_MD_WAKEUP_WAITER(thr);
+}
+#endif /* !defined(_PR_PTHREADS) */
+
+/*
+ * When a nonblocking connect has completed, determine whether it
+ * succeeded or failed, and if it failed, what the error code is.
+ *
+ * The function returns the error code. An error code of 0 means
+ * that the nonblocking connect succeeded.
+ */
+
+int _MD_unix_get_nonblocking_connect_error(int osfd)
+{
+#if defined(NCR) || defined(UNIXWARE) || defined(SNI) || defined(NEC)
+ /*
+ * getsockopt() fails with EPIPE, so use getmsg() instead.
+ */
+
+ int rv;
+ int flags = 0;
+ rv = getmsg(osfd, NULL, NULL, &flags);
+ PR_ASSERT(-1 == rv || 0 == rv);
+ if (-1 == rv && errno != EAGAIN && errno != EWOULDBLOCK) {
+ return errno;
+ }
+ return 0; /* no error */
+#else
+ int err;
+ _PRSockLen_t optlen = sizeof(err);
+ if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, (char *) &err, &optlen) == -1) {
+ return errno;
+ } else {
+ return err;
+ }
+#endif
+}
+
+/************************************************************************/
+
+/*
+** Special hacks for xlib. Xlib/Xt/Xm is not re-entrant nor is it thread
+** safe. Unfortunately, neither is mozilla. To make these programs work
+** in a pre-emptive threaded environment, we need to use a lock.
+*/
+
+void PR_XLock()
+{
+ PR_EnterMonitor(_pr_Xfe_mon);
+}
+
+void PR_XUnlock()
+{
+ PR_ExitMonitor(_pr_Xfe_mon);
+}
+
+PRBool PR_XIsLocked()
+{
+ return (PR_InMonitor(_pr_Xfe_mon)) ? PR_TRUE : PR_FALSE;
+}
+
+void PR_XWait(int ms)
+{
+ PR_Wait(_pr_Xfe_mon, PR_MillisecondsToInterval(ms));
+}
+
+void PR_XNotify(void)
+{
+ PR_Notify(_pr_Xfe_mon);
+}
+
+void PR_XNotifyAll(void)
+{
+ PR_NotifyAll(_pr_Xfe_mon);
+}
+
+#ifdef HAVE_BSD_FLOCK
+
+#include <sys/file.h>
+
+PR_IMPLEMENT(PRStatus)
+_MD_LockFile(PRInt32 f)
+{
+ PRInt32 rv;
+ rv = flock(f, LOCK_EX);
+ if (rv == 0)
+ return PR_SUCCESS;
+ _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO());
+ return PR_FAILURE;
+}
+
+PR_IMPLEMENT(PRStatus)
+_MD_TLockFile(PRInt32 f)
+{
+ PRInt32 rv;
+ rv = flock(f, LOCK_EX|LOCK_NB);
+ if (rv == 0)
+ return PR_SUCCESS;
+ _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO());
+ return PR_FAILURE;
+}
+
+PR_IMPLEMENT(PRStatus)
+_MD_UnlockFile(PRInt32 f)
+{
+ PRInt32 rv;
+ rv = flock(f, LOCK_UN);
+ if (rv == 0)
+ return PR_SUCCESS;
+ _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO());
+ return PR_FAILURE;
+}
+#else
+
+PR_IMPLEMENT(PRStatus)
+_MD_LockFile(PRInt32 f)
+{
+ PRInt32 rv;
+ rv = lockf(f, F_LOCK, 0);
+ if (rv == 0)
+ return PR_SUCCESS;
+ _PR_MD_MAP_LOCKF_ERROR(_MD_ERRNO());
+ return PR_FAILURE;
+}
+
+PR_IMPLEMENT(PRStatus)
+_MD_TLockFile(PRInt32 f)
+{
+ PRInt32 rv;
+ rv = lockf(f, F_TLOCK, 0);
+ if (rv == 0)
+ return PR_SUCCESS;
+ _PR_MD_MAP_LOCKF_ERROR(_MD_ERRNO());
+ return PR_FAILURE;
+}
+
+PR_IMPLEMENT(PRStatus)
+_MD_UnlockFile(PRInt32 f)
+{
+ PRInt32 rv;
+ rv = lockf(f, F_ULOCK, 0);
+ if (rv == 0)
+ return PR_SUCCESS;
+ _PR_MD_MAP_LOCKF_ERROR(_MD_ERRNO());
+ return PR_FAILURE;
+}
+#endif
+
+PR_IMPLEMENT(PRStatus) _MD_gethostname(char *name, PRUint32 namelen)
+{
+ PRIntn rv;
+
+ rv = gethostname(name, namelen);
+ if (0 == rv) {
+ return PR_SUCCESS;
+ }
+ _PR_MD_MAP_GETHOSTNAME_ERROR(_MD_ERRNO());
+ return PR_FAILURE;
+}
+
+/*
+ *******************************************************************
+ *
+ * Memory-mapped files
+ *
+ *******************************************************************
+ */
+
+PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size)
+{
+ PRFileInfo info;
+ PRUint32 sz;
+
+ LL_L2UI(sz, size);
+ if (sz) {
+ if (PR_GetOpenFileInfo(fmap->fd, &info) == PR_FAILURE) {
+ return PR_FAILURE;
+ }
+ if (sz > info.size) {
+ /*
+ * Need to extend the file
+ */
+ if (fmap->prot != PR_PROT_READWRITE) {
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, 0);
+ return PR_FAILURE;
+ }
+ if (PR_Seek(fmap->fd, sz - 1, PR_SEEK_SET) == -1) {
+ return PR_FAILURE;
+ }
+ if (PR_Write(fmap->fd, "", 1) != 1) {
+ return PR_FAILURE;
+ }
+ }
+ }
+ if (fmap->prot == PR_PROT_READONLY) {
+ fmap->md.prot = PROT_READ;
+ fmap->md.flags = 0;
+ } else if (fmap->prot == PR_PROT_READWRITE) {
+ fmap->md.prot = PROT_READ | PROT_WRITE;
+ fmap->md.flags = MAP_SHARED;
+ } else {
+ PR_ASSERT(fmap->prot == PR_PROT_WRITECOPY);
+ fmap->md.prot = PROT_READ | PROT_WRITE;
+ fmap->md.flags = MAP_PRIVATE;
+ }
+ return PR_SUCCESS;
+}
+
+void * _MD_MemMap(
+ PRFileMap *fmap,
+ PRInt64 offset,
+ PRUint32 len)
+{
+ PRInt32 off;
+ void *addr;
+
+ LL_L2I(off, offset);
+ if ((addr = mmap(0, len, fmap->md.prot, fmap->md.flags,
+ fmap->fd->secret->md.osfd, off)) == (void *) -1) {
+ _PR_MD_MAP_MMAP_ERROR(_MD_ERRNO());
+ addr = NULL;
+ }
+ return addr;
+}
+
+PRStatus _MD_MemUnmap(void *addr, PRUint32 len)
+{
+ if (munmap(addr, len) == 0) {
+ return PR_SUCCESS;
+ } else {
+ if (errno == EINVAL) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, errno);
+ } else {
+ PR_SetError(PR_UNKNOWN_ERROR, errno);
+ }
+ return PR_FAILURE;
+ }
+}
+
+PRStatus _MD_CloseFileMap(PRFileMap *fmap)
+{
+ PR_DELETE(fmap);
+ return PR_SUCCESS;
+}
diff --git a/pr/src/md/unix/unix_errors.c b/pr/src/md/unix/unix_errors.c
new file mode 100644
index 00000000..92d96c73
--- /dev/null
+++ b/pr/src/md/unix/unix_errors.c
@@ -0,0 +1,1484 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "prtypes.h"
+#include "md/_unix_errors.h"
+#include "prerror.h"
+#include <errno.h>
+
+void _MD_unix_map_opendir_error(int err)
+{
+ switch (err) {
+ case ENOTDIR:
+ PR_SetError(PR_NOT_DIRECTORY_ERROR, err);
+ break;
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case EMFILE:
+ PR_SetError(PR_PROC_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ENFILE:
+ PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ELOOP:
+ PR_SetError(PR_LOOP_ERROR, err);
+ break;
+ case ENAMETOOLONG:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ENOENT:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_closedir_error(int err)
+{
+ switch (err) {
+ case EINVAL:
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_readdir_error(int err)
+{
+
+ switch (err) {
+ case ENOENT:
+ PR_SetError(PR_NO_MORE_FILES_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+#ifdef IRIX
+#ifdef IRIX5_3
+#else
+ case EDIRCORRUPTED:
+ PR_SetError(PR_DIRECTORY_CORRUPTED_ERROR, err);
+ break;
+#endif
+#endif
+#ifdef EOVERFLOW
+ case EOVERFLOW:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+#endif
+ case EINVAL:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+#ifdef EBADMSG
+ case EBADMSG:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+#endif
+ case EDEADLK:
+ PR_SetError(PR_DEADLOCK_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
+ break;
+ case EIO:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ENOLCK:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+#ifdef ENOLINK
+ case ENOLINK:
+ PR_SetError(PR_REMOTE_FILE_ERROR, err);
+ break;
+#endif
+ case ENXIO:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_unlink_error(int err)
+{
+ switch (err) {
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case EBUSY:
+ PR_SetError(PR_FILESYSTEM_MOUNTED_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
+ break;
+ case ELOOP:
+ PR_SetError(PR_LOOP_ERROR, err);
+ break;
+ case ENAMETOOLONG:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ENOENT:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ENOTDIR:
+ PR_SetError(PR_NOT_DIRECTORY_ERROR, err);
+ break;
+ case EPERM:
+ PR_SetError(PR_IS_DIRECTORY_ERROR, err);
+ break;
+ case EROFS:
+ PR_SetError(PR_READ_ONLY_FILESYSTEM_ERROR, err);
+ break;
+#if !defined(OSF1) && !defined(FREEBSD) && !defined(BSDI)
+ case EMULTIHOP:
+ case ENOLINK:
+ PR_SetError(PR_REMOTE_FILE_ERROR, err);
+ break;
+#endif
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_stat_error(int err)
+{
+ switch (err) {
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
+ break;
+ case ETIMEDOUT:
+#if !defined(OSF1) && !defined(FREEBSD) && !defined(BSDI)
+ case EMULTIHOP:
+ case ENOLINK:
+#endif
+ PR_SetError(PR_REMOTE_FILE_ERROR, err);
+ break;
+ case ELOOP:
+ PR_SetError(PR_LOOP_ERROR, err);
+ break;
+ case ENAMETOOLONG:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ENOENT:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ENOTDIR:
+ PR_SetError(PR_NOT_DIRECTORY_ERROR, err);
+ break;
+#ifdef EOVERFLOW
+ case EOVERFLOW:
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err);
+ break;
+#endif
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_fstat_error(int err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
+ break;
+ case ETIMEDOUT:
+#ifdef ENOLINK
+ case ENOLINK:
+#endif
+ PR_SetError(PR_REMOTE_FILE_ERROR, err);
+ break;
+#ifdef EOVERFLOW
+ case EOVERFLOW:
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err);
+ break;
+#endif
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_rename_error(int err)
+{
+ switch (err) {
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case EBUSY:
+ PR_SetError(PR_FILESYSTEM_MOUNTED_ERROR, err);
+ break;
+#ifdef EDQUOT
+ case EDQUOT:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+#endif
+ case EEXIST:
+ PR_SetError(PR_DIRECTORY_NOT_EMPTY_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
+ break;
+ case EINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case EIO:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case EISDIR:
+ PR_SetError(PR_IS_DIRECTORY_ERROR, err);
+ break;
+ case ELOOP:
+ PR_SetError(PR_LOOP_ERROR, err);
+ break;
+#if !defined(OSF1) && !defined(FREEBSD) && !defined(BSDI)
+ case EMULTIHOP:
+ case ENOLINK:
+ PR_SetError(PR_REMOTE_FILE_ERROR, err);
+ break;
+#endif
+ case ENAMETOOLONG:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ENOENT:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ENOSPC:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+ case ENOTDIR:
+ PR_SetError(PR_NOT_DIRECTORY_ERROR, err);
+ break;
+ case EROFS:
+ PR_SetError(PR_READ_ONLY_FILESYSTEM_ERROR, err);
+ break;
+ case EXDEV:
+ PR_SetError(PR_NOT_SAME_DEVICE_ERROR, err);
+ break;
+ case EMLINK:
+ PR_SetError(PR_MAX_DIRECTORY_ENTRIES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_access_error(int err)
+{
+ switch (err) {
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
+ break;
+ case EINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ELOOP:
+ PR_SetError(PR_LOOP_ERROR, err);
+ break;
+#if !defined(OSF1) && !defined(FREEBSD) && !defined(BSDI)
+ case EMULTIHOP:
+ case ENOLINK:
+#endif
+ case ETIMEDOUT:
+ PR_SetError(PR_REMOTE_FILE_ERROR, err);
+ break;
+ case ENAMETOOLONG:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ENOENT:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ENOTDIR:
+ PR_SetError(PR_NOT_DIRECTORY_ERROR, err);
+ break;
+ case EROFS:
+ PR_SetError(PR_READ_ONLY_FILESYSTEM_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_mkdir_error(int err)
+{
+ switch (err) {
+ case ENOTDIR:
+ PR_SetError(PR_NOT_DIRECTORY_ERROR, err);
+ break;
+ case ENOENT:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ENAMETOOLONG:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case EEXIST:
+ PR_SetError(PR_FILE_EXISTS_ERROR, err);
+ break;
+ case EROFS:
+ PR_SetError(PR_READ_ONLY_FILESYSTEM_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ELOOP:
+ PR_SetError(PR_LOOP_ERROR, err);
+ break;
+ case EMLINK:
+ PR_SetError(PR_MAX_DIRECTORY_ENTRIES_ERROR, err);
+ break;
+ case ENOSPC:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+#ifdef EDQUOT
+ case EDQUOT:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+#endif
+ case EIO:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+#if !defined(OSF1) && !defined(FREEBSD) && !defined(BSDI)
+ case EMULTIHOP:
+ case ENOLINK:
+ PR_SetError(PR_REMOTE_FILE_ERROR, err);
+ break;
+#endif
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_rmdir_error(int err)
+{
+
+ switch (err) {
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case EBUSY:
+ PR_SetError(PR_FILESYSTEM_MOUNTED_ERROR, err);
+ break;
+ case EEXIST:
+ PR_SetError(PR_DIRECTORY_NOT_EMPTY_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EINVAL:
+ PR_SetError(PR_DIRECTORY_NOT_EMPTY_ERROR, err);
+ break;
+ case EIO:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ELOOP:
+ PR_SetError(PR_LOOP_ERROR, err);
+ break;
+#if !defined(OSF1) && !defined(FREEBSD) && !defined(BSDI)
+ case EMULTIHOP:
+ case ENOLINK:
+#endif
+ case ETIMEDOUT:
+ PR_SetError(PR_REMOTE_FILE_ERROR, err);
+ break;
+ case ENAMETOOLONG:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ENOENT:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ENOTDIR:
+ PR_SetError(PR_NOT_DIRECTORY_ERROR, err);
+ break;
+ case EROFS:
+ PR_SetError(PR_READ_ONLY_FILESYSTEM_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_read_error(int err)
+{
+ switch (err) {
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case EAGAIN:
+#if EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+#ifdef EBADMSG
+ case EBADMSG:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+#endif
+ case EDEADLK:
+ PR_SetError(PR_DEADLOCK_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
+ break;
+ case EINVAL:
+ PR_SetError(PR_INVALID_METHOD_ERROR, err);
+ break;
+ case EIO:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ENOLCK:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ENXIO:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case EISDIR:
+ PR_SetError(PR_IS_DIRECTORY_ERROR, err);
+ break;
+ case ECONNRESET:
+ case EPIPE:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+#ifdef ENOLINK
+ case ENOLINK:
+ PR_SetError(PR_REMOTE_FILE_ERROR, err);
+ break;
+#endif
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_write_error(int err)
+{
+ switch (err) {
+ case EAGAIN:
+#if EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case EDEADLK:
+ PR_SetError(PR_DEADLOCK_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EFBIG:
+ PR_SetError(PR_FILE_TOO_BIG_ERROR, err);
+ break;
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
+ break;
+ case EINVAL:
+ PR_SetError(PR_INVALID_METHOD_ERROR, err);
+ break;
+ case EIO:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ENOLCK:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+#ifdef ENOSR
+ case ENOSR:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+#endif
+ case ENOSPC:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+ case ENXIO:
+ PR_SetError(PR_INVALID_METHOD_ERROR, err);
+ break;
+ case ERANGE:
+ PR_SetError(PR_INVALID_METHOD_ERROR, err);
+ break;
+ case ETIMEDOUT:
+ PR_SetError(PR_REMOTE_FILE_ERROR, err);
+ break;
+ case ECONNRESET:
+ case EPIPE:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+#ifdef EDQUOT
+ case EDQUOT:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+#endif
+#ifdef ENOLINK
+ case ENOLINK:
+ PR_SetError(PR_REMOTE_FILE_ERROR, err);
+ break;
+#endif
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_lseek_error(int err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ESPIPE:
+ PR_SetError(PR_INVALID_METHOD_ERROR, err);
+ break;
+ case EINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_fsync_error(int err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+#ifdef ENOLINK
+ case ENOLINK:
+#endif
+ case ETIMEDOUT:
+ PR_SetError(PR_REMOTE_FILE_ERROR, err);
+ break;
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
+ break;
+ case EIO:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case EINVAL:
+ PR_SetError(PR_INVALID_METHOD_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_close_error(int err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
+ break;
+#ifdef ENOLINK
+ case ENOLINK:
+#endif
+ case ETIMEDOUT:
+ PR_SetError(PR_REMOTE_FILE_ERROR, err);
+ break;
+ case EIO:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_socket_error(int err)
+{
+ switch (err) {
+ case EPROTONOSUPPORT:
+ PR_SetError(PR_PROTOCOL_NOT_SUPPORTED_ERROR, err);
+ break;
+ case EMFILE:
+ PR_SetError(PR_PROC_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ENFILE:
+ PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+#if !defined(SCO_SV)
+ case ENOBUFS:
+#endif /* !defined(SCO_SV) */
+ case ENOMEM:
+#ifdef ENOSR
+ case ENOSR:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+#endif
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_socketavailable_error(int err)
+{
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+}
+
+void _MD_unix_map_recv_error(int err)
+{
+ switch (err) {
+ case EAGAIN:
+#if EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ENOMEM:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, err);
+ break;
+ case ECONNRESET:
+ case EPIPE:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+#ifdef ENOSR
+ case ENOSR:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+#endif
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_recvfrom_error(int err)
+{
+ switch (err) {
+ case EAGAIN:
+#if EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ENOMEM:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, err);
+ break;
+#ifdef ENOSR
+ case ENOSR:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+#endif
+ case ECONNRESET:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_send_error(int err)
+{
+ switch (err) {
+ case EAGAIN:
+#if EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case EMSGSIZE:
+ case EINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+#if !defined(SCO_SV)
+ case ENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+#endif /* !defined(SCO_SV) */
+ case ECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case EISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
+ break;
+ case ENOMEM:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, err);
+ break;
+#ifdef ENOSR
+ case ENOSR:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+#endif
+ case ECONNRESET:
+ case EPIPE:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_sendto_error(int err)
+{
+ switch (err) {
+ case EAGAIN:
+#if EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case EMSGSIZE:
+ case EINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+#if !defined(SCO_SV)
+ case ENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+#endif /* !defined(SCO_SV) */
+ case ECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case EISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
+ break;
+ case ENOMEM:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, err);
+ break;
+#ifdef ENOSR
+ case ENOSR:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+#endif
+ case ECONNRESET:
+ case EPIPE:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_writev_error(int err)
+{
+ switch (err) {
+ case EAGAIN:
+#if EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
+ break;
+#ifdef ENOSR
+ case ENOSR:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+#endif
+ case EINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ECONNRESET:
+ case EPIPE:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_accept_error(int err)
+{
+ switch (err) {
+ case EAGAIN:
+#if EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case EOPNOTSUPP:
+ case ENODEV:
+ PR_SetError(PR_NOT_TCP_SOCKET_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EMFILE:
+ PR_SetError(PR_PROC_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ENFILE:
+ PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
+ break;
+ case ENOMEM:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, err);
+ break;
+#ifdef ENOSR
+ case ENOSR:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+#endif
+#ifdef EPROTO
+ case EPROTO:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+#endif
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_connect_error(int err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case EADDRNOTAVAIL:
+ PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err);
+ break;
+ case EINPROGRESS:
+ PR_SetError(PR_IN_PROGRESS_ERROR, err);
+ break;
+ case EALREADY:
+ PR_SetError(PR_ALREADY_INITIATED_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case EAFNOSUPPORT:
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err);
+ break;
+ case EISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+ case ETIMEDOUT:
+ PR_SetError(PR_IO_TIMEOUT_ERROR, err);
+ break;
+ case ECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case ENETUNREACH:
+ PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, err);
+ break;
+ case EADDRINUSE:
+ PR_SetError(PR_ADDRESS_IN_USE_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ /*
+ * UNIX domain sockets are not supported in NSPR
+ */
+ case EACCES:
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err);
+ break;
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
+ break;
+ case EINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case EIO:
+#if defined(UNIXWARE) || defined(SNI) || defined(NEC)
+ /*
+ * On some platforms, if we connect to a port on
+ * the local host (the loopback address) that no
+ * process is listening on, we get EIO instead
+ * of ECONNREFUSED.
+ */
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+#else
+ PR_SetError(PR_IO_ERROR, err);
+#endif
+ break;
+ case ELOOP:
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err);
+ break;
+ case ENOENT:
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err);
+ break;
+#ifdef ENOSR
+ case ENOSR:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+#endif
+ case ENXIO:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case EPROTOTYPE:
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_bind_error(int err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EADDRNOTAVAIL:
+ PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err);
+ break;
+ case EADDRINUSE:
+ PR_SetError(PR_ADDRESS_IN_USE_ERROR, err);
+ break;
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case EINVAL:
+ PR_SetError(PR_SOCKET_ADDRESS_IS_BOUND_ERROR, err);
+ break;
+#ifdef ENOSR
+ case ENOSR:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+#endif
+ /*
+ * UNIX domain sockets are not supported in NSPR
+ */
+ case EIO:
+ case EISDIR:
+ case ELOOP:
+ case ENOENT:
+ case ENOTDIR:
+ case EROFS:
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_listen_error(int err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case EOPNOTSUPP:
+ PR_SetError(PR_NOT_TCP_SOCKET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_shutdown_error(int err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case ENOTCONN:
+ PR_SetError(PR_NOT_CONNECTED_ERROR, err);
+ break;
+ case ENOMEM:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, err);
+ break;
+#ifdef ENOSR
+ case ENOSR:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+#endif
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_socketpair_error(int err)
+{
+ switch (err) {
+ case EMFILE:
+ PR_SetError(PR_PROC_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ENOMEM:
+#ifdef ENOSR
+ case ENOSR:
+#endif
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case EAFNOSUPPORT:
+ case EPROTONOSUPPORT:
+ case EOPNOTSUPP:
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_getsockname_error(int err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+#if !defined(SCO_SV)
+ case ENOBUFS:
+#endif /* !defined(SCO_SV) */
+ case ENOMEM:
+#ifdef ENOSR
+ case ENOSR:
+#endif
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_getpeername_error(int err)
+{
+
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case ENOTCONN:
+ PR_SetError(PR_NOT_CONNECTED_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+#if !defined(SCO_SV)
+ case ENOBUFS:
+#endif /* !defined(SCO_SV) */
+ case ENOMEM:
+#ifdef ENOSR
+ case ENOSR:
+#endif
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_getsockopt_error(int err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case ENOPROTOOPT:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EINVAL:
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err);
+ break;
+ case ENOMEM:
+#ifdef ENOSR
+ case ENOSR:
+#endif
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_setsockopt_error(int err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case ENOPROTOOPT:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EINVAL:
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err);
+ break;
+ case ENOMEM:
+#ifdef ENOSR
+ case ENOSR:
+#endif
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_open_error(int err)
+{
+ switch (err) {
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case EAGAIN:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case EBUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case EEXIST:
+ PR_SetError(PR_FILE_EXISTS_ERROR, err);
+ break;
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
+ break;
+ case EINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case EIO:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case EISDIR:
+ PR_SetError(PR_IS_DIRECTORY_ERROR, err);
+ break;
+ case ELOOP:
+ PR_SetError(PR_LOOP_ERROR, err);
+ break;
+ case EMFILE:
+ PR_SetError(PR_PROC_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ENAMETOOLONG:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ENFILE:
+ PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ENODEV:
+ case ENOENT:
+ case ENXIO:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ENOMEM:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ENOSPC:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+#ifdef ENOSR
+ case ENOSR:
+#endif
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ENOTDIR:
+ PR_SetError(PR_NOT_DIRECTORY_ERROR, err);
+ break;
+ case EPERM:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ETIMEDOUT:
+#if !defined(OSF1) && !defined(FREEBSD) && !defined(BSDI)
+ case EMULTIHOP:
+ case ENOLINK:
+#endif
+ PR_SetError(PR_REMOTE_FILE_ERROR, err);
+ break;
+ case EROFS:
+ PR_SetError(PR_READ_ONLY_FILESYSTEM_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_mmap_error(int err)
+{
+
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case EAGAIN:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ENOMEM:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_gethostname_error(int err)
+{
+ switch (err) {
+ case EFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_select_error(int err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err);
+ break;
+ case EINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_flock_error(int err)
+{
+ switch (err) {
+ case EBADF:
+ case EINVAL:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case EWOULDBLOCK:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_map_lockf_error(int err)
+{
+ switch (err) {
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case EACCES:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case EDEADLK:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+#ifdef HPUX11
+void _MD_hpux_map_sendfile_error(int oserror)
+{
+ PRErrorCode prerror;
+
+ switch (oserror) {
+ case ENOTSOCK:
+ prerror = PR_NOT_SOCKET_ERROR;
+ break;
+ case EFAULT:
+ prerror = PR_ACCESS_FAULT_ERROR;
+ break;
+ case ENOBUFS:
+ prerror = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+ case EINVAL:
+ prerror = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ case ENOTCONN:
+ prerror = PR_NOT_CONNECTED_ERROR;
+ break;
+ case EPIPE:
+ prerror = PR_CONNECT_RESET_ERROR;
+ break;
+ case ENOMEM:
+ prerror = PR_OUT_OF_MEMORY_ERROR;
+ break;
+ case EOPNOTSUPP:
+ prerror = PR_NOT_TCP_SOCKET_ERROR;
+ break;
+ default:
+ prerror = PR_UNKNOWN_ERROR;
+ }
+ PR_SetError(prerror, oserror);
+}
+#endif /* HPUX11 */
diff --git a/pr/src/md/unix/unixware.c b/pr/src/md/unix/unixware.c
new file mode 100644
index 00000000..4bc410bf
--- /dev/null
+++ b/pr/src/md/unix/unixware.c
@@ -0,0 +1,548 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+#include "primpl.h"
+
+#if !defined (USE_SVR4_THREADS)
+
+/*
+ * using only NSPR threads here
+ */
+
+#include <setjmp.h>
+
+void _MD_EarlyInit(void)
+{
+}
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+}
+
+#ifdef ALARMS_BREAK_TCP /* I don't think they do */
+
+PRInt32 _MD_connect(PRInt32 osfd, const PRNetAddr *addr, PRInt32 addrlen,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv;
+
+ _MD_BLOCK_CLOCK_INTERRUPTS();
+ rv = _connect(osfd,addr,addrlen);
+ _MD_UNBLOCK_CLOCK_INTERRUPTS();
+}
+
+PRInt32 _MD_accept(PRInt32 osfd, PRNetAddr *addr, PRInt32 addrlen,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv;
+
+ _MD_BLOCK_CLOCK_INTERRUPTS();
+ rv = _accept(osfd,addr,addrlen);
+ _MD_UNBLOCK_CLOCK_INTERRUPTS();
+ return(rv);
+}
+#endif
+
+/*
+ * These are also implemented in pratom.c using NSPR locks. Any reason
+ * this might be better or worse? If you like this better, define
+ * _PR_HAVE_ATOMIC_OPS in include/md/unixware.h
+ */
+#ifdef _PR_HAVE_ATOMIC_OPS
+/* Atomic operations */
+#include <stdio.h>
+static FILE *_uw_semf;
+
+void
+_MD_INIT_ATOMIC(void)
+{
+ /* Sigh. Sure wish SYSV semaphores weren't such a pain to use */
+ if ((_uw_semf = tmpfile()) == NULL)
+ PR_ASSERT(0);
+
+ return;
+}
+
+void
+_MD_ATOMIC_INCREMENT(PRInt32 *val)
+{
+ flockfile(_uw_semf);
+ (*val)++;
+ unflockfile(_uw_semf);
+}
+
+void
+_MD_ATOMIC_DECREMENT(PRInt32 *val)
+{
+ flockfile(_uw_semf);
+ (*val)--;
+ unflockfile(_uw_semf);
+}
+
+void
+_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
+{
+ flockfile(_uw_semf);
+ *val = newval;
+ unflockfile(_uw_semf);
+}
+#endif
+
+void
+_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
+{
+ return;
+}
+
+PRStatus
+_MD_InitializeThread(PRThread *thread)
+{
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ _PR_MD_SWITCH_CONTEXT(thread);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread) {
+ PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
+ }
+ return PR_SUCCESS;
+}
+
+/* These functions should not be called for Unixware */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for Unixware.");
+}
+
+PRStatus
+_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for Unixware.");
+}
+
+#else /* USE_SVR4_THREADS */
+
+/* NOTE:
+ * SPARC v9 (Ultras) do have an atomic test-and-set operation. But
+ * SPARC v8 doesn't. We should detect in the init if we are running on
+ * v8 or v9, and then use assembly where we can.
+ */
+
+#include <thread.h>
+#include <synch.h>
+
+static mutex_t _unixware_atomic = DEFAULTMUTEX;
+
+#define TEST_THEN_ADD(where, inc) \
+ if (mutex_lock(&_unixware_atomic) != 0)\
+ PR_ASSERT(0);\
+ *where += inc;\
+ if (mutex_unlock(&_unixware_atomic) != 0)\
+ PR_ASSERT(0);
+
+#define TEST_THEN_SET(where, val) \
+ if (mutex_lock(&_unixware_atomic) != 0)\
+ PR_ASSERT(0);\
+ *where = val;\
+ if (mutex_unlock(&_unixware_atomic) != 0)\
+ PR_ASSERT(0);
+
+void
+_MD_INIT_ATOMIC(void)
+{
+}
+
+void
+_MD_ATOMIC_INCREMENT(PRInt32 *val)
+{
+ TEST_THEN_ADD(val, 1);
+}
+
+void
+_MD_ATOMIC_DECREMENT(PRInt32 *val)
+{
+ TEST_THEN_ADD(val, 0xffffffff);
+}
+
+void
+_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
+{
+ TEST_THEN_SET(val, newval);
+}
+
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <sys/lwp.h>
+#include <sys/procfs.h>
+#include <sys/syscall.h>
+
+
+THREAD_KEY_T threadid_key;
+THREAD_KEY_T cpuid_key;
+THREAD_KEY_T last_thread_key;
+static sigset_t set, oldset;
+
+void _MD_EarlyInit(void)
+{
+ THR_KEYCREATE(&threadid_key, NULL);
+ THR_KEYCREATE(&cpuid_key, NULL);
+ THR_KEYCREATE(&last_thread_key, NULL);
+ sigemptyset(&set);
+ sigaddset(&set, SIGALRM);
+}
+
+PRStatus _MD_CREATE_THREAD(PRThread *thread,
+ void (*start)(void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ long flags;
+
+ /* mask out SIGALRM for native thread creation */
+ thr_sigsetmask(SIG_BLOCK, &set, &oldset);
+
+ flags = (state == PR_JOINABLE_THREAD ? THR_SUSPENDED/*|THR_NEW_LWP*/
+ : THR_SUSPENDED|THR_DETACHED/*|THR_NEW_LWP*/);
+ if (thread->flags & _PR_GCABLE_THREAD)
+ flags |= THR_BOUND;
+
+ if (thr_create(NULL, thread->stack->stackSize,
+ (void *(*)(void *)) start, (void *) thread,
+ flags,
+ &thread->md.handle)) {
+ thr_sigsetmask(SIG_SETMASK, &oldset, NULL);
+ return PR_FAILURE;
+ }
+
+
+ /* When the thread starts running, then the lwpid is set to the right
+ * value. Until then we want to mark this as 'uninit' so that
+ * its register state is initialized properly for GC */
+
+ thread->md.lwpid = -1;
+ thr_sigsetmask(SIG_SETMASK, &oldset, NULL);
+ _MD_NEW_SEM(&thread->md.waiter_sem, 0);
+
+ if (scope == PR_GLOBAL_THREAD) {
+ thread->flags |= _PR_GLOBAL_SCOPE;
+ }
+
+ /*
+ ** Set the thread priority. This will also place the thread on
+ ** the runQ.
+ **
+ ** Force PR_SetThreadPriority to set the priority by
+ ** setting thread->priority to 100.
+ */
+ {
+ int pri;
+ pri = thread->priority;
+ thread->priority = 100;
+ PR_SetThreadPriority( thread, pri );
+
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("(0X%x)[Start]: on to runq at priority %d",
+ thread, thread->priority));
+ }
+
+ /* Activate the thread */
+ if (thr_continue( thread->md.handle ) ) {
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+void _MD_cleanup_thread(PRThread *thread)
+{
+ thread_t hdl;
+ PRMonitor *mon;
+
+ hdl = thread->md.handle;
+
+ /*
+ ** First, suspend the thread (unless it's the active one)
+ ** Because we suspend it first, we don't have to use LOCK_SCHEDULER to
+ ** prevent both of us modifying the thread structure at the same time.
+ */
+ if ( thread != _PR_MD_CURRENT_THREAD() ) {
+ thr_suspend(hdl);
+ }
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("(0X%x)[DestroyThread]\n", thread));
+
+ _MD_DESTROY_SEM(&thread->md.waiter_sem);
+}
+
+void _MD_SET_PRIORITY(_MDThread *md_thread, PRUintn newPri)
+{
+ if(thr_setprio((thread_t)md_thread->handle, newPri)) {
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("_PR_SetThreadPriority: can't set thread priority\n"));
+ }
+}
+
+void _MD_WAIT_CV(
+ struct _MDCVar *md_cv, struct _MDLock *md_lock, PRIntervalTime timeout)
+{
+ struct timespec tt;
+ PRUint32 msec;
+ int rv;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ msec = PR_IntervalToMilliseconds(timeout);
+
+ GETTIME (&tt);
+
+ tt.tv_sec += msec / PR_MSEC_PER_SEC;
+ tt.tv_nsec += (msec % PR_MSEC_PER_SEC) * PR_NSEC_PER_MSEC;
+ /* Check for nsec overflow - otherwise we'll get an EINVAL */
+ if (tt.tv_nsec >= PR_NSEC_PER_SEC) {
+ tt.tv_sec++;
+ tt.tv_nsec -= PR_NSEC_PER_SEC;
+ }
+ me->md.sp = unixware_getsp();
+
+
+ /* XXX Solaris 2.5.x gives back EINTR occasionally for no reason
+ * hence ignore EINTR for now */
+
+ COND_TIMEDWAIT(&md_cv->cv, &md_lock->lock, &tt);
+}
+
+void _MD_lock(struct _MDLock *md_lock)
+{
+ mutex_lock(&md_lock->lock);
+}
+
+void _MD_unlock(struct _MDLock *md_lock)
+{
+ mutex_unlock(&((md_lock)->lock));
+}
+
+
+PRThread *_pr_current_thread_tls()
+{
+ PRThread *ret;
+
+ thr_getspecific(threadid_key, (void **)&ret);
+ return ret;
+}
+
+PRStatus
+_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ _MD_WAIT_SEM(&thread->md.waiter_sem);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread == NULL) {
+ return PR_SUCCESS;
+ }
+ _MD_POST_SEM(&thread->md.waiter_sem);
+ return PR_SUCCESS;
+}
+
+_PRCPU *_pr_current_cpu_tls()
+{
+ _PRCPU *ret;
+
+ thr_getspecific(cpuid_key, (void **)&ret);
+ return ret;
+}
+
+PRThread *_pr_last_thread_tls()
+{
+ PRThread *ret;
+
+ thr_getspecific(last_thread_key, (void **)&ret);
+ return ret;
+}
+
+_MDLock _pr_ioq_lock;
+
+void _MD_INIT_IO (void)
+{
+ _MD_NEW_LOCK(&_pr_ioq_lock);
+}
+
+PRStatus _MD_InitializeThread(PRThread *thread)
+{
+ if (!_PR_IS_NATIVE_THREAD(thread))
+ return;
+ /* prime the sp; substract 4 so we don't hit the assert that
+ * curr sp > base_stack
+ */
+ thread->md.sp = (uint_t) thread->stack->allocBase - sizeof(long);
+ thread->md.lwpid = _lwp_self();
+ thread->md.handle = THR_SELF();
+
+ /* all threads on Solaris are global threads from NSPR's perspective
+ * since all of them are mapped to Solaris threads.
+ */
+ thread->flags |= _PR_GLOBAL_SCOPE;
+
+ /* For primordial/attached thread, we don't create an underlying native thread.
+ * So, _MD_CREATE_THREAD() does not get called. We need to do initialization
+ * like allocating thread's synchronization variables and set the underlying
+ * native thread's priority.
+ */
+ if (thread->flags & (_PR_PRIMORDIAL | _PR_ATTACHED)) {
+ _MD_NEW_SEM(&thread->md.waiter_sem, 0);
+ _MD_SET_PRIORITY(&(thread->md), thread->priority);
+ }
+ return PR_SUCCESS;
+}
+
+static sigset_t old_mask; /* store away original gc thread sigmask */
+static int gcprio; /* store away original gc thread priority */
+static lwpid_t *all_lwps=NULL; /* list of lwps that we suspended */
+static int num_lwps ;
+static int suspendAllOn = 0;
+
+#define VALID_SP(sp, bottom, top) \
+ (((uint_t)(sp)) > ((uint_t)(bottom)) && ((uint_t)(sp)) < ((uint_t)(top)))
+
+void unixware_preempt_off()
+{
+ sigset_t set;
+ (void)sigfillset(&set);
+ sigprocmask (SIG_SETMASK, &set, &old_mask);
+}
+
+void unixware_preempt_on()
+{
+ sigprocmask (SIG_SETMASK, &old_mask, NULL);
+}
+
+void _MD_Begin_SuspendAll()
+{
+ unixware_preempt_off();
+
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin_SuspendAll\n"));
+ /* run at highest prio so I cannot be preempted */
+ thr_getprio(thr_self(), &gcprio);
+ thr_setprio(thr_self(), 0x7fffffff);
+ suspendAllOn = 1;
+}
+
+void _MD_End_SuspendAll()
+{
+}
+
+void _MD_End_ResumeAll()
+{
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End_ResumeAll\n"));
+ thr_setprio(thr_self(), gcprio);
+ unixware_preempt_on();
+ suspendAllOn = 0;
+}
+
+void _MD_Suspend(PRThread *thr)
+{
+ int lwp_fd, result;
+ int lwp_main_proc_fd = 0;
+
+ thr_suspend(thr->md.handle);
+ if (!(thr->flags & _PR_GCABLE_THREAD))
+ return;
+ /* XXX Primordial thread can't be bound to an lwp, hence there is no
+ * way we can assume that we can get the lwp status for primordial
+ * thread reliably. Hence we skip this for primordial thread, hoping
+ * that the SP is saved during lock and cond. wait.
+ * XXX - Again this is concern only for java interpreter, not for the
+ * server, 'cause primordial thread in the server does not do java work
+ */
+ if (thr->flags & _PR_PRIMORDIAL)
+ return;
+
+ /* if the thread is not started yet then don't do anything */
+ if (!suspendAllOn || thr->md.lwpid == -1)
+ return;
+
+}
+void _MD_Resume(PRThread *thr)
+{
+ if (!(thr->flags & _PR_GCABLE_THREAD) || !suspendAllOn){
+ /*XXX When the suspendAllOn is set, we will be trying to do lwp_suspend
+ * during that time we can't call any thread lib or libc calls. Hence
+ * make sure that no resume is requested for Non gcable thread
+ * during suspendAllOn */
+ PR_ASSERT(!suspendAllOn);
+ thr_continue(thr->md.handle);
+ return;
+ }
+ if (thr->md.lwpid == -1)
+ return;
+
+ if ( _lwp_continue(thr->md.lwpid) < 0) {
+ PR_ASSERT(0); /* ARGH, we are hosed! */
+ }
+}
+
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ if (isCurrent) {
+ (void) getcontext(CONTEXT(t)); /* XXX tune me: set md_IRIX.c */
+ }
+ *np = NGREG;
+ if (t->md.lwpid == -1)
+ memset(&t->md.context.uc_mcontext.gregs[0], 0, NGREG * sizeof(PRWord));
+ return (PRWord*) &t->md.context.uc_mcontext.gregs[0];
+}
+
+int
+_pr_unixware_clock_gettime (struct timespec *tp)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ tp->tv_sec = tv.tv_sec;
+ tp->tv_nsec = tv.tv_usec * 1000;
+ return 0;
+}
+
+
+#endif /* USE_SVR4_THREADS */
diff --git a/pr/src/md/unix/uxproces.c b/pr/src/md/unix/uxproces.c
new file mode 100644
index 00000000..7f8ab887
--- /dev/null
+++ b/pr/src/md/unix/uxproces.c
@@ -0,0 +1,715 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+/*
+ **********************************************************************
+ *
+ * The Unix process routines
+ *
+ **********************************************************************
+ */
+
+#define _PR_SIGNALED_EXITSTATUS 256
+
+typedef enum pr_PidState {
+ _PR_PID_DETACHED,
+ _PR_PID_REAPED,
+ _PR_PID_WAITING
+} pr_PidState;
+
+typedef struct pr_PidRecord {
+ pid_t pid;
+ int exitStatus;
+ pr_PidState state;
+ PRCondVar *reapedCV;
+ struct pr_PidRecord *next;
+} pr_PidRecord;
+
+/*
+ * Irix sprocs and LinuxThreads are actually a kind of processes
+ * that can share the virtual address space and file descriptors.
+ */
+#if (defined(IRIX) && !defined(_PR_PTHREADS)) \
+ || (defined(LINUX) && defined(_PR_PTHREADS))
+#define _PR_SHARE_CLONES
+#endif
+
+/*
+ * The macro _PR_NATIVE_THREADS indicates that we are
+ * using native threads only, so waitpid() blocks just the
+ * calling thread, not the process. In this case, the waitpid
+ * daemon thread can safely block in waitpid(). So we don't
+ * need to catch SIGCHLD, and the pipe to unblock PR_Poll() is
+ * also not necessary.
+ */
+
+#if defined(_PR_GLOBAL_THREADS_ONLY) \
+ || (defined(_PR_PTHREADS) && !defined(LINUX))
+#define _PR_NATIVE_THREADS
+#endif
+
+/*
+ * All the static variables used by the Unix process routines are
+ * collected in this structure.
+ */
+
+static struct {
+ PRCallOnceType once;
+ PRThread *thread;
+ PRLock *ml;
+#if defined(_PR_NATIVE_THREADS)
+ PRInt32 numProcs;
+ PRCondVar *cv;
+#else
+ int pipefd[2];
+#endif
+ pr_PidRecord **pidTable;
+
+#ifdef _PR_SHARE_CLONES
+ struct pr_CreateProcOp *opHead, *opTail;
+#endif
+} pr_wp;
+
+#ifdef _PR_SHARE_CLONES
+static int pr_waitpid_daemon_exit;
+
+void
+_MD_unix_terminate_waitpid_daemon(void)
+{
+ if (pr_wp.thread) {
+ pr_waitpid_daemon_exit = 1;
+ write(pr_wp.pipefd[1], "", 1);
+ PR_JoinThread(pr_wp.thread);
+ }
+}
+#endif
+
+static PRStatus _MD_InitProcesses(void);
+#if !defined(_PR_NATIVE_THREADS)
+static void pr_InstallSigchldHandler(void);
+#endif
+
+static PRProcess *
+ForkAndExec(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr)
+{
+ PRProcess *process;
+
+ process = PR_NEW(PRProcess);
+ if (!process) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+
+ process->md.pid = fork();
+ if ((pid_t) -1 == process->md.pid) {
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, errno);
+ PR_DELETE(process);
+ return NULL;
+ } else if (0 == process->md.pid) { /* the child process */
+ /*
+ * If the child process needs to exit, it must call _exit().
+ * Do not call exit(), because exit() will flush and close
+ * the standard I/O file descriptors, and hence corrupt
+ * the parent process's standard I/O data structures.
+ */
+
+ if (attr) {
+ if (attr->stdinFd
+ && attr->stdinFd->secret->md.osfd != 0) {
+ if (dup2(attr->stdinFd->secret->md.osfd, 0) != 0) {
+ _exit(1); /* failed */
+ }
+ close(attr->stdinFd->secret->md.osfd);
+ }
+ if (attr->stdoutFd
+ && attr->stdoutFd->secret->md.osfd != 1) {
+ if (dup2(attr->stdoutFd->secret->md.osfd, 1) != 1) {
+ _exit(1); /* failed */
+ }
+ close(attr->stdoutFd->secret->md.osfd);
+ }
+ if (attr->stderrFd
+ && attr->stderrFd->secret->md.osfd != 2) {
+ if (dup2(attr->stderrFd->secret->md.osfd, 2) != 2) {
+ _exit(1); /* failed */
+ }
+ close(attr->stderrFd->secret->md.osfd);
+ }
+ }
+
+ (void)execve(path, argv, envp);
+ /* Whoops! It returned. That's a bad sign. */
+ _exit(1);
+ }
+
+#if defined(_PR_NATIVE_THREADS)
+ PR_Lock(pr_wp.ml);
+ if (0 == pr_wp.numProcs++) {
+ PR_NotifyCondVar(pr_wp.cv);
+ }
+ PR_Unlock(pr_wp.ml);
+#endif
+ return process;
+}
+
+#ifdef _PR_SHARE_CLONES
+
+struct pr_CreateProcOp {
+ const char *path;
+ char *const *argv;
+ char *const *envp;
+ const PRProcessAttr *attr;
+ PRProcess *process;
+ PRErrorCode prerror;
+ PRInt32 oserror;
+ PRBool done;
+ PRCondVar *doneCV;
+ struct pr_CreateProcOp *next;
+};
+
+PRProcess *
+_MD_CreateUnixProcess(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr)
+{
+ struct pr_CreateProcOp *op;
+ PRProcess *proc;
+ int rv;
+
+ if (PR_CallOnce(&pr_wp.once, _MD_InitProcesses) == PR_FAILURE) {
+ return NULL;
+ }
+
+ op = PR_NEW(struct pr_CreateProcOp);
+ if (NULL == op) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+ op->path = path;
+ op->argv = argv;
+ op->envp = envp;
+ op->attr = attr;
+ op->done = PR_FALSE;
+ op->doneCV = PR_NewCondVar(pr_wp.ml);
+ if (NULL == op->doneCV) {
+ PR_DELETE(op);
+ return NULL;
+ }
+ PR_Lock(pr_wp.ml);
+
+ /* add to the tail of op queue */
+ op->next = NULL;
+ if (pr_wp.opTail) {
+ pr_wp.opTail->next = op;
+ pr_wp.opTail = op;
+ } else {
+ PR_ASSERT(NULL == pr_wp.opHead);
+ pr_wp.opHead = pr_wp.opTail = op;
+ }
+
+ /* wake up the daemon thread */
+ do {
+ rv = write(pr_wp.pipefd[1], "", 1);
+ } while (-1 == rv && EINTR == errno);
+
+ while (op->done == PR_FALSE) {
+ PR_WaitCondVar(op->doneCV, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(pr_wp.ml);
+ PR_DestroyCondVar(op->doneCV);
+ proc = op->process;
+ if (!proc) {
+ PR_SetError(op->prerror, op->oserror);
+ }
+ PR_DELETE(op);
+ return proc;
+}
+
+#else /* ! _PR_SHARE_CLONES */
+
+PRProcess *
+_MD_CreateUnixProcess(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr)
+{
+ if (PR_CallOnce(&pr_wp.once, _MD_InitProcesses) == PR_FAILURE) {
+ return NULL;
+ }
+ return ForkAndExec(path, argv, envp, attr);
+} /* _MD_CreateUnixProcess */
+
+#endif /* _PR_SHARE_CLONES */
+
+/*
+ * The pid table is a hashtable.
+ *
+ * The number of buckets in the hashtable (NBUCKETS) must be a power of 2.
+ */
+#define NBUCKETS_LOG2 6
+#define NBUCKETS (1 << NBUCKETS_LOG2)
+#define PID_HASH_MASK ((pid_t) (NBUCKETS - 1))
+
+static pr_PidRecord *
+FindPidTable(pid_t pid)
+{
+ pr_PidRecord *pRec;
+ int keyHash = (int) (pid & PID_HASH_MASK);
+
+ pRec = pr_wp.pidTable[keyHash];
+ while (pRec) {
+ if (pRec->pid == pid) {
+ break;
+ }
+ pRec = pRec->next;
+ }
+ return pRec;
+}
+
+static void
+InsertPidTable(pr_PidRecord *pRec)
+{
+ int keyHash = (int) (pRec->pid & PID_HASH_MASK);
+
+ pRec->next = pr_wp.pidTable[keyHash];
+ pr_wp.pidTable[keyHash] = pRec;
+}
+
+static void
+DeletePidTable(pr_PidRecord *pRec)
+{
+ int keyHash = (int) (pRec->pid & PID_HASH_MASK);
+
+ if (pr_wp.pidTable[keyHash] == pRec) {
+ pr_wp.pidTable[keyHash] = pRec->next;
+ } else {
+ pr_PidRecord *pred, *cur; /* predecessor and current */
+
+ pred = pr_wp.pidTable[keyHash];
+ cur = pred->next;
+ while (cur) {
+ if (cur == pRec) {
+ pred->next = cur->next;
+ break;
+ }
+ pred = cur;
+ cur = cur->next;
+ }
+ PR_ASSERT(cur != NULL);
+ }
+}
+
+static int
+ExtractExitStatus(int rawExitStatus)
+{
+ /*
+ * We did not specify the WCONTINUED and WUNTRACED options
+ * for waitpid, so these two events should not be reported.
+ */
+ PR_ASSERT(!WIFSTOPPED(rawExitStatus));
+#ifdef WIFCONTINUED
+ PR_ASSERT(!WIFCONTINUED(rawExitStatus));
+#endif
+ if (WIFEXITED(rawExitStatus)) {
+ return WEXITSTATUS(rawExitStatus);
+ } else {
+ PR_ASSERT(WIFSIGNALED(rawExitStatus));
+ return _PR_SIGNALED_EXITSTATUS;
+ }
+}
+
+static void
+ProcessReapedChildInternal(pid_t pid, int status)
+{
+ pr_PidRecord *pRec;
+
+ pRec = FindPidTable(pid);
+ if (NULL == pRec) {
+ pRec = PR_NEW(pr_PidRecord);
+ pRec->pid = pid;
+ pRec->state = _PR_PID_REAPED;
+ pRec->exitStatus = ExtractExitStatus(status);
+ pRec->reapedCV = NULL;
+ InsertPidTable(pRec);
+ } else {
+ PR_ASSERT(pRec->state != _PR_PID_REAPED);
+ if (_PR_PID_DETACHED == pRec->state) {
+ PR_ASSERT(NULL == pRec->reapedCV);
+ DeletePidTable(pRec);
+ PR_DELETE(pRec);
+ } else {
+ PR_ASSERT(_PR_PID_WAITING == pRec->state);
+ PR_ASSERT(NULL != pRec->reapedCV);
+ pRec->exitStatus = ExtractExitStatus(status);
+ pRec->state = _PR_PID_REAPED;
+ PR_NotifyCondVar(pRec->reapedCV);
+ }
+ }
+}
+
+#if defined(_PR_NATIVE_THREADS)
+
+/*
+ * If all the threads are native threads, the daemon thread is
+ * simpler. We don't need to catch the SIGCHLD signal. We can
+ * just have the daemon thread block in waitpid().
+ */
+
+static void WaitPidDaemonThread(void *unused)
+{
+ pid_t pid;
+ int status;
+
+ while (1) {
+ PR_Lock(pr_wp.ml);
+ while (0 == pr_wp.numProcs) {
+ PR_WaitCondVar(pr_wp.cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(pr_wp.ml);
+
+ while (1) {
+ do {
+ pid = waitpid((pid_t) -1, &status, 0);
+ } while ((pid_t) -1 == pid && EINTR == errno);
+
+ /*
+ * waitpid() cannot return 0 because we did not invoke it
+ * with the WNOHANG option.
+ */
+ PR_ASSERT(0 != pid);
+
+ /*
+ * The only possible error code is ECHILD. But if we do
+ * our accounting correctly, we should only call waitpid()
+ * when there is a child process to wait for.
+ */
+ PR_ASSERT((pid_t) -1 != pid);
+ if ((pid_t) -1 == pid) {
+ break;
+ }
+
+ PR_Lock(pr_wp.ml);
+ ProcessReapedChildInternal(pid, status);
+ pr_wp.numProcs--;
+ while (0 == pr_wp.numProcs) {
+ PR_WaitCondVar(pr_wp.cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(pr_wp.ml);
+ }
+ }
+}
+
+#else /* _PR_NATIVE_THREADS */
+
+static void WaitPidDaemonThread(void *unused)
+{
+ PRPollDesc pd;
+ PRFileDesc *fd;
+ int rv;
+ char buf[128];
+ pid_t pid;
+ int status;
+#ifdef _PR_SHARE_CLONES
+ struct pr_CreateProcOp *op;
+#endif
+
+#ifdef _PR_SHARE_CLONES
+ pr_InstallSigchldHandler();
+#endif
+
+ fd = PR_ImportFile(pr_wp.pipefd[0]);
+ PR_ASSERT(NULL != fd);
+ pd.fd = fd;
+ pd.in_flags = PR_POLL_READ;
+
+ while (1) {
+ rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(1 == rv);
+
+#ifdef _PR_SHARE_CLONES
+ if (pr_waitpid_daemon_exit) {
+ return;
+ }
+ PR_Lock(pr_wp.ml);
+#endif
+
+ do {
+ rv = read(pr_wp.pipefd[0], buf, sizeof(buf));
+ } while (sizeof(buf) == rv || (-1 == rv && EINTR == errno));
+
+#ifdef _PR_SHARE_CLONES
+ PR_Unlock(pr_wp.ml);
+ while ((op = pr_wp.opHead) != NULL) {
+ op->process = ForkAndExec(op->path, op->argv,
+ op->envp, op->attr);
+ if (NULL == op->process) {
+ op->prerror = PR_GetError();
+ op->oserror = PR_GetOSError();
+ }
+ PR_Lock(pr_wp.ml);
+ pr_wp.opHead = op->next;
+ if (NULL == pr_wp.opHead) {
+ pr_wp.opTail = NULL;
+ }
+ op->done = PR_TRUE;
+ PR_NotifyCondVar(op->doneCV);
+ PR_Unlock(pr_wp.ml);
+ }
+#endif
+
+ while (1) {
+ do {
+ pid = waitpid((pid_t) -1, &status, WNOHANG);
+ } while ((pid_t) -1 == pid && EINTR == errno);
+ if (0 == pid) break;
+ if ((pid_t) -1 == pid) {
+ /* must be because we have no child processes */
+ PR_ASSERT(ECHILD == errno);
+ break;
+ }
+
+ PR_Lock(pr_wp.ml);
+ ProcessReapedChildInternal(pid, status);
+ PR_Unlock(pr_wp.ml);
+ }
+ }
+}
+
+static void pr_SigchldHandler(int sig)
+{
+ int errnoCopy;
+ int rv;
+
+ errnoCopy = errno;
+
+ do {
+ rv = write(pr_wp.pipefd[1], "", 1);
+ } while (-1 == rv && EINTR == errno);
+
+#ifdef DEBUG
+ if (-1 == rv && EAGAIN != errno && EWOULDBLOCK != errno) {
+ char *msg = "cannot write to pipe\n";
+ write(2, msg, strlen(msg) + 1);
+ _exit(1);
+ }
+#endif
+
+ errno = errnoCopy;
+}
+
+static void pr_InstallSigchldHandler()
+{
+#if defined(HPUX) && defined(_PR_DCETHREADS)
+#error "HP-UX DCE threads have their own SIGCHLD handler"
+#endif
+
+ struct sigaction act, oact;
+ int rv;
+
+ act.sa_handler = pr_SigchldHandler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_NOCLDSTOP | SA_RESTART;
+ rv = sigaction(SIGCHLD, &act, &oact);
+ PR_ASSERT(0 == rv);
+ /* Make sure we are not overriding someone else's SIGCHLD handler */
+#ifndef _PR_SHARE_CLONES
+ PR_ASSERT(oact.sa_handler == SIG_DFL);
+#endif
+}
+
+#endif /* !defined(_PR_NATIVE_THREADS) */
+
+static PRStatus _MD_InitProcesses()
+{
+#if !defined(_PR_NATIVE_THREADS)
+ int rv;
+ int flags;
+#endif
+#ifdef SUNOS4
+#define _PR_NBIO_FLAG FNDELAY
+#else
+#define _PR_NBIO_FLAG O_NONBLOCK
+#endif
+
+ pr_wp.ml = PR_NewLock();
+ PR_ASSERT(NULL != pr_wp.ml);
+
+#if defined(_PR_NATIVE_THREADS)
+ pr_wp.numProcs = 0;
+ pr_wp.cv = PR_NewCondVar(pr_wp.ml);
+ PR_ASSERT(NULL != pr_wp.cv);
+#else
+ rv = pipe(pr_wp.pipefd);
+ PR_ASSERT(0 == rv);
+ flags = fcntl(pr_wp.pipefd[0], F_GETFL, 0);
+ fcntl(pr_wp.pipefd[0], F_SETFL, flags | _PR_NBIO_FLAG);
+ flags = fcntl(pr_wp.pipefd[1], F_GETFL, 0);
+ fcntl(pr_wp.pipefd[1], F_SETFL, flags | _PR_NBIO_FLAG);
+
+#ifndef _PR_SHARE_CLONES
+ pr_InstallSigchldHandler();
+#endif
+#endif /* !_PR_NATIVE_THREADS */
+
+ pr_wp.thread = PR_CreateThread(PR_SYSTEM_THREAD,
+ WaitPidDaemonThread, NULL, PR_PRIORITY_NORMAL,
+#ifdef _PR_SHARE_CLONES
+ PR_GLOBAL_THREAD,
+#else
+ PR_LOCAL_THREAD,
+#endif
+ PR_JOINABLE_THREAD, 0);
+ PR_ASSERT(NULL != pr_wp.thread);
+
+ pr_wp.pidTable = (pr_PidRecord**)PR_CALLOC(NBUCKETS * sizeof(pr_PidRecord *));
+ PR_ASSERT(NULL != pr_wp.pidTable);
+ return PR_SUCCESS;
+}
+
+PRStatus _MD_DetachUnixProcess(PRProcess *process)
+{
+ PRStatus retVal = PR_SUCCESS;
+ pr_PidRecord *pRec;
+
+ PR_Lock(pr_wp.ml);
+ pRec = FindPidTable(process->md.pid);
+ if (NULL == pRec) {
+ pRec = PR_NEW(pr_PidRecord);
+ if (NULL == pRec) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ retVal = PR_FAILURE;
+ goto done;
+ }
+ pRec->pid = process->md.pid;
+ pRec->state = _PR_PID_DETACHED;
+ pRec->reapedCV = NULL;
+ InsertPidTable(pRec);
+ } else {
+ PR_ASSERT(_PR_PID_REAPED == pRec->state);
+ if (_PR_PID_REAPED != pRec->state) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ retVal = PR_FAILURE;
+ } else {
+ DeletePidTable(pRec);
+ PR_ASSERT(NULL == pRec->reapedCV);
+ PR_DELETE(pRec);
+ }
+ }
+
+done:
+ PR_Unlock(pr_wp.ml);
+ return retVal;
+}
+
+PRStatus _MD_WaitUnixProcess(
+ PRProcess *process,
+ PRInt32 *exitCode)
+{
+ pr_PidRecord *pRec;
+ PRStatus retVal = PR_SUCCESS;
+ PRBool interrupted = PR_FALSE;
+
+ PR_Lock(pr_wp.ml);
+ pRec = FindPidTable(process->md.pid);
+ if (NULL == pRec) {
+ pRec = PR_NEW(pr_PidRecord);
+ if (NULL == pRec) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ retVal = PR_FAILURE;
+ goto done;
+ }
+ pRec->pid = process->md.pid;
+ pRec->state = _PR_PID_WAITING;
+ pRec->reapedCV = PR_NewCondVar(pr_wp.ml);
+ if (NULL == pRec->reapedCV) {
+ PR_DELETE(pRec);
+ retVal = PR_FAILURE;
+ goto done;
+ }
+ InsertPidTable(pRec);
+ while (!interrupted && _PR_PID_REAPED != pRec->state) {
+ if (PR_WaitCondVar(pRec->reapedCV,
+ PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE
+ && PR_GetError() == PR_PENDING_INTERRUPT_ERROR) {
+ interrupted = PR_TRUE;
+ }
+ }
+ if (_PR_PID_REAPED == pRec->state) {
+ if (exitCode) {
+ *exitCode = pRec->exitStatus;
+ }
+ } else {
+ PR_ASSERT(interrupted);
+ retVal = PR_FAILURE;
+ }
+ DeletePidTable(pRec);
+ PR_DestroyCondVar(pRec->reapedCV);
+ PR_DELETE(pRec);
+ } else {
+ PR_ASSERT(_PR_PID_REAPED == pRec->state);
+ PR_ASSERT(NULL == pRec->reapedCV);
+ DeletePidTable(pRec);
+ if (exitCode) {
+ *exitCode = pRec->exitStatus;
+ }
+ PR_DELETE(pRec);
+ }
+
+done:
+ PR_Unlock(pr_wp.ml);
+ return retVal;
+} /* _MD_WaitUnixProcess */
+
+PRStatus _MD_KillUnixProcess(PRProcess *process)
+{
+ PRErrorCode prerror;
+ PRInt32 oserror;
+
+ if (kill(process->md.pid, SIGKILL) == 0) {
+ return PR_SUCCESS;
+ }
+ oserror = errno;
+ switch (oserror) {
+ case EPERM:
+ prerror = PR_NO_ACCESS_RIGHTS_ERROR;
+ break;
+ case ESRCH:
+ prerror = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ default:
+ prerror = PR_UNKNOWN_ERROR;
+ break;
+ }
+ PR_SetError(prerror, oserror);
+ return PR_FAILURE;
+} /* _MD_KillUnixProcess */
diff --git a/pr/src/md/unix/uxwrap.c b/pr/src/md/unix/uxwrap.c
new file mode 100644
index 00000000..9fc0fd3d
--- /dev/null
+++ b/pr/src/md/unix/uxwrap.c
@@ -0,0 +1,518 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ *------------------------------------------------------------------------
+ * File: uxwrap.c
+ *
+ * Our wrapped versions of the Unix select() and poll() system calls.
+ *
+ *------------------------------------------------------------------------
+ */
+
+#include "primpl.h"
+
+#if defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY)
+/* Do not wrap select() and poll(). */
+#else /* defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) */
+/* The include files for select() */
+#ifdef IRIX
+#include <unistd.h>
+#include <bstring.h>
+#endif
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#define ZAP_SET(_to, _width) \
+ PR_BEGIN_MACRO \
+ memset(_to, 0, \
+ ((_width + 8*sizeof(int)-1) / (8*sizeof(int))) \
+ * sizeof(int) \
+ ); \
+ PR_END_MACRO
+
+#define COPY_SET(_to, _from, _width) \
+ PR_BEGIN_MACRO \
+ memcpy(_to, _from, \
+ ((_width + 8*sizeof(int)-1) / (8*sizeof(int))) \
+ * sizeof(int) \
+ ); \
+ PR_END_MACRO
+
+/* An internal global variable defined in prfile.c */
+extern PRIOMethods _pr_fileMethods;
+
+
+/* see comments in ns/cmd/xfe/mozilla.c (look for "PR_XGetXtHackFD") */
+static int _pr_xt_hack_fd = -1;
+
+int PR_XGetXtHackFD(void)
+{
+ int fds[2];
+
+ if (_pr_xt_hack_fd == -1) {
+ if (!pipe(fds)) {
+ _pr_xt_hack_fd = fds[0];
+ }
+ }
+ return _pr_xt_hack_fd;
+ }
+
+static int (*_pr_xt_hack_okayToReleaseXLock)(void) = 0;
+
+void PR_SetXtHackOkayToReleaseXLockFn(int (*fn)(void))
+{
+ _pr_xt_hack_okayToReleaseXLock = fn;
+}
+
+
+/*
+ *-----------------------------------------------------------------------
+ * select() --
+ *
+ * Wrap up the select system call so that we can deschedule
+ * a thread that tries to wait for i/o.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+#if defined(HPUX9)
+int select(size_t width, int *rl, int *wl, int *el, const struct timeval *tv)
+#elif defined(AIX4_1)
+int wrap_select(unsigned long width, void *rl, void *wl, void *el,
+ struct timeval *tv)
+#elif (defined(BSDI) && !defined(BSDI_2))
+int select(int width, fd_set *rd, fd_set *wr, fd_set *ex,
+ const struct timeval *tv)
+#else
+int select(int width, fd_set *rd, fd_set *wr, fd_set *ex, struct timeval *tv)
+#endif
+{
+ int i;
+ int nfds;
+ int npds;
+ void *pollset;
+ PRPollDesc *pd;
+ PRFileDesc *prfd;
+ PRFilePrivate *secret;
+ PRIntervalTime timeout;
+ int retVal;
+#if defined(HPUX9) || defined(AIX4_1)
+ fd_set *rd = (fd_set*) rl;
+ fd_set *wr = (fd_set*) wl;
+ fd_set *ex = (fd_set*) el;
+#endif
+ fd_set r, w, x;
+
+#if 0
+ /*
+ * Easy special case: zero timeout. Simply call the native
+ * select() with no fear of blocking.
+ */
+ if (tv != NULL && tv->tv_sec == 0 && tv->tv_usec == 0) {
+#if defined(HPUX9) || defined(AIX4_1)
+ return _MD_SELECT(width, rl, wl, el, tv);
+#else
+ return _MD_SELECT(width, rd, wr, ex, tv);
+#endif
+ }
+#endif
+
+ if (!_pr_initialized)
+ _PR_ImplicitInitialization();
+
+#ifndef _PR_LOCAL_THREADS_ONLY
+ if (_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) {
+ nfds = _MD_SELECT(width, rd, wr, ex, tv);
+ return(nfds);
+ }
+#endif
+
+ if (width < 0 || width > FD_SETSIZE) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Compute timeout */
+ if (tv) {
+ /*
+ * These acceptable ranges for t_sec and t_usec are taken
+ * from the select() man pages.
+ */
+ if (tv->tv_sec < 0 || tv->tv_sec > 100000000
+ || tv->tv_usec < 0 || tv->tv_usec >= 1000000) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Convert microseconds to ticks */
+ timeout = PR_MicrosecondsToInterval(1000000*tv->tv_sec + tv->tv_usec);
+ } else {
+ /* tv being a NULL pointer means blocking indefinitely */
+ timeout = PR_INTERVAL_NO_TIMEOUT;
+ }
+
+ /* Check for no descriptors case (just doing a timeout) */
+ if ((!rd && !wr && !ex) || !width) {
+ PR_Sleep(timeout);
+ return 0;
+ }
+
+ if (rd) { COPY_SET(&r, rd, width); }
+ if (wr) { COPY_SET(&w, wr, width); }
+ if (ex) { COPY_SET(&x, ex, width); }
+
+ /*
+ * Set up for PR_Poll(). The PRPollDesc array is allocated
+ * dynamically. If this turns out to have high performance
+ * penalty, one can change to use a large PRPollDesc array
+ * on the stack, and allocate dynamically only when it turns
+ * out to be not large enough.
+ *
+ * I allocate an array of size 'width', which is the maximum
+ * number of fds we may need to poll.
+ */
+ pollset = PR_CALLOC(width *
+ (sizeof(PRPollDesc) + sizeof(PRFileDesc) + sizeof(PRFilePrivate)));
+ if (!pollset) {
+ errno = ENOMEM;
+ return -1;
+ }
+ pd = (PRPollDesc*)pollset;
+ prfd = (PRFileDesc*)(&pd[width]);
+ secret = (PRFilePrivate*)(&prfd[width]);
+
+ for (npds = 0, i = 0; i < width; i++) {
+ int in_flags = 0;
+ if (rd && FD_ISSET(i, &r)) {
+ in_flags |= PR_POLL_READ;
+ }
+ if (wr && FD_ISSET(i, &w)) {
+ in_flags |= PR_POLL_WRITE;
+ }
+ if (ex && FD_ISSET(i, &x)) {
+ in_flags |= PR_POLL_EXCEPT;
+ }
+ if (in_flags) {
+ prfd[npds].secret = &secret[npds];
+ prfd[npds].secret->state = _PR_FILEDESC_OPEN;
+ prfd[npds].secret->md.osfd = i;
+ prfd[npds].methods = &_pr_fileMethods;
+
+ pd[npds].fd = &prfd[npds];
+ pd[npds].in_flags = in_flags;
+ pd[npds].out_flags = 0;
+ npds += 1;
+ }
+ }
+
+ /* see comments in ns/cmd/xfe/mozilla.c (look for "PR_XGetXtHackFD") */
+ {
+
+ int needToLockXAgain;
+
+ needToLockXAgain = 0;
+ if (rd && (_pr_xt_hack_fd != -1) &&
+ FD_ISSET(_pr_xt_hack_fd, &r) && PR_XIsLocked() &&
+ (!_pr_xt_hack_okayToReleaseXLock || _pr_xt_hack_okayToReleaseXLock())) {
+ PR_XUnlock();
+ needToLockXAgain = 1;
+ }
+
+ /* This is the potentially blocking step */
+ retVal = PR_Poll(pd, npds, timeout);
+
+ if (needToLockXAgain) {
+ PR_XLock();
+ }
+ }
+
+ if (retVal > 0)
+ {
+ /* Compute select results */
+ if (rd) ZAP_SET(rd, width);
+ if (wr) ZAP_SET(wr, width);
+ if (ex) ZAP_SET(ex, width);
+
+ /*
+ * The return value can be either the number of ready file
+ * descriptors or the number of set bits in the three fd_set's.
+ */
+ retVal = 0; /* we're going to recompute */
+ for (i = 0; i < npds; ++i, pd++)
+ {
+ if (pd->out_flags) {
+ int nbits = 0; /* The number of set bits on for this fd */
+
+ if (pd->out_flags & PR_POLL_NVAL) {
+ errno = EBADF;
+ PR_LOG(_pr_io_lm, PR_LOG_ERROR,
+ ("select returns EBADF for %d", pd->fd));
+ retVal = -1;
+ break;
+ }
+ if (rd && (pd->out_flags & PR_POLL_READ)) {
+ FD_SET(pd->fd->secret->md.osfd, rd);
+ nbits++;
+ }
+ if (wr && (pd->out_flags & PR_POLL_WRITE)) {
+ FD_SET(pd->fd->secret->md.osfd, wr);
+ nbits++;
+ }
+ if (ex && (pd->out_flags & PR_POLL_EXCEPT)) {
+ FD_SET(pd->fd->secret->md.osfd, ex);
+ nbits++;
+ }
+ PR_ASSERT(nbits > 0);
+#if defined(HPUX) || defined(SOLARIS) || defined(SUNOS4) || defined(OSF1) || defined(AIX)
+ retVal += nbits;
+#else /* IRIX */
+ retVal += 1;
+#endif
+ }
+ }
+ }
+
+ PR_ASSERT(tv || retVal != 0);
+ PR_LOG(_pr_io_lm, PR_LOG_MIN, ("select returns %d", retVal));
+ PR_DELETE(pollset);
+
+ return retVal;
+}
+
+/*
+ * Linux, BSDI, and FreeBSD don't have poll()
+ */
+
+#if !defined(LINUX) && !defined(FREEBSD) && !defined(BSDI)
+
+/*
+ *-----------------------------------------------------------------------
+ * poll() --
+ *
+ * RETURN VALUES:
+ * -1: fails, errno indicates the error.
+ * 0: timed out, the revents bitmasks are not set.
+ * positive value: the number of file descriptors for which poll()
+ * has set the revents bitmask.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+#include <poll.h>
+
+#if defined(AIX4_1)
+int wrap_poll(void *listptr, unsigned long nfds, long timeout)
+#elif (defined(AIX) && !defined(AIX4_1))
+int poll(void *listptr, unsigned long nfds, long timeout)
+#elif defined(OSF1) || (defined(HPUX) && !defined(HPUX9))
+int poll(struct pollfd filedes[], unsigned int nfds, int timeout)
+#elif defined(HPUX9)
+int poll(struct pollfd filedes[], int nfds, int timeout)
+#else
+int poll(struct pollfd *filedes, unsigned long nfds, int timeout)
+#endif
+{
+#ifdef AIX
+ struct pollfd *filedes = (struct pollfd *) listptr;
+#endif
+ void *pollset;
+ PRPollDesc *pd;
+ PRFileDesc *prfd;
+ PRFilePrivate *secret;
+ int i;
+ PRUint32 ticks;
+ PRInt32 retVal;
+
+ /*
+ * Easy special case: zero timeout. Simply call the native
+ * poll() with no fear of blocking.
+ */
+ if (timeout == 0) {
+#if defined(AIX)
+ return _MD_POLL(listptr, nfds, timeout);
+#else
+ return _MD_POLL(filedes, nfds, timeout);
+#endif
+ }
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+#ifndef _PR_LOCAL_THREADS_ONLY
+ if (_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) {
+ retVal = _MD_POLL(filedes, nfds, timeout);
+ return(retVal);
+ }
+#endif
+
+ /* We do not support the pollmsg structures on AIX */
+#ifdef AIX
+ PR_ASSERT((nfds & 0xff00) == 0);
+#endif
+
+ if (timeout < 0 && timeout != -1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Convert timeout from miliseconds to ticks */
+ if (timeout == -1) {
+ ticks = PR_INTERVAL_NO_TIMEOUT;
+ } else if (timeout == 0) {
+ ticks = PR_INTERVAL_NO_WAIT;
+ } else {
+ ticks = PR_MillisecondsToInterval(timeout);
+ }
+
+ /* Check for no descriptor case (just do a timeout) */
+ if (nfds == 0) {
+ PR_Sleep(ticks);
+ return 0;
+ }
+
+ pollset = PR_CALLOC(nfds *
+ (sizeof(PRPollDesc) + sizeof(PRFileDesc) + sizeof(PRFilePrivate)));
+ if (!pollset) {
+ errno = EAGAIN;
+ return -1;
+ }
+ pd = (PRPollDesc*)pollset;
+ prfd = (PRFileDesc*)(&pd[nfds]);
+ secret = (PRFilePrivate*)(&prfd[nfds]);
+
+ for (i = 0; i < nfds; i++) {
+ prfd[i].secret = &secret[i];
+ prfd[i].secret->state = _PR_FILEDESC_OPEN;
+ prfd[i].secret->md.osfd = filedes[i].fd;
+ prfd[i].methods = &_pr_fileMethods;
+
+ pd[i].fd = &prfd[i];
+ pd[i].out_flags = 0;
+
+ /*
+ * poll() ignores negative fd's. We emulate this behavior
+ * by making sure the in_flags for a negative fd is zero.
+ */
+ if (filedes[i].fd < 0) {
+ pd[i].in_flags = 0;
+ continue;
+ }
+#ifdef _PR_USE_POLL
+ pd[i].in_flags = filedes[i].events;
+#else
+ /*
+ * Map the native poll flags to nspr20 poll flags.
+ * POLLIN, POLLRDNORM ===> PR_POLL_READ
+ * POLLOUT, POLLWRNORM ===> PR_POLL_WRITE
+ * POLLPRI, POLLRDBAND ===> PR_POLL_EXCEPT
+ * POLLNORM, POLLWRBAND (and POLLMSG on some platforms)
+ * are ignored.
+ *
+ * The output events POLLERR and POLLHUP are never turned on.
+ * POLLNVAL may be turned on.
+ */
+ pd[i].in_flags = 0;
+ if (filedes[i].events & (POLLIN
+#ifdef POLLRDNORM
+ | POLLRDNORM
+#endif
+ )) {
+ pd[i].in_flags |= PR_POLL_READ;
+ }
+ if (filedes[i].events & (POLLOUT
+#ifdef POLLWRNORM
+ | POLLWRNORM
+#endif
+ )) {
+ pd[i].in_flags |= PR_POLL_WRITE;
+ }
+ if (filedes[i].events & (POLLPRI
+#ifdef POLLRDBAND
+ | POLLRDBAND
+#endif
+ )) {
+ pd[i].in_flags |= PR_POLL_EXCEPT;
+ }
+#endif /* _PR_USE_POLL */
+ }
+
+ retVal = PR_Poll(pd, nfds, ticks);
+
+ if (retVal > 0) {
+ /* Set the revents bitmasks */
+ for (i = 0; i < nfds; i++) {
+ PR_ASSERT(filedes[i].fd >= 0 || pd[i].in_flags == 0);
+ if (filedes[i].fd < 0) {
+ continue; /* skip negative fd's */
+ }
+#ifdef _PR_USE_POLL
+ filedes[i].revents = pd[i].out_flags;
+#else
+ filedes[i].revents = 0;
+ if (0 == pd[i].out_flags) {
+ continue;
+ }
+ if (pd[i].out_flags & PR_POLL_READ) {
+ if (filedes[i].events & POLLIN)
+ filedes[i].revents |= POLLIN;
+#ifdef POLLRDNORM
+ if (filedes[i].events & POLLRDNORM)
+ filedes[i].revents |= POLLRDNORM;
+#endif
+ }
+ if (pd[i].out_flags & PR_POLL_WRITE) {
+ if (filedes[i].events & POLLOUT)
+ filedes[i].revents |= POLLOUT;
+#ifdef POLLWRNORM
+ if (filedes[i].events & POLLWRNORM)
+ filedes[i].revents |= POLLWRNORM;
+#endif
+ }
+ if (pd[i].out_flags & PR_POLL_EXCEPT) {
+ if (filedes[i].events & POLLPRI)
+ filedes[i].revents |= POLLPRI;
+#ifdef POLLRDBAND
+ if (filedes[i].events & POLLRDBAND)
+ filedes[i].revents |= POLLRDBAND;
+#endif
+ }
+ if (pd[i].out_flags & PR_POLL_ERR) {
+ filedes[i].revents |= POLLERR;
+ }
+ if (pd[i].out_flags & PR_POLL_NVAL) {
+ filedes[i].revents |= POLLNVAL;
+ }
+#endif /* _PR_USE_POLL */
+ }
+ }
+
+ PR_DELETE(pollset);
+
+ return retVal;
+}
+
+#endif /* !defined(LINUX) */
+
+#endif /* defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) */
+
+/* uxwrap.c */
+
diff --git a/pr/src/md/windows/Makefile b/pr/src/md/windows/Makefile
new file mode 100644
index 00000000..eaac7b56
--- /dev/null
+++ b/pr/src/md/windows/Makefile
@@ -0,0 +1,75 @@
+#
+# The contents of this file are subject to the Netscape Public License
+# Version 1.0 (the "NPL"); you may not use this file except in
+# compliance with the NPL. You may obtain a copy of the NPL at
+# http://www.mozilla.org/NPL/
+#
+# Software distributed under the NPL is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+# for the specific language governing rights and limitations under the
+# NPL.
+#
+# The Initial Developer of this code under the NPL is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1998 Netscape Communications Corporation. All Rights
+# Reserved.
+#
+
+#! gmake
+
+MOD_DEPTH = ../../../..
+
+include $(MOD_DEPTH)/config/config.mk
+
+ifeq ($(OS_TARGET), WIN16)
+CSRCS = \
+ w16null.c \
+ w16thred.c \
+ w16proc.c \
+ w16fmem.c \
+ w16sock.c \
+ w16mem.c \
+ w16io.c \
+ w16gc.c \
+ w16error.c \
+ w16stdio.c \
+ w16callb.c \
+ ntinrval.c \
+ $(NULL)
+else
+ifeq ($(OS_TARGET), WIN95)
+CSRCS = \
+ ntmisc.c \
+ ntsem.c \
+ ntinrval.c \
+ ntgc.c \
+ w95thred.c \
+ w95io.c \
+ w95cv.c \
+ w95sock.c \
+ win32_errors.c \
+ w32poll.c \
+ $(NULL)
+else
+CSRCS = \
+ ntmisc.c \
+ ntsem.c \
+ ntinrval.c \
+ ntgc.c \
+ ntthread.c \
+ ntio.c \
+ win32_errors.c \
+ w32poll.c \
+ $(NULL)
+endif
+endif
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(DIST)/include/private -I$(DIST)/include
+
+include $(MOD_DEPTH)/config/rules.mk
+
+export:: $(TARGETS)
+
+install:: export
diff --git a/pr/src/md/windows/ntgc.c b/pr/src/md/windows/ntgc.c
new file mode 100644
index 00000000..8bc21dba
--- /dev/null
+++ b/pr/src/md/windows/ntgc.c
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * GC related routines
+ *
+ */
+#include <windows.h>
+#include "primpl.h"
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+#if defined(_X86_)
+ CONTEXT context;
+ context.ContextFlags = CONTEXT_INTEGER;
+
+ if (_PR_IS_NATIVE_THREAD(t)) {
+ context.ContextFlags |= CONTEXT_CONTROL;
+ if (GetThreadContext(t->md.handle, &context)) {
+ t->md.gcContext[0] = context.Eax;
+ t->md.gcContext[1] = context.Ebx;
+ t->md.gcContext[2] = context.Ecx;
+ t->md.gcContext[3] = context.Edx;
+ t->md.gcContext[4] = context.Esi;
+ t->md.gcContext[5] = context.Edi;
+ t->md.gcContext[6] = context.Esp;
+ t->md.gcContext[7] = context.Ebp;
+ *np = PR_NUM_GCREGS;
+ } else {
+ PR_ASSERT(0);/* XXX */
+ }
+ } else {
+ /* WARNING WARNING WARNING WARNING WARNING WARNING WARNING
+ *
+ * This code is extremely machine dependant and completely
+ * undocumented by MS. Its only known to work experimentally.
+ * Ready for a walk on the wild * side?
+ *
+ * WARNING WARNING WARNING WARNING WARNING WARNING WARNING */
+
+#if !defined WIN95 // Win95 does not have fibers
+ int *fiberData = t->md.fiber_id;
+
+ /* I found these offsets by disassembling SwitchToFiber().
+ * Are your palms sweating yet?
+ */
+
+ /*
+ ** EAX is on the stack (ESP+0)
+ ** EDX is on the stack (ESP+4)
+ ** ECX is on the stack (ESP+8)
+ */
+ t->md.gcContext[0] = 0; /* context.Eax */
+ t->md.gcContext[1] = fiberData[0x2e]; /* context.Ebx */
+ t->md.gcContext[2] = 0; /* context.Ecx */
+ t->md.gcContext[2] = 0; /* context.Edx */
+ t->md.gcContext[4] = fiberData[0x2d]; /* context.Esi */
+ t->md.gcContext[5] = fiberData[0x2c]; /* context.Edi */
+ t->md.gcContext[6] = fiberData[0x36]; /* context.Esp */
+ t->md.gcContext[7] = fiberData[0x32]; /* context.Ebp */
+ *np = PR_NUM_GCREGS;
+#endif
+ }
+ return (PRWord *)&t->md.gcContext;
+#elif defined(_ALPHA_)
+#endif /* defined(_X86_) */
+}
+
+/* This function is not used right now, but is left as a reference.
+ * If you ever need to get the fiberID from the currently running fiber,
+ * this is it.
+ */
+void *
+GetMyFiberID()
+{
+#if defined(_X86_)
+ void *fiberData;
+
+ /* A pointer to our tib entry is found at FS:[18]
+ * At offset 10h is the fiberData pointer. The context of the
+ * fiber is stored in there.
+ */
+ __asm {
+ mov EDX, FS:[18h]
+ mov EAX, DWORD PTR [EDX+10h]
+ mov [fiberData], EAX
+ }
+
+ return fiberData;
+#elif defined(_ALPHA_)
+#endif /* defined(_X86_) */
+}
diff --git a/pr/src/md/windows/ntinrval.c b/pr/src/md/windows/ntinrval.c
new file mode 100644
index 00000000..05cc3ae1
--- /dev/null
+++ b/pr/src/md/windows/ntinrval.c
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * NT interval timers
+ *
+ */
+
+#include "primpl.h"
+
+#if defined(WIN16)
+#include <win/compobj.h>
+#define QueryPerformanceFrequency(x) FALSE
+#define QueryPerformanceCounter(x) FALSE
+#endif
+
+PRIntn _nt_bitShift = 0;
+PRInt32 _nt_highMask = 0;
+PRInt32 _nt_ticksPerSec = -1;
+
+void
+_PR_MD_INTERVAL_INIT()
+{
+ LARGE_INTEGER count;
+
+ if (QueryPerformanceFrequency(&count)) {
+ while(count.LowPart > PR_INTERVAL_MAX) {
+ count.LowPart >>= 1;
+ _nt_bitShift++;
+ _nt_highMask = (_nt_highMask << 1)+1;
+ }
+
+ _nt_ticksPerSec = count.LowPart;
+ PR_ASSERT(_nt_ticksPerSec > PR_INTERVAL_MIN);
+ } else
+ _nt_ticksPerSec = -1;
+}
+
+PRIntervalTime
+_PR_MD_GET_INTERVAL()
+{
+ LARGE_INTEGER count;
+
+ /* Sadly; nspr requires the interval to range from 1000 ticks per second
+ * to only 100000 ticks per second; QueryPerformanceCounter is too high
+ * resolution...
+ */
+ if (QueryPerformanceCounter(&count)) {
+ PRInt32 top = count.HighPart & _nt_highMask;
+ top = top << (32 - _nt_bitShift);
+ count.LowPart = count.LowPart >> _nt_bitShift;
+ count.LowPart = count.LowPart + top;
+ return (PRUint32)count.LowPart;
+ } else
+#if defined(WIN16)
+ return clock(); /* milliseconds since application start */
+#else
+ return timeGetTime(); /* milliseconds since system start */
+#endif
+}
+
+PRIntervalTime
+_PR_MD_INTERVAL_PER_SEC()
+{
+ LARGE_INTEGER count;
+
+ if (_nt_ticksPerSec != -1)
+ return _nt_ticksPerSec;
+ else
+ return 1000;
+}
diff --git a/pr/src/md/windows/ntio.c b/pr/src/md/windows/ntio.c
new file mode 100644
index 00000000..e9c5779c
--- /dev/null
+++ b/pr/src/md/windows/ntio.c
@@ -0,0 +1,3776 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/* Windows NT IO module
+ *
+ * This module handles IO for LOCAL_SCOPE and GLOBAL_SCOPE threads.
+ * For LOCAL_SCOPE threads, we're using NT fibers. For GLOBAL_SCOPE threads
+ * we're using NT-native threads.
+ *
+ * When doing IO, we want to use completion ports for optimal performance
+ * with fibers. But if we use completion ports for all IO, it is difficult
+ * to project a blocking model with GLOBAL_SCOPE threads. To handle this
+ * we create an extra thread for completing IO for GLOBAL_SCOPE threads.
+ * We don't really want to complete IO on a separate thread for LOCAL_SCOPE
+ * threads because it means extra context switches, which are really slow
+ * on NT... Since we're using a single completion port, some IO will
+ * be incorrectly completed on the GLOBAL_SCOPE IO thread; this will mean
+ * extra context switching; but I don't think there is anything I can do
+ * about it.
+ */
+
+#include "primpl.h"
+#include <direct.h>
+
+static HANDLE _pr_completion_port;
+static PRThread *_pr_io_completion_thread;
+
+#define RECYCLE_SIZE 512
+static struct _MDLock _pr_recycle_lock;
+static PRInt32 _pr_recycle_array[RECYCLE_SIZE];
+static PRInt32 _pr_recycle_tail = 0;
+
+__declspec(thread) PRThread *_pr_io_restarted_io = NULL;
+PRBool _nt_version_gets_lockfile_completion;
+
+struct _MDLock _pr_ioq_lock;
+extern _MDLock _nt_idleLock;
+extern PRCList _nt_idleList;
+extern PRUint32 _nt_idleCount;
+
+#define CLOSE_TIMEOUT PR_SecondsToInterval(5)
+
+/*
+ * The NSPR epoch (00:00:00 1 Jan 1970 UTC) in FILETIME.
+ * We store the value in a PRTime variable for convenience.
+ * This constant is used by _PR_FileTimeToPRTime().
+ */
+static const PRTime _pr_filetime_offset = 116444736000000000i64;
+
+#define _NEED_351_FILE_LOCKING_HACK
+#ifdef _NEED_351_FILE_LOCKING_HACK
+#define _PR_LOCAL_FILE 1
+#define _PR_REMOTE_FILE 2
+PRBool IsFileLocalInit();
+PRInt32 IsFileLocal(HANDLE hFile);
+#endif /* _NEED_351_FILE_LOCKING_HACK */
+
+static PRInt32 _md_Associate(HANDLE);
+static PRInt32 _md_MakeNonblock(HANDLE);
+
+/* The _nt_use_async flag is used to prevent nspr from using any async io.
+ * this is a temporary hack. Don't learn to rely on it.
+ */
+static int _nt_use_async = 1;
+PRInt32 _nt_nonblock_accept(PRFileDesc *fd, struct sockaddr_in *addr, int *len, PRIntervalTime);
+PRInt32 _nt_nonblock_recv(PRFileDesc *fd, char *buf, int len, PRIntervalTime);
+PRInt32 _nt_nonblock_send(PRFileDesc *fd, char *buf, int len, PRIntervalTime);
+PRInt32 _nt_nonblock_writev(PRFileDesc *fd, PRIOVec *iov, int size, PRIntervalTime);
+PRInt32 _nt_nonblock_sendto(PRFileDesc *, const char *, int, const struct sockaddr *, int, PRIntervalTime);
+PRInt32 _nt_nonblock_recvfrom(PRFileDesc *, char *, int, struct sockaddr *, int *, PRIntervalTime);
+
+/*
+ * UDP support
+ *
+ * UDP is supported on NT by the continuation thread mechanism.
+ * The code is borrowed from ptio.c in pthreads nspr, hence the
+ * PT and pt prefixes. This mechanism is in fact general and
+ * not limited to UDP. For now, only UDP's recvfrom and sendto
+ * go through the continuation thread if they get WSAEWOULDBLOCK
+ * on first try. Recv and send on a connected UDP socket still
+ * goes through asychronous io.
+ */
+
+#define PT_DEFAULT_SELECT_MSEC 100
+
+typedef struct pt_Continuation pt_Continuation;
+typedef PRBool (*ContinuationFn)(pt_Continuation *op, PRInt16 revent);
+
+typedef enum pr_ContuationStatus
+{
+ pt_continuation_sumbitted,
+ pt_continuation_inprogress,
+ pt_continuation_abort,
+ pt_continuation_done
+} pr_ContuationStatus;
+
+struct pt_Continuation
+{
+ /* These objects are linked in ascending timeout order */
+ pt_Continuation *next, *prev; /* self linked list of these things */
+
+ /* The building of the continuation operation */
+ ContinuationFn function; /* what function to continue */
+ union { SOCKET osfd; } arg1; /* #1 - the op's fd */
+ union { void* buffer; } arg2; /* #2 - primary transfer buffer */
+ union { PRIntn amount; } arg3; /* #3 - size of 'buffer' */
+ union { PRIntn flags; } arg4; /* #4 - read/write flags */
+ union { PRNetAddr *addr; } arg5; /* #5 - send/recv address */
+
+ PRIntervalTime timeout; /* representation of the timeout */
+
+ PRIntn event; /* flags for select()'s events */
+
+ /*
+ ** The representation and notification of the results of the operation.
+ ** These function can either return an int return code or a pointer to
+ ** some object.
+ */
+ union { PRIntn code; void *object; } result;
+
+ PRIntn syserrno; /* in case it failed, why (errno) */
+ pr_ContuationStatus status; /* the status of the operation */
+ PRCondVar *complete; /* to notify the initiating thread */
+};
+
+static struct pt_TimedQueue
+{
+ PRLock *ml; /* a little protection */
+ PRThread *thread; /* internal thread's identification */
+ PRCondVar *new_op; /* new operation supplied */
+ PRCondVar *finish_op; /* an existing operation finished */
+ PRUintn op_count; /* number of operations in the list */
+ pt_Continuation *head, *tail; /* head/tail of list of operations */
+
+ pt_Continuation *op; /* timed operation furthest in future */
+ PRIntervalTime epoch; /* the epoch of 'timed' */
+} pt_tq;
+
+#if defined(DEBUG)
+static struct pt_debug_s
+{
+ PRIntn predictionsFoiled;
+ PRIntn pollingListMax;
+ PRIntn continuationsServed;
+} pt_debug;
+#endif /* DEBUG */
+
+static void ContinuationThread(void *arg);
+static PRInt32 pt_SendTo(
+ SOCKET osfd, const void *buf,
+ PRInt32 amount, PRInt32 flags, const PRNetAddr *addr,
+ PRIntn addrlen, PRIntervalTime timeout);
+static PRInt32 pt_RecvFrom(SOCKET osfd, void *buf, PRInt32 amount,
+ PRInt32 flags, PRNetAddr *addr, PRIntn *addr_len, PRIntervalTime timeout);
+
+
+/* The key returned from GetQueuedCompletionStatus() is used to determine what
+ * type of completion we have. We differentiate between IO completions and
+ * CVAR completions.
+ */
+#define KEY_IO 0xaaaaaaaa
+#define KEY_CVAR 0xbbbbbbbb
+
+PRInt32
+_PR_MD_PAUSE_CPU(PRIntervalTime ticks)
+{
+ int awoken = 0;
+ unsigned long bytes, key;
+ int rv;
+ LPOVERLAPPED olp;
+ PRThread *completed_io;
+ PRUint32 timeout;
+
+ if (_nt_idleCount > 0) {
+ PRThread *deadThread;
+
+ _MD_LOCK(&_nt_idleLock);
+ while( !PR_CLIST_IS_EMPTY(&_nt_idleList) ) {
+ deadThread = _PR_THREAD_PTR(PR_LIST_HEAD(&_nt_idleList));
+ PR_REMOVE_LINK(&deadThread->links);
+
+ PR_ASSERT(deadThread->state == _PR_DEAD_STATE);
+
+ /* XXXMB - cleanup to do here? */
+ if ( !_PR_IS_NATIVE_THREAD(deadThread) ){
+ /* Spinlock while user thread is still running.
+ * There is no way to use a condition variable here. The thread
+ * is dead, and we have to wait until we switch off the dead
+ * thread before we can kill the fiber completely.
+ */
+ while ( deadThread->no_sched)
+ ;
+
+ DeleteFiber(deadThread->md.fiber_id);
+ }
+ memset(deadThread, 0xa, sizeof(PRThread)); /* debugging */
+ if (!deadThread->threadAllocatedOnStack)
+ PR_DELETE(deadThread);
+ _nt_idleCount--;
+ }
+ _MD_UNLOCK(&_nt_idleLock);
+ }
+
+ if (ticks == PR_INTERVAL_NO_TIMEOUT)
+#if 0
+ timeout = INFINITE;
+#else
+ /* temporary hack to poll the runq every 5 seconds because of bug in
+ * native threads creating user threads and not poking the right cpu.
+ */
+ timeout = 5000;
+#endif
+ else
+ timeout = PR_IntervalToMilliseconds(ticks);
+
+ /*
+ * The idea of looping here is to complete as many IOs as possible before
+ * returning. This should minimize trips to the idle thread.
+ */
+ while(1) {
+ rv = GetQueuedCompletionStatus(
+ _pr_completion_port,
+ &bytes,
+ &key,
+ &olp,
+ timeout);
+ if (rv == 0 && olp == NULL) {
+ /* Error in GetQueuedCompetionStatus */
+ if (GetLastError() != WAIT_TIMEOUT) {
+ /* ARGH - what can we do here? Log an error? XXXMB */
+ return -1;
+ } else {
+ /* If awoken == 0, then we just had a timeout */
+ return awoken;
+ }
+ }
+
+ if (olp == NULL)
+ return 0;
+
+ completed_io = _PR_THREAD_MD_TO_PTR(olp);
+ completed_io->md.blocked_io_status = rv;
+ if (rv == 0)
+ completed_io->md.blocked_io_error = GetLastError();
+ completed_io->md.blocked_io_bytes = bytes;
+
+ if ( !_PR_IS_NATIVE_THREAD(completed_io) ) {
+ int pri = completed_io->priority;
+ _PRCPU *lockedCPU = _PR_MD_CURRENT_CPU();
+
+ /* The KEY_CVAR notification only occurs when a native thread
+ * is notifying a user thread. For user-user notifications
+ * the wakeup occurs by having the notifier place the thread
+ * on the runq directly; for native-native notifications the
+ * wakeup occurs by calling ReleaseSemaphore.
+ */
+ if ( key == KEY_CVAR ) {
+ PR_ASSERT(completed_io->io_pending == PR_FALSE || completed_io->io_suspended == PR_TRUE);
+
+ /* Thread has already been deleted from sleepQ */
+
+ /* Switch CPU and add to runQ */
+ completed_io->cpu = lockedCPU;
+ completed_io->state = _PR_RUNNABLE;
+ _PR_RUNQ_LOCK(lockedCPU);
+ _PR_ADD_RUNQ(completed_io, lockedCPU, pri);
+ _PR_RUNQ_UNLOCK(lockedCPU);
+ } else {
+ PR_ASSERT(key == KEY_IO);
+ PR_ASSERT(completed_io->io_pending == PR_TRUE);
+
+ _PR_THREAD_LOCK(completed_io);
+
+ completed_io->io_pending = PR_FALSE;
+
+ /* If io_suspended is true, then this IO has already resumed.
+ * We don't need to do anything; because the thread is
+ * already running.
+ */
+ if (completed_io->io_suspended == PR_FALSE) {
+ if (completed_io->flags & (_PR_ON_SLEEPQ|_PR_ON_PAUSEQ)) {
+ _PR_SLEEPQ_LOCK(completed_io->cpu);
+ _PR_DEL_SLEEPQ(completed_io, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(completed_io->cpu);
+
+ _PR_THREAD_UNLOCK(completed_io);
+
+ completed_io->cpu = lockedCPU;
+ completed_io->state = _PR_RUNNABLE;
+ _PR_RUNQ_LOCK(lockedCPU);
+ _PR_ADD_RUNQ(completed_io, lockedCPU, pri);
+ _PR_RUNQ_UNLOCK(lockedCPU);
+ } else {
+ _PR_THREAD_UNLOCK(completed_io);
+ }
+ } else {
+ _PR_THREAD_UNLOCK(completed_io);
+ }
+ }
+ } else {
+ int old_count;
+ PRBool fNeedRelease = PR_FALSE;
+
+ /* For native threads, they are only notified through this loop
+ * when completing IO. So, don't worry about this being a CVAR
+ * notification, because that is not possible.
+ */
+ _PR_THREAD_LOCK(completed_io);
+ completed_io->io_pending = PR_FALSE;
+ if (completed_io->io_suspended == PR_FALSE) {
+ completed_io->state = _PR_RUNNABLE;
+ fNeedRelease = PR_TRUE;
+ }
+ _PR_THREAD_UNLOCK(completed_io);
+ if (fNeedRelease) {
+ rv = ReleaseSemaphore(completed_io->md.blocked_sema,
+ 1, &old_count);
+ PR_ASSERT(0 != rv);
+ }
+ }
+
+ awoken++;
+ timeout = 0; /* Don't block on subsequent trips through the loop */
+ }
+
+ /* never reached */
+ return 0;
+}
+
+PRStatus
+_PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ DWORD rv;
+
+ if ( thread->flags & _PR_GLOBAL_SCOPE ) {
+ PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ?
+ INFINITE : PR_IntervalToMilliseconds(ticks);
+ rv = WaitForSingleObject(thread->md.blocked_sema, msecs);
+ switch(rv) {
+ case WAIT_OBJECT_0:
+ return PR_SUCCESS;
+ break;
+ case WAIT_TIMEOUT:
+ _PR_THREAD_LOCK(thread);
+ if (thread->state == _PR_IO_WAIT) {
+ if (thread->io_pending == PR_TRUE) {
+ thread->io_suspended = PR_TRUE;
+ _PR_THREAD_UNLOCK(thread);
+ } else {
+ /* The IO completed just at the same time the timeout
+ * occurred. This led to us being notified twice.
+ * call WaitForSingleObject() to clear the semaphore.
+ */
+ _PR_THREAD_UNLOCK(thread);
+ rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
+ PR_ASSERT(rv == WAIT_OBJECT_0);
+ }
+ } else {
+ if (thread->wait.cvar != NULL) {
+ thread->wait.cvar = NULL;
+ thread->state = _PR_RUNNING;
+ _PR_THREAD_UNLOCK(thread);
+ } else {
+ /* The CVAR was notified just as the timeout
+ * occurred. This led to us being notified twice.
+ * call WaitForSingleObject() to clear the semaphore.
+ */
+ _PR_THREAD_UNLOCK(thread);
+ rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
+ PR_ASSERT(rv == WAIT_OBJECT_0);
+ }
+ }
+ return PR_SUCCESS;
+ break;
+ default:
+ return PR_FAILURE;
+ break;
+ }
+ } else {
+ PRInt32 is;
+
+ /* XXXMB - This is barely safe, but works. We should find a
+ * way to make all callers of PR_MD_WAIT zero the overlapped buffer
+ * themselves...
+ */
+ if (thread->state != _PR_IO_WAIT)
+ memset(&(thread->md.overlapped), 0, sizeof(OVERLAPPED));
+ if ( !_PR_IS_NATIVE_THREAD(thread)) _PR_INTSOFF(is);
+ _PR_MD_SWITCH_CONTEXT(thread);
+ }
+
+ return PR_SUCCESS;
+}
+
+static PRStatus
+_NT_IO_WAIT(PRThread *thread, PRIntervalTime timeout)
+{
+ PRBool fWait = PR_TRUE;
+
+ if (!_PR_IS_NATIVE_THREAD(thread)) {
+
+ _PR_THREAD_LOCK(thread);
+
+ /* The IO may have already completed; if so, don't add to sleepQ,
+ * since we are already on the runQ!
+ */
+ if (thread->io_pending == PR_TRUE) {
+ _PR_SLEEPQ_LOCK(thread->cpu);
+ _PR_ADD_SLEEPQ(thread, timeout);
+ _PR_SLEEPQ_UNLOCK(thread->cpu);
+ } else
+ fWait = PR_FALSE;
+ _PR_THREAD_UNLOCK(thread);
+ }
+ if (fWait)
+ return _PR_MD_WAIT(thread, timeout);
+ else
+ return PR_SUCCESS;
+}
+
+/*
+ * Unblock threads waiting for I/O
+ * used when interrupting threads
+ *
+ * NOTE: The thread lock should held when this function is called.
+ * On return, the thread lock is released.
+ */
+void _PR_Unblock_IO_Wait(PRThread *thr)
+{
+ PRStatus rv;
+ _PRCPU *cpu = thr->cpu;
+
+ PR_ASSERT(thr->state == _PR_IO_WAIT);
+ thr->io_suspended = PR_TRUE;
+ thr->state = _PR_RUNNABLE;
+
+ if (!_PR_IS_NATIVE_THREAD(thr)) {
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PR_ASSERT(thr->flags & (_PR_ON_SLEEPQ | _PR_ON_PAUSEQ));
+ _PR_SLEEPQ_LOCK(cpu);
+ _PR_DEL_SLEEPQ(thr, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(cpu);
+
+ PR_ASSERT(!(thr->flags & _PR_IDLE_THREAD));
+ _PR_AddThreadToRunQ(me, thr);
+ }
+ _PR_THREAD_UNLOCK(thr);
+ rv = _PR_MD_WAKEUP_WAITER(thr);
+ PR_ASSERT(PR_SUCCESS == rv);
+}
+
+/* Resume an outstanding IO; requires that after the switch, we disable */
+static PRStatus
+_NT_ResumeIO(PRThread *thread, PRIntervalTime ticks)
+{
+ PRBool fWait = PR_TRUE;
+
+ if (!_PR_IS_NATIVE_THREAD(thread)) {
+ _pr_io_restarted_io = thread;
+ } else {
+ _PR_THREAD_LOCK(thread);
+ if (!thread->io_pending)
+ fWait = PR_FALSE;
+ thread->io_suspended = PR_FALSE;
+
+ _PR_THREAD_UNLOCK(thread);
+ }
+ /* We don't put ourselves back on the sleepQ yet; until we
+ * set the suspended bit to false, we can't do that. Just save
+ * the sleep time here, and then continue. The restarted_io handler
+ * will add us to the sleepQ if needed.
+ */
+ thread->sleep = ticks;
+
+ if (fWait)
+ return _PR_MD_WAIT(thread, ticks);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_PR_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread == NULL) {
+ /* If thread is NULL, we aren't waking a thread, we're just poking
+ * idle thread
+ */
+ if ( PostQueuedCompletionStatus(_pr_completion_port, 0,
+ KEY_CVAR, NULL) == FALSE)
+ return PR_FAILURE;
+ return PR_SUCCESS;
+ }
+
+ if ( _PR_IS_NATIVE_THREAD(thread) ) {
+ if (ReleaseSemaphore(thread->md.blocked_sema, 1, NULL) == FALSE)
+ return PR_FAILURE;
+ else
+ return PR_SUCCESS;
+ } else {
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ /* When a Native thread has to awaken a user thread, it has to poke
+ * the completion port because all user threads might be idle, and
+ * thus the CPUs are just waiting for a completion.
+ *
+ * XXXMB - can we know when we are truely idle (and not checking
+ * the runq)?
+ */
+ if (_PR_IS_NATIVE_THREAD(me) || (thread->cpu != me->cpu)) {
+ /* The thread should not be in any queue */
+ PR_ASSERT(thread->queueCount == 0);
+ if ( PostQueuedCompletionStatus(_pr_completion_port, 0,
+ KEY_CVAR, &(thread->md.overlapped)) == FALSE)
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+ }
+}
+
+void
+_PR_MD_INIT_IO()
+{
+ WORD WSAVersion = 0x0101;
+ WSADATA WSAData;
+ OSVERSIONINFO OSversion;
+
+ WSAStartup( WSAVersion, &WSAData );
+
+ _pr_completion_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
+ NULL,
+ 0,
+ 0);
+
+ _MD_NEW_LOCK(&_pr_recycle_lock);
+ _MD_NEW_LOCK(&_pr_ioq_lock);
+
+ OSversion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if (GetVersionEx(&OSversion)) {
+ _nt_version_gets_lockfile_completion = PR_FALSE;
+ if (OSversion.dwMajorVersion >= 4) {
+ _nt_version_gets_lockfile_completion = PR_TRUE;
+ }
+ } else
+ PR_ASSERT(0);
+
+ IsFileLocalInit();
+
+ /*
+ * UDP support: start up the continuation thread
+ */
+
+ pt_tq.op_count = 0;
+ pt_tq.head = pt_tq.tail = NULL;
+ pt_tq.ml = PR_NewLock();
+ PR_ASSERT(NULL != pt_tq.ml);
+ pt_tq.new_op = PR_NewCondVar(pt_tq.ml);
+ PR_ASSERT(NULL != pt_tq.new_op);
+#if defined(DEBUG)
+ memset(&pt_debug, 0, sizeof(struct pt_debug_s));
+#endif
+
+ pt_tq.thread = PR_CreateThread(
+ PR_SYSTEM_THREAD, ContinuationThread, NULL,
+ PR_PRIORITY_URGENT, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+
+ PR_ASSERT(NULL != pt_tq.thread);
+
+#ifdef DEBUG
+ /* Doublecheck _pr_filetime_offset's hard-coded value is correct. */
+ {
+ SYSTEMTIME systime;
+ union {
+ PRTime prt;
+ FILETIME ft;
+ } filetime;
+ BOOL rv;
+
+ systime.wYear = 1970;
+ systime.wMonth = 1;
+ /* wDayOfWeek is ignored */
+ systime.wDay = 1;
+ systime.wHour = 0;
+ systime.wMinute = 0;
+ systime.wSecond = 0;
+ systime.wMilliseconds = 0;
+
+ rv = SystemTimeToFileTime(&systime, &filetime.ft);
+ PR_ASSERT(0 != rv);
+ PR_ASSERT(filetime.prt == _pr_filetime_offset);
+ }
+#endif /* DEBUG */
+}
+
+/* --- SOCKET IO --------------------------------------------------------- */
+
+/* _md_get_recycled_socket()
+ * Get a socket from the recycle bin; if no sockets are in the bin,
+ * create one. The socket will be passed to AcceptEx() as the
+ * second argument.
+ */
+static SOCKET
+_md_get_recycled_socket()
+{
+ SOCKET rv;
+ int af = AF_INET;
+
+ _MD_LOCK(&_pr_recycle_lock);
+ if (_pr_recycle_tail) {
+ _pr_recycle_tail--;
+ rv = _pr_recycle_array[_pr_recycle_tail];
+ _MD_UNLOCK(&_pr_recycle_lock);
+ return rv;
+ }
+ _MD_UNLOCK(&_pr_recycle_lock);
+
+#ifdef _PR_INET6
+ if (_pr_ipv6_enabled) {
+ af = AF_INET6;
+ }
+#endif
+ rv = _PR_MD_SOCKET(af, SOCK_STREAM, 0);
+ if (rv != INVALID_SOCKET && _md_Associate((HANDLE)rv) == 0) {
+ closesocket(rv);
+ return INVALID_SOCKET;
+ }
+ return rv;
+}
+
+/* _md_put_recycled_socket()
+ * Add a socket to the recycle bin.
+ */
+static void
+_md_put_recycled_socket(SOCKET newsock)
+{
+ PR_ASSERT(_pr_recycle_tail >= 0);
+
+ _MD_LOCK(&_pr_recycle_lock);
+ if (_pr_recycle_tail < RECYCLE_SIZE) {
+ _pr_recycle_array[_pr_recycle_tail] = newsock;
+ _pr_recycle_tail++;
+ _MD_UNLOCK(&_pr_recycle_lock);
+ } else {
+ _MD_UNLOCK(&_pr_recycle_lock);
+ closesocket(newsock);
+ }
+
+ return;
+}
+
+/* _md_Associate()
+ * Associates a file with the completion port.
+ * Returns 0 on failure, 1 on success.
+ */
+static PRInt32
+_md_Associate(HANDLE file)
+{
+ HANDLE port;
+
+ port = CreateIoCompletionPort((HANDLE)file,
+ _pr_completion_port,
+ KEY_IO,
+ 0);
+
+ /* XXX should map error codes on failures */
+ return (port == _pr_completion_port);
+}
+
+/*
+ * _md_MakeNonblock()
+ * Make a socket nonblocking.
+ * Returns 0 on failure, 1 on success.
+ */
+static PRInt32
+_md_MakeNonblock(HANDLE file)
+{
+ int rv;
+ u_long one = 1;
+
+ rv = ioctlsocket((SOCKET)file, FIONBIO, &one);
+ /* XXX should map error codes on failures */
+ return (rv == 0);
+}
+
+static int missing_completions = 0;
+static int max_wait_loops = 0;
+
+static PRInt32
+_NT_IO_ABORT(PRInt32 sock)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRBool fWait;
+ PRInt32 rv;
+ int loop_count;
+
+ /* This is a clumsy way to abort the IO, but it is all we can do.
+ * It looks a bit racy, but we handle all the cases.
+ * case 1: IO completes before calling closesocket
+ * case 1a: fWait is set to PR_FALSE
+ * This should e the most likely case. We'll properly
+ * not wait call _NT_IO_WAIT, since the closesocket()
+ * won't be forcing a completion.
+ * case 1b: fWait is set to PR_TRUE
+ * This hopefully won't happen much. When it does, this
+ * thread will timeout in _NT_IO_WAIT for CLOSE_INTERVAL
+ * before cleaning up.
+ * case 2: IO does not complete before calling closesocket
+ * case 2a: IO never completes
+ * This is the likely case. We'll close it and wait
+ * for the completion forced by the close. Return should
+ * be immediate.
+ * case 2b: IO completes just after calling closesocket
+ * Since the closesocket is issued, we'll either get a
+ * completion back for the real IO or for the close. We
+ * don't really care. It may not even be possible to get
+ * a real completion here. In any event, we'll awaken
+ * from NT_IO_WAIT immediately.
+ */
+
+ _PR_THREAD_LOCK(me);
+ fWait = me->io_pending;
+ if (fWait) {
+ /*
+ * If there's still I/O pending, it should have already timed
+ * out once before this function is called.
+ */
+ PR_ASSERT(me->io_suspended == PR_TRUE);
+
+ /* Set up to wait for I/O completion again */
+ me->state = _PR_IO_WAIT;
+ me->io_suspended = PR_FALSE;
+ }
+ _PR_THREAD_UNLOCK(me);
+
+ /* Close the socket if there is one */
+ if (sock != INVALID_SOCKET) {
+ rv = closesocket((SOCKET)sock);
+ }
+
+ /* If there was I/O pending before the close, wait for it to complete */
+ if (fWait) {
+
+ /* Wait and wait for the I/O to complete */
+ for (loop_count = 0; fWait; ++loop_count) {
+
+ _NT_IO_WAIT(me, CLOSE_TIMEOUT);
+
+ _PR_THREAD_LOCK(me);
+ fWait = me->io_pending;
+ if (fWait) {
+ PR_ASSERT(me->io_suspended == PR_TRUE);
+ me->state = _PR_IO_WAIT;
+ me->io_suspended = PR_FALSE;
+ }
+ _PR_THREAD_UNLOCK(me);
+
+ if (loop_count > max_wait_loops) {
+ max_wait_loops = loop_count;
+ }
+ }
+
+ if (loop_count > 1) {
+ ++missing_completions;
+ }
+
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+ me->io_suspended = PR_FALSE;
+
+ return rv;
+}
+
+
+PRInt32
+_PR_MD_SOCKET(int af, int type, int flags)
+{
+ SOCKET sock;
+
+ sock = socket(af, type, flags);
+
+ if (sock == INVALID_SOCKET) {
+ _PR_MD_MAP_SOCKET_ERROR(WSAGetLastError());
+ }
+
+ return (PRInt32)sock;
+}
+
+struct connect_data_s {
+ PRInt32 status;
+ PRInt32 error;
+ PRInt32 osfd;
+ struct sockaddr *addr;
+ PRUint32 addrlen;
+ PRIntervalTime timeout;
+};
+
+void
+_PR_MD_connect_thread(void *cdata)
+{
+ struct connect_data_s *cd = (struct connect_data_s *)cdata;
+
+ cd->status = connect(cd->osfd, cd->addr, cd->addrlen);
+
+ if (cd->status == SOCKET_ERROR)
+ cd->error = WSAGetLastError();
+
+ return;
+}
+
+
+PRInt32
+_PR_MD_CONNECT(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 rv;
+ PRThread *cThread;
+ struct connect_data_s cd;
+
+ if (!_nt_use_async || fd->secret->nonblocking) {
+ PRInt32 rv;
+ fd_set wd;
+ struct timeval tv, *tvp;
+
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+
+ while ((rv = connect(osfd, (struct sockaddr *) addr, addrlen)) == -1) {
+ rv = WSAGetLastError();
+ if ((!fd->secret->nonblocking) && ((rv == WSAEWOULDBLOCK) ||
+ (rv == WSAEALREADY) ||
+ (rv == WSAEINVAL) /* for winsock1.1, it reports EALREADY as EINVAL */)) {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ tvp = NULL;
+ else {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ rv = select(osfd + 1, NULL, &wd, NULL, tvp);
+ if (rv > 0) {
+ rv = 0;
+ } else if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return(-1);
+ } else if (rv < 0) {
+ rv = WSAGetLastError();
+ _PR_MD_MAP_SELECT_ERROR(rv);
+ return(-1);
+ }
+ } else if ((rv == WSAEISCONN)) {
+ /* Success! */
+ return 0;
+ } else {
+ _PR_MD_MAP_CONNECT_ERROR(rv);
+ return -1;
+ }
+ }
+ return rv;
+ }
+
+ /* If we are a native thread, just make the blocking IO call */
+ if (_PR_IS_NATIVE_THREAD(me)) {
+ rv = connect(osfd, (struct sockaddr *)addr, addrlen);
+ if (rv == -1) {
+ rv = WSAGetLastError();
+ _PR_MD_MAP_CONNECT_ERROR(rv);
+ return -1;
+ } else
+ return rv;
+ }
+
+ /* NT doesn't provide a nice way to do asynchronous
+ * connect. The proxy team invented a huge chunk of code which has
+ * a single thread multiplexing multiple connect requests via
+ * WSAAsyncSelect(). That is a better solution, but I'm not doing that
+ * now. At this point, just create a real thread to do the work.
+ *
+ * Rumor has it that on nt3.51, all the WSA library does is create
+ * a thread to call a blocking connect() anyway. On 4.0 they've fixed
+ * that. -mbelshe
+ */
+ cd.osfd = osfd;
+ cd.addr = (struct sockaddr *)addr;
+ cd.addrlen = addrlen;
+ cd.timeout = timeout;
+ cThread = PR_CreateThread(PR_SYSTEM_THREAD,
+ _PR_MD_connect_thread,
+ (void *)&cd,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+
+ if (cThread == NULL) {
+ return -1;
+ }
+
+ PR_JoinThread(cThread);
+
+ rv = cd.status;
+
+ if (rv == SOCKET_ERROR) {
+ _PR_MD_MAP_CONNECT_ERROR(cd.error);
+ return -1;
+ }
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen)
+{
+ PRInt32 rv;
+#if 0
+ int one = 1;
+#endif
+
+ rv = bind(fd->secret->md.osfd, (const struct sockaddr *)&(addr->inet), addrlen);
+
+ if (rv == SOCKET_ERROR) {
+ _PR_MD_MAP_BIND_ERROR(WSAGetLastError());
+ return -1;
+ }
+
+#if 0
+ /* Disable nagle- so far unknown if this is good or not...
+ */
+ rv = setsockopt(fd->secret->md.osfd,
+ SOL_SOCKET,
+ TCP_NODELAY,
+ (const char *)&one,
+ sizeof(one));
+ PR_ASSERT(rv == 0);
+#endif
+
+ return 0;
+}
+
+void _PR_MD_UPDATE_ACCEPT_CONTEXT(PRInt32 accept_sock, PRInt32 listen_sock)
+{
+ /* Sockets accept()'d with AcceptEx need to call this setsockopt before
+ * calling anything other than ReadFile(), WriteFile(), send(), recv(),
+ * Transmitfile(), and closesocket(). In order to call any other
+ * winsock functions, we have to make this setsockopt call.
+ *
+ * XXXMB - For the server, we *NEVER* need this in
+ * the "normal" code path. But now we have to call it. This is a waste
+ * of a system call. We'd like to only call it before calling the
+ * obscure socket calls, but since we don't know at that point what the
+ * original socket was (or even if it is still alive) we can't do it
+ * at that point...
+ */
+ setsockopt((SOCKET)accept_sock,
+ SOL_SOCKET,
+ SO_UPDATE_ACCEPT_CONTEXT,
+ (char *)&listen_sock,
+ sizeof(listen_sock));
+
+}
+
+#define INET_ADDR_PADDED (sizeof(PRNetAddr) + 16)
+PRInt32
+_PR_MD_FAST_ACCEPT(PRFileDesc *fd, PRNetAddr *raddr, PRUint32 *rlen,
+ PRIntervalTime timeout, PRBool fast,
+ _PR_AcceptTimeoutCallback callback, void *callbackArg)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ SOCKET accept_sock;
+ int bytes;
+ PRNetAddr *Laddr;
+ PRNetAddr *Raddr;
+ PRUint32 llen, err;
+ int rv;
+
+ if (!_nt_use_async || fd->secret->nonblocking) {
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+ /*
+ * The accepted socket inherits the nonblocking attribute of
+ * the listening socket, so no need to call _md_MakeNonblock().
+ */
+ return _nt_nonblock_accept(fd, (struct sockaddr_in *)raddr, rlen, timeout);
+ }
+
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_Associate((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+
+ if (!me->md.acceptex_buf) {
+ me->md.acceptex_buf = PR_MALLOC(2*INET_ADDR_PADDED);
+ if (!me->md.acceptex_buf) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+ }
+
+ accept_sock = _md_get_recycled_socket();
+ if (accept_sock == INVALID_SOCKET)
+ return -1;
+
+ memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
+
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+
+ me->io_pending = PR_TRUE;
+ me->io_fd = osfd;
+ me->state = _PR_IO_WAIT;
+ rv = AcceptEx((SOCKET)osfd,
+ accept_sock,
+ me->md.acceptex_buf,
+ 0,
+ INET_ADDR_PADDED,
+ INET_ADDR_PADDED,
+ &bytes,
+ &(me->md.overlapped));
+
+ if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING)) {
+ /* Argh! The IO failed */
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ _PR_MD_MAP_ACCEPTEX_ERROR(err);
+ return -1;
+ }
+
+ if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
+ PR_ASSERT(0);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
+
+ if (me->io_suspended) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ return -1;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ _PR_MD_MAP_ACCEPTEX_ERROR(me->md.blocked_io_error);
+ return -1;
+ }
+
+ if (!fast)
+ _PR_MD_UPDATE_ACCEPT_CONTEXT((SOCKET)accept_sock, (SOCKET)osfd);
+
+ /* IO is done */
+ GetAcceptExSockaddrs(
+ me->md.acceptex_buf,
+ 0,
+ INET_ADDR_PADDED,
+ INET_ADDR_PADDED,
+ (LPSOCKADDR *)&(Laddr),
+ &llen,
+ (LPSOCKADDR *)&(Raddr),
+ (unsigned int *)rlen);
+
+ if (raddr != NULL)
+ memcpy((char *)raddr, (char *)&Raddr->inet, *rlen);
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+
+ return accept_sock;
+}
+
+PRInt32
+_PR_MD_FAST_ACCEPT_READ(PRFileDesc *sd, PRInt32 *newSock, PRNetAddr **raddr,
+ void *buf, PRInt32 amount, PRIntervalTime timeout,
+ PRBool update, _PR_AcceptTimeoutCallback callback,
+ void *callbackArg)
+{
+ PRInt32 sock = sd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ int bytes;
+ PRNetAddr *Laddr;
+ PRUint32 llen, rlen, err;
+ int rv;
+ PRBool isConnected;
+ PRBool madeCallback = PR_FALSE;
+
+ if (!_nt_use_async) {
+ PRFileDesc *nd;
+ bytes = _PR_EmulateAcceptRead(sd, &nd, raddr, buf, amount, timeout);
+ if (bytes != -1) {
+ /*
+ * This part is the same as SocketClose(nd), except
+ * that we don't close the osfd.
+ */
+ PR_ASSERT(nd->secret->state == _PR_FILEDESC_OPEN);
+ *newSock = nd->secret->md.osfd;
+ nd->secret->state = _PR_FILEDESC_CLOSED;
+ PR_FreeFileDesc(nd);
+ }
+ return bytes;
+ }
+
+ if (!sd->secret->md.io_model_committed) {
+ rv = _md_Associate((HANDLE)sock);
+ PR_ASSERT(0 != rv);
+ sd->secret->md.io_model_committed = PR_TRUE;
+ }
+
+ *newSock = _md_get_recycled_socket();
+ if (*newSock == INVALID_SOCKET)
+ return -1;
+
+ memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
+
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+
+ me->io_pending = PR_TRUE;
+ me->io_fd = sock;
+ me->state = _PR_IO_WAIT;
+ rv = AcceptEx((SOCKET)sock,
+ *newSock,
+ buf,
+ amount,
+ INET_ADDR_PADDED,
+ INET_ADDR_PADDED,
+ &bytes,
+ &(me->md.overlapped));
+
+ if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING)) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ _PR_MD_MAP_ACCEPTEX_ERROR(err);
+ return -1;
+ }
+
+ if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
+ PR_ASSERT(0);
+ return -1;
+ }
+
+retry:
+ if (me->io_suspended) {
+ PRInt32 err;
+ INT seconds;
+ INT bytes = sizeof(seconds);
+
+ PR_ASSERT(timeout != PR_INTERVAL_NO_TIMEOUT);
+
+ err = getsockopt(*newSock,
+ SOL_SOCKET,
+ SO_CONNECT_TIME,
+ (char *)&seconds,
+ (PINT)&bytes);
+ if ( err == NO_ERROR ) {
+ PRIntervalTime elapsed = PR_SecondsToInterval(seconds);
+
+ if (seconds == 0xffffffff)
+ isConnected = PR_FALSE;
+ else
+ isConnected = PR_TRUE;
+
+ if (!isConnected) {
+ if (madeCallback == PR_FALSE && callback)
+ callback(callbackArg);
+ madeCallback = PR_TRUE;
+ me->state = _PR_IO_WAIT;
+ if (_NT_ResumeIO(me, timeout) == PR_FAILURE)
+ return -1;
+ goto retry;
+ }
+
+ if (elapsed < timeout) {
+ /* Socket is not connected but time not elapsed, RESUME IO */
+ timeout -= elapsed;
+ me->state = _PR_IO_WAIT;
+ if (_NT_ResumeIO(me, timeout) == PR_FAILURE)
+ return -1;
+ goto retry;
+ }
+ } else {
+ /* What to do here? Assume socket not open?*/
+ PR_ASSERT(0);
+ isConnected = PR_FALSE;
+ }
+
+ rv = _NT_IO_ABORT(*newSock);
+
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+ PR_ASSERT(me->io_pending == PR_FALSE);
+ /* If the IO is still suspended, it means we didn't get any
+ * completion from NT_IO_WAIT. This is not disasterous, I hope,
+ * but it may mean we still have an IO outstanding... Try to
+ * recover by just allowing ourselves to continue.
+ */
+ me->io_suspended = PR_FALSE;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ me->state = _PR_RUNNING;
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
+
+ if (me->md.blocked_io_status == 0) {
+ _PR_MD_MAP_ACCEPTEX_ERROR(me->md.blocked_io_error);
+ closesocket(*newSock);
+ return -1;
+ }
+
+ if (update)
+ _PR_MD_UPDATE_ACCEPT_CONTEXT((SOCKET)*newSock, (SOCKET)sock);
+
+ /* IO is done */
+ GetAcceptExSockaddrs(
+ buf,
+ amount,
+ INET_ADDR_PADDED,
+ INET_ADDR_PADDED,
+ (LPSOCKADDR *)&(Laddr),
+ &llen,
+ (LPSOCKADDR *)(raddr),
+ (unsigned int *)&rlen);
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+
+ return me->md.blocked_io_bytes;
+}
+
+PRInt32
+_PR_MD_TRANSMITFILE(PRFileDesc *sock, PRFileDesc *file, const void *headers, PRInt32 hlen,
+ PRInt32 flags, PRIntervalTime timeout)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 tflags;
+ int rv, err;
+
+ if (!_nt_use_async) {
+ if (!sock->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)sock->secret->md.osfd);
+ PR_ASSERT(0 != rv);
+ sock->secret->md.io_model_committed = PR_TRUE;
+ }
+ return _PR_EmulateTransmitFile(sock, file, headers, hlen, flags, timeout);
+ }
+
+ if (!sock->secret->md.io_model_committed) {
+ rv = _md_Associate((HANDLE)sock->secret->md.osfd);
+ PR_ASSERT(0 != rv);
+ sock->secret->md.io_model_committed = PR_TRUE;
+ }
+ if (!me->md.xmit_bufs) {
+ me->md.xmit_bufs = PR_NEW(TRANSMIT_FILE_BUFFERS);
+ if (!me->md.xmit_bufs) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+ }
+ me->md.xmit_bufs->Head = (void *)headers;
+ me->md.xmit_bufs->HeadLength = hlen;
+ me->md.xmit_bufs->Tail = (void *)NULL;
+ me->md.xmit_bufs->TailLength = 0;
+
+ memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
+
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+
+ tflags = 0;
+ if (flags & PR_TRANSMITFILE_CLOSE_SOCKET)
+ tflags = TF_DISCONNECT | TF_REUSE_SOCKET;
+
+ me->io_pending = PR_TRUE;
+ me->io_fd = sock->secret->md.osfd;
+ me->state = _PR_IO_WAIT;
+ rv = TransmitFile((SOCKET)sock->secret->md.osfd,
+ (HANDLE)file->secret->md.osfd,
+ (DWORD)0,
+ (DWORD)0,
+ (LPOVERLAPPED)&(me->md.overlapped),
+ (TRANSMIT_FILE_BUFFERS *)me->md.xmit_bufs,
+ (DWORD)tflags);
+ if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ _PR_MD_MAP_TRANSMITFILE_ERROR(err);
+ return -1;
+ }
+
+ if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
+ PR_ASSERT(0);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
+
+ if (me->io_suspended) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ return -1;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ _PR_MD_MAP_TRANSMITFILE_ERROR(me->md.blocked_io_error);
+ return -1;
+ }
+
+ if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) {
+ _md_put_recycled_socket(sock->secret->md.osfd);
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+
+ return me->md.blocked_io_bytes;
+}
+
+PRInt32
+_PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ int bytes;
+ int rv, err;
+
+ if (!_nt_use_async || fd->secret->nonblocking) {
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+ return _nt_nonblock_recv(fd, buf, amount, timeout);
+ }
+
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_Associate((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+
+ memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
+
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+
+ me->io_pending = PR_TRUE;
+ me->io_fd = osfd;
+ me->state = _PR_IO_WAIT;
+ rv = ReadFile((HANDLE)osfd,
+ buf,
+ amount,
+ &bytes,
+ &(me->md.overlapped));
+ if ( (rv == 0) && (GetLastError() != ERROR_IO_PENDING) ) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ if ((err = GetLastError()) == ERROR_HANDLE_EOF)
+ return 0;
+ _PR_MD_MAP_READ_ERROR(err);
+ return -1;
+ }
+
+ if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
+ PR_ASSERT(0);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
+
+ if (me->io_suspended) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ return -1;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ if (me->md.blocked_io_error == ERROR_HANDLE_EOF)
+ return 0;
+ _PR_MD_MAP_READ_ERROR(me->md.blocked_io_error);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+
+ return me->md.blocked_io_bytes;
+}
+
+PRInt32
+_PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ int bytes;
+ int rv, err;
+
+ if (!_nt_use_async || fd->secret->nonblocking) {
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+ return _nt_nonblock_send(fd, (char *)buf, amount, timeout);
+ }
+
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_Associate((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+
+ memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
+
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+
+ me->io_pending = PR_TRUE;
+ me->io_fd = osfd;
+ me->state = _PR_IO_WAIT;
+ rv = WriteFile((HANDLE)osfd,
+ buf,
+ amount,
+ &bytes,
+ &(me->md.overlapped));
+ if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ _PR_MD_MAP_WRITE_ERROR(err);
+ return -1;
+ }
+
+ if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
+ PR_ASSERT(0);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
+
+ if (me->io_suspended) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ return -1;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ _PR_MD_MAP_WRITE_ERROR(me->md.blocked_io_error);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+
+ return me->md.blocked_io_bytes;
+}
+
+PRInt32
+_PR_MD_SENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv;
+
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+ if (_nt_use_async && !fd->secret->nonblocking)
+ return pt_SendTo(osfd, buf, amount, flags, addr, addrlen, timeout);
+ else
+ return _nt_nonblock_sendto(fd, buf, amount, (struct sockaddr *)addr, addrlen, timeout);
+}
+
+PRInt32
+_PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv;
+
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+ if (_nt_use_async && !fd->secret->nonblocking)
+ return pt_RecvFrom(osfd, buf, amount, flags, addr, addrlen, timeout);
+ else
+ return _nt_nonblock_recvfrom(fd, buf, amount, (struct sockaddr *)addr, addrlen, timeout);
+}
+
+/* XXXMB - for now this is a sockets call only */
+PRInt32
+_PR_MD_WRITEV(PRFileDesc *fd, PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ int index;
+ int sent = 0;
+ int rv;
+
+ if (!_nt_use_async || fd->secret->nonblocking) {
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+ return _nt_nonblock_writev(fd, iov, iov_size, timeout);
+ }
+
+ for (index=0; index<iov_size; index++) {
+ rv = _PR_MD_SEND(fd, iov[index].iov_base, iov[index].iov_len, 0,
+ timeout);
+ if (rv > 0)
+ sent += rv;
+ if ( rv != iov[index].iov_len ) {
+ if (sent <= 0)
+ return -1;
+ return -1;
+ }
+ }
+
+ return sent;
+}
+
+PRInt32
+_PR_MD_LISTEN(PRFileDesc *fd, PRIntn backlog)
+{
+ PRInt32 rv;
+
+ rv = listen(fd->secret->md.osfd, backlog);
+ if (rv < 0)
+ _PR_MD_MAP_LISTEN_ERROR(WSAGetLastError());
+ return(rv);
+}
+
+PRInt32
+_PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how)
+{
+ PRInt32 rv;
+
+ rv = shutdown(fd->secret->md.osfd, how);
+ if (rv < 0)
+ _PR_MD_MAP_SHUTDOWN_ERROR(WSAGetLastError());
+ return(rv);
+}
+
+PRStatus
+_PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
+{
+ PRInt32 rv;
+
+ rv = getsockname((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, len);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_GETSOCKNAME_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
+{
+ PRInt32 rv;
+
+ /*
+ * NT has a bug that, when invoked on a socket accepted by
+ * AcceptEx(), getpeername() returns an all-zero peer address.
+ * To work around this bug, we store the peer's address (returned
+ * by AcceptEx()) with the socket fd and use the cached peer
+ * address if the socket is an accepted socket.
+ */
+
+ if (fd->secret->md.accepted_socket) {
+ INT seconds;
+ INT bytes = sizeof(seconds);
+
+ /*
+ * Determine if the socket is connected.
+ */
+
+ rv = getsockopt(fd->secret->md.osfd,
+ SOL_SOCKET,
+ SO_CONNECT_TIME,
+ (char *) &seconds,
+ (PINT) &bytes);
+ if (rv == NO_ERROR) {
+ if (seconds == 0xffffffff) {
+ PR_SetError(PR_NOT_CONNECTED_ERROR, 0);
+ return PR_FAILURE;
+ }
+ *len = PR_NETADDR_SIZE(addr);
+ memcpy(addr, &fd->secret->md.peer_addr, *len);
+ return PR_SUCCESS;
+ } else {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+ } else {
+ rv = getpeername((SOCKET)fd->secret->md.osfd,
+ (struct sockaddr *) addr, len);
+ if (rv == 0) {
+ return PR_SUCCESS;
+ } else {
+ _PR_MD_MAP_GETPEERNAME_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+ }
+}
+
+PRStatus
+_PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen)
+{
+ PRInt32 rv;
+
+ rv = getsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen)
+{
+ PRInt32 rv;
+
+ rv = setsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_SETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+/* --- FILE IO ----------------------------------------------------------- */
+
+PRInt32
+_PR_MD_OPEN(const char *name, PRIntn osflags, PRIntn mode)
+{
+ HANDLE file;
+ PRInt32 access = 0;
+ PRInt32 flags = 0;
+ PRInt32 flag6 = 0;
+
+ if (osflags & PR_SYNC) flag6 = FILE_FLAG_WRITE_THROUGH;
+
+ if (_nt_use_async)
+ {
+ if (osflags & PR_RDONLY || osflags & PR_RDWR) access |= GENERIC_READ;
+ if (osflags & PR_WRONLY || osflags & PR_RDWR) access |= GENERIC_WRITE;
+
+ if (osflags & PR_CREATE_FILE)
+ flags = (0 != (osflags & PR_TRUNCATE)) ? CREATE_ALWAYS : OPEN_ALWAYS;
+ else if (osflags & PR_TRUNCATE) flags = CREATE_ALWAYS;
+ else flags = OPEN_EXISTING;
+
+ flag6 |= FILE_FLAG_OVERLAPPED;
+
+ file = CreateFile(name,
+ access,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ flags,
+ flag6,
+ NULL);
+ if (file == INVALID_HANDLE_VALUE) {
+ _PR_MD_MAP_OPEN_ERROR(GetLastError());
+ return -1;
+ }
+
+ if (_md_Associate(file) == 0) {
+ CloseHandle(file);
+ return -1;
+ }
+
+ if (osflags & PR_APPEND) {
+ if ( SetFilePointer(file, 0, 0, FILE_END) == 0xFFFFFFFF ) {
+ _PR_MD_MAP_LSEEK_ERROR(GetLastError());
+ CloseHandle(file);
+ return -1;
+ }
+ }
+
+ return (PRInt32)file;
+ }
+ else
+ {
+
+ if (osflags & PR_RDONLY || osflags & PR_RDWR)
+ access |= GENERIC_READ;
+ if (osflags & PR_WRONLY || osflags & PR_RDWR)
+ access |= GENERIC_WRITE;
+ if (osflags & PR_CREATE_FILE)
+ flags = OPEN_ALWAYS;
+ else if (osflags & PR_TRUNCATE)
+ flags = CREATE_ALWAYS;
+ else
+ flags = OPEN_EXISTING;
+
+ file = CreateFile(name,
+ access,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ flags,
+ flag6,
+ NULL);
+ if (file == INVALID_HANDLE_VALUE) {
+ _PR_MD_MAP_OPEN_ERROR(GetLastError());
+ return -1;
+ }
+
+ /* Note: we didn't bother putting it in nonblocking mode */
+ return (PRInt32)file;
+ }
+}
+
+PRInt32
+_PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len)
+{
+ PRInt32 f = fd->secret->md.osfd;
+ PRUint32 bytes;
+ int rv, err;
+
+ if (_nt_use_async && !fd->secret->md.nonoverlapped) {
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
+
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+
+ me->md.overlapped.Offset = SetFilePointer((HANDLE)f, 0, 0, FILE_CURRENT);
+
+ me->io_pending = PR_TRUE;
+ me->io_fd = f;
+ me->state = _PR_IO_WAIT;
+ rv = ReadFile((HANDLE)f,
+ (LPVOID)buf,
+ len,
+ &bytes,
+ &me->md.overlapped);
+ if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ if (err == ERROR_HANDLE_EOF)
+ return 0;
+ _PR_MD_MAP_READ_ERROR(err);
+ return -1;
+ }
+
+ if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
+ PR_ASSERT(0);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
+
+ if (me->io_suspended) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ return -1;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ if (me->md.blocked_io_error == ERROR_HANDLE_EOF)
+ return 0;
+ _PR_MD_MAP_READ_ERROR(me->md.blocked_io_error);
+ return -1;
+ }
+
+ SetFilePointer((HANDLE)f, me->md.blocked_io_bytes, 0, FILE_CURRENT);
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+
+ return me->md.blocked_io_bytes;
+ } else {
+
+ rv = ReadFile((HANDLE)f,
+ (LPVOID)buf,
+ len,
+ &bytes,
+ NULL);
+ if (rv == 0) {
+ err = GetLastError();
+ /* ERROR_HANDLE_EOF can only be returned by async io */
+ PR_ASSERT(err != ERROR_HANDLE_EOF);
+ if (err == ERROR_BROKEN_PIPE) {
+ /* The write end of the pipe has been closed. */
+ return 0;
+ }
+ _PR_MD_MAP_READ_ERROR(err);
+ return -1;
+ }
+ return bytes;
+ }
+}
+
+PRInt32
+_PR_MD_WRITE(PRFileDesc *fd, void *buf, PRInt32 len)
+{
+ PRInt32 f = fd->secret->md.osfd;
+ PRInt32 bytes;
+ int rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_nt_use_async && !fd->secret->md.nonoverlapped) {
+ memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
+
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+
+ me->md.overlapped.Offset = SetFilePointer((HANDLE)f, 0, 0, FILE_CURRENT);
+
+ me->io_pending = PR_TRUE;
+ me->io_fd = f;
+ me->state = _PR_IO_WAIT;
+ rv = WriteFile((HANDLE)f,
+ buf,
+ len,
+ &bytes,
+ &(me->md.overlapped));
+ if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ _PR_MD_MAP_WRITE_ERROR(err);
+ return -1;
+ }
+
+ if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
+ PR_ASSERT(0);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
+
+ if (me->io_suspended) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ return -1;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ _PR_MD_MAP_WRITE_ERROR(me->md.blocked_io_error);
+ return -1;
+ }
+
+ SetFilePointer((HANDLE)f, me->md.blocked_io_bytes, 0, FILE_CURRENT);
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+
+ return me->md.blocked_io_bytes;
+ } else {
+ rv = WriteFile((HANDLE)f,
+ buf,
+ len,
+ &bytes,
+ NULL);
+ if (rv == 0) {
+ _PR_MD_MAP_WRITE_ERROR(GetLastError());
+ return -1;
+ }
+ return bytes;
+ }
+}
+
+PRInt32
+_PR_MD_SOCKETAVAILABLE(PRFileDesc *fd)
+{
+ PRInt32 result;
+
+ if (ioctlsocket(fd->secret->md.osfd, FIONREAD, &result) < 0) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, WSAGetLastError());
+ return -1;
+ }
+ return result;
+}
+
+PRInt32
+_PR_MD_LSEEK(PRFileDesc *fd, PRInt32 offset, int whence)
+{
+ PRInt32 rv;
+
+ rv = SetFilePointer((HANDLE)fd->secret->md.osfd, offset, NULL, whence);
+ /*
+ * If the lpDistanceToMoveHigh argument (third argument) is
+ * NULL, SetFilePointer returns 0xffffffff on failure.
+ */
+ if (-1 == rv) {
+ _PR_MD_MAP_LSEEK_ERROR(GetLastError());
+ }
+ return rv;
+}
+
+PRInt64
+_PR_MD_LSEEK64(PRFileDesc *fd, PRInt64 offset, int whence)
+{
+ PRInt64 result;
+ PRInt32 rv, low = (PRInt32)offset, hi = (PRInt32)(offset >> 32);
+
+ rv = SetFilePointer((HANDLE)fd->secret->md.osfd, low, &hi, whence);
+
+ /*
+ * If the lpDistanceToMoveHigh argument (third argument) is
+ * NULL, SetFilePointer returns 0xffffffff on failure.
+ */
+ if (-1 == rv)
+ {
+ _PR_MD_MAP_LSEEK_ERROR(GetLastError());
+ return -1;
+ }
+
+ result = (hi << 32) + rv;
+ return result;
+}
+
+/*
+ * This is documented to succeed on read-only files, but Win32's
+ * FlushFileBuffers functions fails with "access denied" in such a
+ * case. So we only signal an error if the error is *not* "access
+ * denied".
+ */
+PRInt32
+_PR_MD_FSYNC(PRFileDesc *fd)
+{
+ /*
+ * From the documentation:
+ *
+ * On Windows NT, the function FlushFileBuffers fails if hFile
+ * is a handle to console output. That is because console
+ * output is not buffered. The function returns FALSE, and
+ * GetLastError returns ERROR_INVALID_HANDLE.
+ *
+ * On the other hand, on Win95, it returns without error. I cannot
+ * assume that 0, 1, and 2 are console, because if someone closes
+ * System.out and then opens a file, they might get file descriptor
+ * 1. An error on *that* version of 1 should be reported, whereas
+ * an error on System.out (which was the original 1) should be
+ * ignored. So I use isatty() to ensure that such an error was
+ * because of this, and if it was, I ignore the error.
+ */
+
+ long handle = _get_osfhandle(fd->secret->md.osfd);
+ BOOL ok = FlushFileBuffers((HANDLE)handle);
+
+ if (!ok) {
+ DWORD err = GetLastError();
+
+ if (err != ERROR_ACCESS_DENIED) { /* from winerror.h */
+ _PR_MD_MAP_FSYNC_ERROR(err);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+PRInt32
+_PR_MD_CLOSE(PRInt32 osfd, PRBool socket)
+{
+ PRInt32 rv;
+ PRInt32 err;
+ if (_nt_use_async) {
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (socket) {
+ rv = closesocket((SOCKET)osfd);
+ if (rv < 0)
+ err = WSAGetLastError();
+ } else {
+ rv = CloseHandle((HANDLE)osfd)?0:-1;
+ if (rv < 0)
+ err = GetLastError();
+ }
+
+ if (rv == 0 && me->io_pending) {
+ if (me->io_fd == osfd) {
+ PRBool fWait;
+
+ PR_ASSERT(me->io_suspended == PR_TRUE);
+ _PR_THREAD_LOCK(me);
+ me->state = _PR_IO_WAIT;
+ /* The IO could have completed on another thread just after
+ * calling closesocket while the io_suspended flag was true.
+ * So we now grab the lock to do a safe check on io_pending to
+ * see if we need to wait or not. At this point we can check
+ * io_pending safely because we've reset io_suspended to FALSE.
+ * XXXMB - 1-15-97 this seems fishy and begging for a race...
+ */
+ fWait = me->io_pending;
+ me->io_suspended = PR_FALSE;
+ _PR_THREAD_UNLOCK(me);
+
+ if (fWait)
+ _NT_IO_WAIT(me, CLOSE_TIMEOUT);
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+ PR_ASSERT(me->io_pending == PR_FALSE);
+ me->io_suspended = PR_FALSE;
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ }
+ } else {
+ me->io_suspended = PR_FALSE;
+ if (rv < 0)
+ _PR_MD_MAP_CLOSE_ERROR(err);
+ }
+ return rv;
+ } else {
+ if (socket) {
+ rv = closesocket((SOCKET)osfd);
+ if (rv == -1)
+ _PR_MD_MAP_CLOSE_ERROR(WSAGetLastError());
+ } else {
+ rv = CloseHandle((HANDLE)osfd)?0:-1;
+ if (rv == -1)
+ _PR_MD_MAP_CLOSE_ERROR(GetLastError());
+ }
+ }
+}
+
+
+/* --- DIR IO ------------------------------------------------------------ */
+#define GetFileFromDIR(d) (d)->d_entry.cFileName
+#define FileIsHidden(d) ((d)->d_entry.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
+
+void FlipSlashes(char *cp, int len)
+{
+ while (--len >= 0) {
+ if (cp[0] == '/') {
+ cp[0] = PR_DIRECTORY_SEPARATOR;
+ }
+ cp++;
+ }
+}
+
+/*
+**
+** Local implementations of standard Unix RTL functions which are not provided
+** by the VC RTL.
+**
+*/
+
+PRStatus
+_PR_MD_CLOSE_DIR(_MDDir *d)
+{
+ if ( d ) {
+ if (FindClose( d->d_hdl )) {
+ d->magic = (PRUint32)-1;
+ return PR_SUCCESS;
+ } else {
+ _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError());
+ return PR_FAILURE;
+ }
+ }
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+}
+
+
+PRStatus
+_PR_MD_OPEN_DIR(_MDDir *d, const char *name)
+{
+ char filename[ MAX_PATH ];
+
+ PR_snprintf(filename, MAX_PATH, "%s%s%s",
+ name, PR_DIRECTORY_SEPARATOR_STR, "*.*");
+ FlipSlashes( filename, strlen(filename) );
+
+ d->d_hdl = FindFirstFile( filename, &(d->d_entry) );
+ if ( d->d_hdl == INVALID_HANDLE_VALUE ) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return PR_FAILURE;
+ }
+ d->firstEntry = PR_TRUE;
+ d->magic = _MD_MAGIC_DIR;
+ return PR_SUCCESS;
+}
+
+char *
+_PR_MD_READ_DIR(_MDDir *d, PRIntn flags)
+{
+ PRInt32 err;
+ BOOL rv;
+ char *fileName;
+
+ if ( d ) {
+ while (1) {
+ if (d->firstEntry) {
+ d->firstEntry = PR_FALSE;
+ rv = 1;
+ } else {
+ rv = FindNextFile(d->d_hdl, &(d->d_entry));
+ }
+ if (rv == 0) {
+ break;
+ }
+ fileName = GetFileFromDIR(d);
+ if ( (flags & PR_SKIP_DOT) &&
+ (fileName[0] == '.') && (fileName[1] == '\0'))
+ continue;
+ if ( (flags & PR_SKIP_DOT_DOT) &&
+ (fileName[0] == '.') && (fileName[1] == '.') &&
+ (fileName[2] == '\0'))
+ continue;
+ if ( (flags & PR_SKIP_HIDDEN) && FileIsHidden(d))
+ continue;
+ return fileName;
+ }
+ err = GetLastError();
+ PR_ASSERT(NO_ERROR != err);
+ _PR_MD_MAP_READDIR_ERROR(err);
+ return NULL;
+ }
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+}
+
+PRInt32
+_PR_MD_DELETE(const char *name)
+{
+ if (DeleteFile(name)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_DELETE_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+static void
+_PR_FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm)
+{
+ PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime));
+ CopyMemory(prtm, filetime, sizeof(PRTime));
+ *prtm = (*prtm - _pr_filetime_offset) / 10i64;
+
+#ifdef DEBUG
+ /* Doublecheck our calculation. */
+ {
+ SYSTEMTIME systime;
+ PRExplodedTime etm;
+ PRTime cmp; /* for comparison */
+ BOOL rv;
+
+ rv = FileTimeToSystemTime(filetime, &systime);
+ PR_ASSERT(0 != rv);
+
+ /*
+ * PR_ImplodeTime ignores wday and yday.
+ */
+ etm.tm_usec = systime.wMilliseconds * PR_USEC_PER_MSEC;
+ etm.tm_sec = systime.wSecond;
+ etm.tm_min = systime.wMinute;
+ etm.tm_hour = systime.wHour;
+ etm.tm_mday = systime.wDay;
+ etm.tm_month = systime.wMonth - 1;
+ etm.tm_year = systime.wYear;
+ /*
+ * It is not well-documented what time zone the FILETIME's
+ * are in. WIN32_FIND_DATA is documented to be in UTC (GMT).
+ * But BY_HANDLE_FILE_INFORMATION is unclear about this.
+ * By our best judgement, we assume that FILETIME is in UTC.
+ */
+ etm.tm_params.tp_gmt_offset = 0;
+ etm.tm_params.tp_dst_offset = 0;
+ cmp = PR_ImplodeTime(&etm);
+
+ /*
+ * SYSTEMTIME is in milliseconds precision, so we convert PRTime's
+ * microseconds to milliseconds before doing the comparison.
+ */
+ PR_ASSERT((cmp / PR_USEC_PER_MSEC) == (*prtm / PR_USEC_PER_MSEC));
+ }
+#endif /* DEBUG */
+}
+
+PRInt32
+_PR_MD_STAT(const char *fn, struct stat *info)
+{
+ PRInt32 rv;
+
+ rv = _stat(fn, (struct _stat *)info);
+ if (-1 == rv) {
+ /*
+ * Check for MSVC runtime library _stat() bug.
+ * (It's really a bug in FindFirstFile().)
+ * If a pathname ends in a backslash or slash,
+ * e.g., c:\temp\ or c:/temp/, _stat() will fail.
+ * Note: a pathname ending in a slash (e.g., c:/temp/)
+ * can be handled by _stat() on NT but not on Win95.
+ *
+ * We remove the backslash or slash at the end and
+ * try again.
+ */
+
+ int len = strlen(fn);
+ if (len > 0 && len <= _MAX_PATH
+ && (fn[len - 1] == '\\' || fn[len - 1] == '/')) {
+ char newfn[_MAX_PATH + 1];
+
+ strcpy(newfn, fn);
+ newfn[len - 1] = '\0';
+ rv = _stat(newfn, (struct _stat *)info);
+ }
+ }
+
+ if (-1 == rv) {
+ _PR_MD_MAP_STAT_ERROR(errno);
+ }
+ return rv;
+}
+
+#define _PR_IS_SLASH(ch) ((ch) == '/' || (ch) == '\\')
+
+/*
+ * IsRootDirectory --
+ *
+ * Return PR_TRUE if the pathname 'fn' is a valid root directory,
+ * else return PR_FALSE. The char buffer pointed to by 'fn' must
+ * be writable. During the execution of this function, the contents
+ * of the buffer pointed to by 'fn' may be modified, but on return
+ * the original contents will be restored. 'buflen' is the size of
+ * the buffer pointed to by 'fn'.
+ *
+ * Root directories come in three formats:
+ * 1. / or \, meaning the root directory of the current drive.
+ * 2. C:/ or C:\, where C is a drive letter.
+ * 3. \\<server name>\<share point name>\ or
+ * \\<server name>\<share point name>, meaning the root directory
+ * of a UNC (Universal Naming Convention) name.
+ */
+
+static PRBool
+IsRootDirectory(char *fn, size_t buflen)
+{
+ char *p;
+ PRBool slashAdded = PR_FALSE;
+ PRBool rv = PR_FALSE;
+
+ if (_PR_IS_SLASH(fn[0]) && fn[1] == '\0') {
+ return PR_TRUE;
+ }
+
+ if (isalpha(fn[0]) && fn[1] == ':' && _PR_IS_SLASH(fn[2])
+ && fn[3] == '\0') {
+ rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
+ return rv;
+ }
+
+ /* The UNC root directory */
+
+ if (_PR_IS_SLASH(fn[0]) && _PR_IS_SLASH(fn[1])) {
+ /* The 'server' part should have at least one character. */
+ p = &fn[2];
+ if (*p == '\0' || _PR_IS_SLASH(*p)) {
+ return PR_FALSE;
+ }
+
+ /* look for the next slash */
+ do {
+ p++;
+ } while (*p != '\0' && !_PR_IS_SLASH(*p));
+ if (*p == '\0') {
+ return PR_FALSE;
+ }
+
+ /* The 'share' part should have at least one character. */
+ p++;
+ if (*p == '\0' || _PR_IS_SLASH(*p)) {
+ return PR_FALSE;
+ }
+
+ /* look for the final slash */
+ do {
+ p++;
+ } while (*p != '\0' && !_PR_IS_SLASH(*p));
+ if (_PR_IS_SLASH(*p) && p[1] != '\0') {
+ return PR_FALSE;
+ }
+ if (*p == '\0') {
+ /*
+ * GetDriveType() doesn't work correctly if the
+ * path is of the form \\server\share, so we add
+ * a final slash temporarily.
+ */
+ if ((p + 1) < (fn + buflen)) {
+ *p++ = '\\';
+ *p = '\0';
+ slashAdded = PR_TRUE;
+ } else {
+ return PR_FALSE; /* name too long */
+ }
+ }
+ rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
+ /* restore the 'fn' buffer */
+ if (slashAdded) {
+ *--p = '\0';
+ }
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info)
+{
+ HANDLE hFindFile;
+ WIN32_FIND_DATA findFileData;
+ char pathbuf[MAX_PATH + 1];
+
+ if (NULL == fn || '\0' == *fn) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+
+ /*
+ * FindFirstFile() expands wildcard characters. So
+ * we make sure the pathname contains no wildcard.
+ */
+ if (NULL != strpbrk(fn, "?*")) {
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, 0);
+ return -1;
+ }
+
+ hFindFile = FindFirstFile(fn, &findFileData);
+ if (INVALID_HANDLE_VALUE == hFindFile) {
+ DWORD len;
+ char *filePart;
+
+ /*
+ * FindFirstFile() does not work correctly on root directories.
+ * It also doesn't work correctly on a pathname that ends in a
+ * slash. So we first check to see if the pathname specifies a
+ * root directory. If not, and if the pathname ends in a slash,
+ * we remove the final slash and try again.
+ */
+
+ /*
+ * If the pathname does not contain ., \, and /, it cannot be
+ * a root directory or a pathname that ends in a slash.
+ */
+ if (NULL == strpbrk(fn, ".\\/")) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return -1;
+ }
+ len = GetFullPathName(fn, sizeof(pathbuf), pathbuf,
+ &filePart);
+ PR_ASSERT(0 != len);
+ if (len > sizeof(pathbuf)) {
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
+ return -1;
+ }
+ if (IsRootDirectory(pathbuf, sizeof(pathbuf))) {
+ info->type = PR_FILE_DIRECTORY;
+ info->size = 0;
+ /*
+ * These timestamps don't make sense for root directories.
+ */
+ info->modifyTime = 0;
+ info->creationTime = 0;
+ return 0;
+ }
+ if (!_PR_IS_SLASH(pathbuf[len - 1])) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return -1;
+ } else {
+ pathbuf[len - 1] = '\0';
+ hFindFile = FindFirstFile(pathbuf, &findFileData);
+ if (INVALID_HANDLE_VALUE == hFindFile) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return -1;
+ }
+ }
+ }
+
+ FindClose(hFindFile);
+
+ if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ info->type = PR_FILE_DIRECTORY;
+ } else {
+ info->type = PR_FILE_FILE;
+ }
+
+ info->size = findFileData.nFileSizeHigh;
+ info->size = (info->size << 32) + findFileData.nFileSizeLow;
+
+ _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime);
+
+ if (0 == findFileData.ftCreationTime.dwLowDateTime &&
+ 0 == findFileData.ftCreationTime.dwHighDateTime) {
+ info->creationTime = info->modifyTime;
+ } else {
+ _PR_FileTimeToPRTime(&findFileData.ftCreationTime,
+ &info->creationTime);
+ }
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info)
+{
+ PRFileInfo64 info64;
+ PRInt32 rv = _PR_MD_GETFILEINFO64(fn, &info64);
+ if (0 == rv)
+ {
+ info->type = info64.type;
+ info->size = (PRUint32) info64.size;
+ info->modifyTime = info64.modifyTime;
+ info->creationTime = info64.creationTime;
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info)
+{
+ int rv;
+
+ BY_HANDLE_FILE_INFORMATION hinfo;
+
+ rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo);
+ if (rv == FALSE) {
+ _PR_MD_MAP_FSTAT_ERROR(GetLastError());
+ return -1;
+ }
+
+ if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ info->type = PR_FILE_DIRECTORY;
+ else
+ info->type = PR_FILE_FILE;
+
+ info->size = hinfo.nFileSizeHigh;
+ info->size = (info->size << 32) + hinfo.nFileSizeLow;
+
+ _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) );
+ _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) );
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info)
+{
+ int rv;
+
+ BY_HANDLE_FILE_INFORMATION hinfo;
+
+ rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo);
+ if (rv == FALSE) {
+ _PR_MD_MAP_FSTAT_ERROR(GetLastError());
+ return -1;
+ }
+
+ if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ info->type = PR_FILE_DIRECTORY;
+ else
+ info->type = PR_FILE_FILE;
+
+ info->size = hinfo.nFileSizeLow;
+
+ _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) );
+ _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) );
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_RENAME(const char *from, const char *to)
+{
+ /* Does this work with dot-relative pathnames? */
+ if (MoveFile(from, to)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_RENAME_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+PRInt32
+_PR_MD_ACCESS(const char *name, PRIntn how)
+{
+ PRInt32 rv;
+
+ switch (how) {
+ case PR_ACCESS_WRITE_OK:
+ rv = _access(name, 02);
+ break;
+ case PR_ACCESS_READ_OK:
+ rv = _access(name, 04);
+ break;
+ case PR_ACCESS_EXISTS:
+ rv = _access(name, 00);
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_ACCESS_ERROR(errno);
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_MKDIR(const char *name, PRIntn mode)
+{
+ /* XXXMB - how to translate the "mode"??? */
+ if (CreateDirectory(name, NULL)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_MKDIR_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+PRInt32
+_PR_MD_RMDIR(const char *name)
+{
+ if (RemoveDirectory(name)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_RMDIR_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+PRStatus
+_PR_MD_LOCKFILE(PRInt32 f)
+{
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
+
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+
+ me->state = _PR_IO_WAIT;
+ me->io_pending = PR_TRUE;
+ rv = LockFileEx((HANDLE)f,
+ LOCKFILE_EXCLUSIVE_LOCK,
+ 0,
+ 0x7fffffff,
+ 0,
+ &me->md.overlapped);
+
+ /* HACK AROUND NT BUG
+ * NT 3.51 has a bug. In NT 3.51, if LockFileEx returns true, you
+ * don't get any completion on the completion port. This is a bug.
+ *
+ * They fixed it on NT4.0 so that you do get a completion.
+ *
+ * If we pretend we won't get a completion, NSPR gets confused later
+ * when the unexpected completion arrives. If we assume we do get
+ * a completion, we hang on 3.51. Worse, Microsoft informs me that the
+ * behavior varies on 3.51 depending on if you are using a network
+ * file system or a local disk!
+ *
+ * Solution: For now, _nt_version_gets_lockfile_completion is set
+ * depending on whether or not this system is EITHER
+ * - running NT 4.0
+ * - running NT 3.51 with a service pack greater than 5.
+ *
+ * In the meantime, this code may not work on network file systems.
+ *
+ */
+
+ if ( rv == FALSE && ((err = GetLastError()) != ERROR_IO_PENDING)) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ _PR_MD_MAP_LOCKF_ERROR(err);
+ return PR_FAILURE;
+ }
+#ifdef _NEED_351_FILE_LOCKING_HACK
+ else if (rv) {
+ /* If this is NT 3.51 and the file is local, then we won't get a
+ * completion back from LockFile when it succeeded.
+ */
+ if (_nt_version_gets_lockfile_completion == PR_FALSE) {
+ if ( IsFileLocal((HANDLE)f) == _PR_LOCAL_FILE) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ return PR_SUCCESS;
+ }
+ }
+ }
+#endif /* _NEED_351_FILE_LOCKING_HACK */
+
+ if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ return PR_FAILURE;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ _PR_MD_MAP_LOCKF_ERROR(me->md.blocked_io_error);
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+}
+
+PRStatus
+_PR_MD_TLOCKFILE(PRInt32 f)
+{
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
+
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+
+ me->state = _PR_IO_WAIT;
+ me->io_pending = PR_TRUE;
+ rv = LockFileEx((HANDLE)f,
+ LOCKFILE_FAIL_IMMEDIATELY|LOCKFILE_EXCLUSIVE_LOCK,
+ 0,
+ 0x7fffffff,
+ 0,
+ &me->md.overlapped);
+ if ( rv == FALSE && ((err = GetLastError()) != ERROR_IO_PENDING)) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ _PR_MD_MAP_LOCKF_ERROR(me->md.blocked_io_error);
+ return PR_FAILURE;
+ }
+#ifdef _NEED_351_FILE_LOCKING_HACK
+ else if (rv) {
+ /* If this is NT 3.51 and the file is local, then we won't get a
+ * completion back from LockFile when it succeeded.
+ */
+ if (_nt_version_gets_lockfile_completion == PR_FALSE) {
+ if ( IsFileLocal((HANDLE)f) == _PR_LOCAL_FILE) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ return PR_SUCCESS;
+ }
+ }
+ }
+#endif /* _NEED_351_FILE_LOCKING_HACK */
+
+ if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ return PR_FAILURE;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ _PR_MD_MAP_LOCKF_ERROR(me->md.blocked_io_error);
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+}
+
+
+PRStatus
+_PR_MD_UNLOCKFILE(PRInt32 f)
+{
+ PRInt32 rv;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
+
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+
+ rv = UnlockFileEx((HANDLE)f,
+ 0,
+ 0x7fffffff,
+ 0,
+ &me->md.overlapped);
+
+ if (rv)
+ return PR_SUCCESS;
+ else {
+ int err = GetLastError();
+ _PR_MD_MAP_LOCKF_ERROR(err);
+ return PR_FAILURE;
+ }
+}
+
+void
+_PR_MD_MAKE_NONBLOCK(PRFileDesc *f)
+{
+ /*
+ * On NT, we either call _md_Associate() or _md_MakeNonblock(),
+ * depending on whether the socket is blocking or not.
+ *
+ * Once we associate a socket with the io completion port,
+ * there is no way to disassociate it from the io completion
+ * port. So we have to call _md_Associate/_md_MakeNonblock
+ * lazily.
+ */
+}
+
+#ifdef _NEED_351_FILE_LOCKING_HACK
+/***************
+**
+** Lockfile hacks
+**
+** The following code is a hack to work around a microsoft bug with lockfile.
+** The problem is that on NT 3.51, if LockFileEx() succeeds, you never
+** get a completion back for files that are on local disks. So, we need to
+** know if a file is local or remote so we can tell if we should expect
+** a completion.
+**
+** The only way to check if a file is local or remote based on the handle is
+** to get the serial number for the volume it is mounted on and then to
+** compare that with mounted drives. This code caches the volume numbers of
+** fixed disks and does a relatively quick check.
+**
+** Locking: Since the only thing we ever do when multithreaded is a 32bit
+** assignment, we probably don't need locking. It is included just
+** case anyway.
+**
+** Limitations: Does not work on floppies because they are too slow
+** Unknown if it will work on wierdo 3rd party file systems
+**
+****************
+*/
+
+/* There can only be 26 drive letters on NT */
+#define _PR_MAX_DRIVES 26
+
+_MDLock cachedVolumeLock;
+DWORD dwCachedVolumeSerialNumbers[_PR_MAX_DRIVES] = {0};
+DWORD dwLastCachedDrive = 0;
+DWORD dwRemoveableDrivesToCheck = 0; /* bitmask for removeable drives */
+
+PRBool IsFileLocalInit()
+{
+ TCHAR lpBuffer[_PR_MAX_DRIVES*5];
+ DWORD nBufferLength = _PR_MAX_DRIVES*5;
+ DWORD nBufferNeeded = GetLogicalDriveStrings(0, NULL);
+ DWORD dwIndex = 0;
+ DWORD dwDriveType;
+ DWORD dwVolumeSerialNumber;
+ DWORD dwDriveIndex = 0;
+ DWORD oldmode = (DWORD) -1;
+
+ _MD_NEW_LOCK(&cachedVolumeLock);
+
+ nBufferNeeded = GetLogicalDriveStrings(nBufferLength, lpBuffer);
+ if (nBufferNeeded == 0 || nBufferNeeded > nBufferLength)
+ return PR_FALSE;
+
+ // Calling GetVolumeInformation on a removeable drive where the
+ // disk is currently removed will cause a dialog box to the
+ // console. This is not good.
+ // Temporarily disable the SEM_FAILCRITICALERRORS to avoid the
+ // damn dialog.
+
+ dwCachedVolumeSerialNumbers[dwDriveIndex] = 0;
+ oldmode = SetErrorMode(SEM_FAILCRITICALERRORS);
+
+ // now loop through the logical drives
+ while(lpBuffer[dwIndex] != TEXT('\0'))
+ {
+ // skip the floppy drives. This is *SLOW*
+ if ((lpBuffer[dwIndex] == TEXT('A')) || (lpBuffer[dwIndex] == TEXT('B')))
+ /* Skip over floppies */;
+ else
+ {
+ dwDriveIndex = (lpBuffer[dwIndex] - TEXT('A'));
+
+ dwDriveType = GetDriveType(&lpBuffer[dwIndex]);
+
+ switch(dwDriveType)
+ {
+ // Ignore these drive types
+ case 0:
+ case 1:
+ case DRIVE_REMOTE:
+ default: // If the drive type is unknown, ignore it.
+ break;
+
+ // Removable media drives can have different serial numbers
+ // at different times, so cache the current serial number
+ // but keep track of them so they can be rechecked if necessary.
+ case DRIVE_REMOVABLE:
+
+ // CDROM is a removable media
+ case DRIVE_CDROM:
+
+ // no idea if ramdisks can change serial numbers or not
+ // but it doesn't hurt to treat them as removable.
+
+ case DRIVE_RAMDISK:
+
+
+ // Here is where we keep track of removable drives.
+ dwRemoveableDrivesToCheck |= 1 << dwDriveIndex;
+
+ // removable drives fall through to fixed drives and get cached.
+
+ case DRIVE_FIXED:
+
+ // cache volume serial numbers.
+ if (GetVolumeInformation(
+ &lpBuffer[dwIndex],
+ NULL, 0,
+ &dwVolumeSerialNumber,
+ NULL, NULL, NULL, 0)
+ )
+ {
+ if (dwLastCachedDrive < dwDriveIndex)
+ dwLastCachedDrive = dwDriveIndex;
+ dwCachedVolumeSerialNumbers[dwDriveIndex] = dwVolumeSerialNumber;
+ }
+
+ break;
+ }
+ }
+
+ dwIndex += lstrlen(&lpBuffer[dwIndex]) +1;
+ }
+
+ if (oldmode != (DWORD) -1) {
+ SetErrorMode(oldmode);
+ oldmode = (DWORD) -1;
+ }
+
+ return PR_TRUE;
+}
+
+PRInt32 IsFileLocal(HANDLE hFile)
+{
+ DWORD dwIndex = 0, dwMask;
+ BY_HANDLE_FILE_INFORMATION Info;
+ TCHAR szDrive[4] = TEXT("C:\\");
+ DWORD dwVolumeSerialNumber;
+ DWORD oldmode = (DWORD) -1;
+ int rv = _PR_REMOTE_FILE;
+
+ if (!GetFileInformationByHandle(hFile, &Info))
+ return -1;
+
+ // look to see if the volume serial number has been cached.
+ _MD_LOCK(&cachedVolumeLock);
+ while(dwIndex <= dwLastCachedDrive)
+ if (dwCachedVolumeSerialNumbers[dwIndex++] == Info.dwVolumeSerialNumber)
+ return _PR_LOCAL_FILE;
+ _MD_UNLOCK(&cachedVolumeLock);
+
+ // volume serial number not found in the cache. Check removable files.
+ // removable drives are noted as a bitmask. If the bit associated with
+ // a specific drive is set, then we should query its volume serial number
+ // as its possible it has changed.
+ dwMask = dwRemoveableDrivesToCheck;
+ dwIndex = 0;
+
+ while(dwMask)
+ {
+ while(!(dwMask & 1))
+ {
+ dwIndex++;
+ dwMask = dwMask >> 1;
+ }
+
+ szDrive[0] = TEXT('A')+ (TCHAR) dwIndex;
+
+ // Calling GetVolumeInformation on a removeable drive where the
+ // disk is currently removed will cause a dialog box to the
+ // console. This is not good.
+ // Temporarily disable the SEM_FAILCRITICALERRORS to avoid the
+ // dialog.
+
+ oldmode = SetErrorMode(SEM_FAILCRITICALERRORS);
+
+ if (GetVolumeInformation(
+ szDrive,
+ NULL, 0,
+ &dwVolumeSerialNumber,
+ NULL, NULL, NULL, 0)
+ )
+ {
+ if (dwVolumeSerialNumber == Info.dwVolumeSerialNumber)
+ {
+ _MD_LOCK(&cachedVolumeLock);
+ if (dwLastCachedDrive < dwIndex)
+ dwLastCachedDrive = dwIndex;
+ dwCachedVolumeSerialNumbers[dwIndex] = dwVolumeSerialNumber;
+ _MD_UNLOCK(&cachedVolumeLock);
+ rv = _PR_LOCAL_FILE;
+ }
+ }
+ if (oldmode != (DWORD) -1) {
+ SetErrorMode(oldmode);
+ oldmode = (DWORD) -1;
+ }
+
+ if (rv == _PR_LOCAL_FILE)
+ return _PR_LOCAL_FILE;
+
+ dwIndex++;
+ dwMask = dwMask >> 1;
+ }
+
+ return _PR_REMOTE_FILE;
+}
+#endif /* _NEED_351_FILE_LOCKING_HACK */
+
+void PR_NT_UseNonblock()
+{
+ _nt_use_async = 0;
+}
+
+
+PRInt32 _nt_nonblock_accept(PRFileDesc *fd, struct sockaddr_in *addr, int *len, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ fd_set rd;
+ struct timeval tv, *tvp;
+
+ FD_ZERO(&rd);
+ FD_SET((SOCKET)osfd, &rd);
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ while ((rv = accept(osfd, (struct sockaddr *) addr, len)) == -1) {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking)) {
+ if ((rv = select(osfd + 1, &rd, NULL, NULL,NULL)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ } else {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ break;
+ }
+ }
+ return(rv);
+ } else if (timeout == PR_INTERVAL_NO_WAIT) {
+ if ((rv = accept(osfd, (struct sockaddr *) addr, len)) == -1)
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking)) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ } else {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ }
+ return(rv);
+ } else {
+retry:
+ if ((rv = accept(osfd, (struct sockaddr *) addr, len)) == -1) {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking)) {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+
+ rv = select(osfd + 1, &rd, NULL, NULL, tvp);
+ if (rv > 0) {
+ goto retry;
+ } else if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ } else
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ } else {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ }
+ }
+ }
+ return(rv);
+}
+
+PRInt32 _nt_nonblock_recv(PRFileDesc *fd, char *buf, int len, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ struct timeval tv, *tvp;
+ fd_set rd;
+
+ while ((rv = recv(osfd,buf,len,0)) == -1) {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking)) {
+ FD_ZERO(&rd);
+ FD_SET((SOCKET)osfd, &rd);
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ if ((rv = select(osfd + 1, &rd, NULL,NULL,tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ return -1;
+ } else if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ } else {
+ _PR_MD_MAP_RECV_ERROR(err);
+ break;
+ }
+ }
+ return(rv);
+}
+
+PRInt32 _nt_nonblock_send(PRFileDesc *fd, char *buf, int len, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ struct timeval tv, *tvp;
+ fd_set wd;
+ PRInt32 bytesSent = 0;
+
+ while(bytesSent < len) {
+ while ((rv = send(osfd,buf,len,0)) == -1) {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking)) {
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ if ((rv = select(osfd + 1, NULL, &wd, NULL,tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ } else {
+ _PR_MD_MAP_SEND_ERROR(err);
+ return -1;
+ }
+ }
+ bytesSent += rv;
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if ((rv >= 0) && (bytesSent < len)) {
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ if ((rv = select(osfd + 1, NULL, &wd, NULL,tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ }
+ }
+ return bytesSent;
+}
+
+PRInt32 _nt_nonblock_writev(PRFileDesc *fd, PRIOVec *iov, int size, PRIntervalTime timeout)
+{
+ int index;
+ int sent = 0;
+ int rv;
+
+ for (index=0; index<size; index++) {
+ rv = _nt_nonblock_send(fd, iov[index].iov_base, iov[index].iov_len, timeout);
+ if (rv > 0)
+ sent += rv;
+ if ( rv != iov[index].iov_len ) {
+ if (rv <= 0) {
+ if (fd->secret->nonblocking
+ && (PR_GetError() == PR_WOULD_BLOCK_ERROR)
+ && (sent > 0)) {
+ return sent;
+ } else {
+ return -1;
+ }
+ }
+ /* Only a nonblocking socket can have partial sends */
+ PR_ASSERT(fd->secret->nonblocking);
+ return sent;
+ }
+ }
+
+ return sent;
+}
+
+PRInt32 _nt_nonblock_sendto(
+ PRFileDesc *fd, const char *buf, int len,
+ const struct sockaddr *addr, int addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ struct timeval tv, *tvp;
+ fd_set wd;
+ PRInt32 bytesSent = 0;
+
+ while(bytesSent < len) {
+ while ((rv = sendto(osfd,buf,len,0, addr, addrlen)) == -1) {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking)) {
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ if ((rv = select(osfd + 1, NULL, &wd, NULL,tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ } else {
+ _PR_MD_MAP_SENDTO_ERROR(err);
+ return -1;
+ }
+ }
+ bytesSent += rv;
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if ((rv >= 0) && (bytesSent < len)) {
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ if ((rv = select(osfd + 1, NULL, &wd, NULL,tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ }
+ }
+ return bytesSent;
+}
+
+PRInt32 _nt_nonblock_recvfrom(PRFileDesc *fd, char *buf, int len, struct sockaddr *addr, int *addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ struct timeval tv, *tvp;
+ fd_set rd;
+
+ while ((rv = recvfrom(osfd,buf,len,0,addr, addrlen)) == -1) {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking)) {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&rd);
+ FD_SET((SOCKET)osfd, &rd);
+ if ((rv = select(osfd + 1, &rd, NULL,NULL,tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ return -1;
+ } else if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ } else {
+ _PR_MD_MAP_RECVFROM_ERROR(err);
+ break;
+ }
+ }
+ return(rv);
+}
+
+/*
+ * UDP support: the continuation thread functions and recvfrom and sendto.
+ */
+
+static void pt_InsertTimedInternal(pt_Continuation *op)
+{
+ PRInt32 delta = 0;
+ pt_Continuation *t_op = NULL;
+ PRIntervalTime now = PR_IntervalNow(), op_tmo, qd_tmo;
+
+ /*
+ * If this element operation isn't timed, it gets queued at the
+ * end of the list (just after pt_tq.tail) and we're
+ * finishd early.
+ */
+ if (PR_INTERVAL_NO_TIMEOUT == op->timeout)
+ {
+ t_op = pt_tq.tail; /* put it at the end */
+ goto done;
+ }
+
+ /*
+ * The rest of this routine actaully deals with timed ops.
+ */
+
+ if (NULL != pt_tq.op)
+ {
+ /*
+ * To find where in the list to put the new operation, form
+ * the absolute time the operations in question will expire.
+ *
+ * The new operation ('op') will expire at now() + op->timeout.
+ *
+ * The operation that will time out furthest in the future will
+ * do so at pt_tq.epoch + pt_tq.op->timeout.
+ *
+ * Subsequently earlier timeouts are computed based on the latter
+ * knowledge by subracting the timeout deltas that are stored in
+ * the operation list. There are operation[n]->timeout ticks
+ * between the expiration of operation[n-1] and operation[n].e e
+ *
+ * Therefore, the operation[n-1] will expire operation[n]->timeout
+ * ticks prior to operation[n].
+ *
+ * This should be easy!
+ */
+ t_op = pt_tq.op; /* running pointer to queued op */
+ op_tmo = now + op->timeout; /* that's in absolute ticks */
+ qd_tmo = pt_tq.epoch + t_op->timeout; /* likewise */
+
+ do
+ {
+ /*
+ * If 'op' expires later than t_op, then insert 'op' just
+ * ahead of t_op. Otherwise, compute when operation[n-1]
+ * expires and try again.
+ *
+ * The actual different between the expiriation of 'op'
+ * and the current operation what becomes the new operaton's
+ * timeout interval. That interval is also subtracted from
+ * the interval of the operation immediately following where
+ * we stick 'op' (unless the next one isn't timed). The new
+ * timeout assigned to 'op' takes into account the values of
+ * now() and when the previous intervals were compured.
+ */
+ delta = op_tmo - qd_tmo;
+ if (delta >= 0)
+ {
+ op->timeout += (now - pt_tq.epoch);
+ goto done;
+ }
+
+ qd_tmo -= t_op->timeout; /* previous operaton expiration */
+ t_op = t_op->prev; /* point to previous operation */
+ if (NULL != t_op) qd_tmo += t_op->timeout;
+ } while (NULL != t_op);
+
+ /*
+ * If we got here we backed off the head of the list. That means that
+ * this timed entry has to go at the head of the list. This is just
+ * about like having an empty timer list.
+ */
+ delta = op->timeout; /* $$$ is this right? */
+ }
+
+done:
+
+ /*
+ * Insert 'op' into the queue just after t_op or if t_op is null,
+ * at the head of the list.
+ *
+ * If t_op is NULL, the list is currently empty and this is pretty
+ * easy.
+ */
+ if (NULL == t_op)
+ {
+ op->prev = NULL;
+ op->next = pt_tq.head;
+ pt_tq.head = op;
+ if (NULL == pt_tq.tail) pt_tq.tail = op;
+ else op->next->prev = op;
+ }
+ else
+ {
+ op->prev = t_op;
+ op->next = t_op->next;
+ if (NULL != op->prev)
+ op->prev->next = op;
+ if (NULL != op->next)
+ op->next->prev = op;
+ if (t_op == pt_tq.tail)
+ pt_tq.tail = op;
+ }
+
+ /*
+ * Are we adjusting our epoch, etc? Are we replacing
+ * what was previously the element due to expire furthest
+ * out in the future? Is this even a timed operation?
+ */
+ if (PR_INTERVAL_NO_TIMEOUT != op->timeout)
+ {
+ if ((NULL == pt_tq.op) /* we're the one and only */
+ || (t_op == pt_tq.op)) /* we're replacing */
+ {
+ pt_tq.op = op;
+ pt_tq.epoch = now;
+ }
+ }
+
+ pt_tq.op_count += 1;
+
+} /* pt_InsertTimedInternal */
+
+/*
+ * function: pt_FinishTimed
+ *
+ * Takes the finished operation out of the timed queue. It
+ * notifies the initiating thread that the opertions is
+ * complete and returns to the caller the value of the next
+ * operation in the list (or NULL).
+ */
+static pt_Continuation *pt_FinishTimedInternal(pt_Continuation *op)
+{
+ pt_Continuation *next;
+
+ /* remove this one from the list */
+ if (NULL == op->prev) pt_tq.head = op->next;
+ else op->prev->next = op->next;
+ if (NULL == op->next) pt_tq.tail = op->prev;
+ else op->next->prev = op->prev;
+
+ /* did we happen to hit the timed op? */
+ if (op == pt_tq.op) pt_tq.op = op->prev;
+
+ next = op->next;
+ op->next = op->prev = NULL;
+ op->status = pt_continuation_done;
+
+ pt_tq.op_count -= 1;
+#if defined(DEBUG)
+ pt_debug.continuationsServed += 1;
+#endif
+ PR_NotifyCondVar(op->complete);
+
+ return next;
+} /* pt_FinishTimedInternal */
+
+static void ContinuationThread(void *arg)
+{
+ /* initialization */
+ fd_set readSet, writeSet, exceptSet;
+ struct timeval tv;
+ SOCKET *pollingList = 0; /* list built for polling */
+ PRIntn pollingListUsed; /* # entries used in the list */
+ PRIntn pollingListNeeded; /* # entries needed this time */
+ PRIntn pollingSlotsAllocated = 0; /* # entries available in list */
+ PRIntervalTime mx_select_ticks = PR_MillisecondsToInterval(PT_DEFAULT_SELECT_MSEC);
+
+ /* do some real work */
+ while (1)
+ {
+ PRIntn rv;
+ PRStatus status;
+ PRIntn pollIndex;
+ pt_Continuation *op;
+ PRIntervalTime now = PR_IntervalNow();
+ PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT;
+
+ PR_Lock(pt_tq.ml);
+ while (NULL == pt_tq.head)
+ {
+ status = PR_WaitCondVar(pt_tq.new_op, PR_INTERVAL_NO_TIMEOUT);
+ if ((PR_FAILURE == status)
+ && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) break;
+ }
+ pollingListNeeded = pt_tq.op_count;
+ PR_Unlock(pt_tq.ml);
+
+ /* Okay. We're history */
+ if ((PR_FAILURE == status)
+ && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) break;
+
+ /*
+ * We are not holding the pt_tq.ml lock now, so more items may
+ * get added to pt_tq during this window of time. We hope
+ * that 10 more spaces in the polling list should be enough.
+ */
+
+ FD_ZERO(&readSet);
+ FD_ZERO(&writeSet);
+ FD_ZERO(&exceptSet);
+ pollingListNeeded += 10;
+ if (pollingListNeeded > pollingSlotsAllocated)
+ {
+ if (NULL != pollingList) PR_DELETE(pollingList);
+ pollingList = PR_MALLOC(pollingListNeeded * sizeof(PRPollDesc));
+ PR_ASSERT(NULL != pollingList);
+ pollingSlotsAllocated = pollingListNeeded;
+ }
+
+#if defined(DEBUG)
+ if (pollingListNeeded > pt_debug.pollingListMax)
+ pt_debug.pollingListMax = pollingListUsed;
+#endif
+
+ /*
+ * Build up a polling list.
+ * This list is sorted on time. Operations that have been
+ * interrupted are completed and not included in the list.
+ * There is an assertion that the operation is in progress.
+ */
+ pollingListUsed = 0;
+ PR_Lock(pt_tq.ml);
+
+ for (op = pt_tq.head; NULL != op;)
+ {
+ if (pt_continuation_abort == op->status)
+ {
+ op->result.code = -1;
+ op->syserrno = WSAEINTR;
+ op = pt_FinishTimedInternal(op);
+ }
+ else
+ {
+ PR_ASSERT(pt_continuation_done != op->status);
+ op->status = pt_continuation_inprogress;
+ if (op->event & PR_POLL_READ) {
+ FD_SET(op->arg1.osfd, &readSet);
+ }
+ if (op->event & PR_POLL_WRITE) {
+ FD_SET(op->arg1.osfd, &writeSet);
+ }
+ if (op->event & PR_POLL_EXCEPT) {
+ FD_SET(op->arg1.osfd, &exceptSet);
+ }
+ pollingList[pollingListUsed] = op->arg1.osfd;
+ pollingListUsed += 1;
+ if (pollingListUsed == pollingSlotsAllocated) break;
+ op = op->next;
+ }
+ }
+
+ PR_Unlock(pt_tq.ml);
+
+ /*
+ * If 'op' isn't NULL at this point, then we didn't get to
+ * the end of the list. That means that more items got added
+ * to the list than we anticipated. So, forget this iteration,
+ * go around the horn again.
+ * One would hope this doesn't happen all that often.
+ */
+ if (NULL != op)
+ {
+#if defined(DEBUG)
+ pt_debug.predictionsFoiled += 1; /* keep track */
+#endif
+ continue; /* make it rethink things */
+ }
+
+ /* there's a chance that all ops got blown away */
+ if (NULL == pt_tq.head) continue;
+ /* if not, we know this is the shortest timeout */
+ timeout = pt_tq.head->timeout;
+
+ /*
+ * We don't want to wait forever on this poll. So keep
+ * the interval down. The operations, if they are timed,
+ * still have to timeout, while those that are not timed
+ * should persist forever. But they may be aborted. That's
+ * what this anxiety is all about.
+ */
+ if (timeout > mx_select_ticks) timeout = mx_select_ticks;
+
+ if (PR_INTERVAL_NO_TIMEOUT != pt_tq.head->timeout)
+ pt_tq.head->timeout -= timeout;
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(timeout) % PR_USEC_PER_SEC;
+
+ rv = select(0, &readSet, &writeSet, &exceptSet, &tv);
+
+ if (0 == rv) /* poll timed out - what about leading op? */
+ {
+ if (0 == pt_tq.head->timeout)
+ {
+ /*
+ * The leading element of the timed queue has timed
+ * out. Get rid of it. In any case go around the
+ * loop again, computing the polling list, checking
+ * for interrupted operations.
+ */
+ PR_Lock(pt_tq.ml);
+ do
+ {
+ pt_tq.head->result.code = -1;
+ pt_tq.head->syserrno = WSAETIMEDOUT;
+ op = pt_FinishTimedInternal(pt_tq.head);
+ } while ((NULL != op) && (0 == op->timeout));
+ PR_Unlock(pt_tq.ml);
+ }
+ continue;
+ }
+
+ if (-1 == rv && (WSAGetLastError() == WSAEINTR
+ || WSAGetLastError() == WSAEINPROGRESS))
+ {
+ continue; /* go around the loop again */
+ }
+
+ /*
+ * select() says that something in our list is ready for some more
+ * action or is an invalid fd. Find it, load up the operation and
+ * see what happens.
+ */
+
+ PR_ASSERT(rv > 0 || WSAGetLastError() == WSAENOTSOCK);
+
+
+ /*
+ * $$$ There's a problem here. I'm running the operations list
+ * and I'm not holding any locks. I don't want to hold the lock
+ * and do the operation, so this is really messed up..
+ *
+ * This may work out okay. The rule is that only this thread,
+ * the continuation thread, can remove elements from the list.
+ * Therefore, the list is at worst, longer than when we built
+ * the polling list.
+ */
+ op = pt_tq.head;
+ for (pollIndex = 0; pollIndex < pollingListUsed; ++pollIndex)
+ {
+ PRInt16 revents = 0;
+
+ PR_ASSERT(NULL != op);
+
+ /*
+ * This one wants attention. Redo the operation.
+ * We know that there can only be more elements
+ * in the op list than we knew about when we created
+ * the poll list. Therefore, we might have to skip
+ * a few ops to find the right one to operation on.
+ */
+ while (pollingList[pollIndex] != op->arg1.osfd )
+ {
+ op = op->next;
+ PR_ASSERT(NULL != op);
+ }
+
+ if (FD_ISSET(op->arg1.osfd, &readSet)) {
+ revents |= PR_POLL_READ;
+ }
+ if (FD_ISSET(op->arg1.osfd, &writeSet)) {
+ revents |= PR_POLL_WRITE;
+ }
+ if (FD_ISSET(op->arg1.osfd, &exceptSet)) {
+ revents |= PR_POLL_EXCEPT;
+ }
+
+ /*
+ * Sip over all those not in progress. They'll be
+ * pruned next time we build a polling list. Call
+ * the continuation function. If it reports completion,
+ * finish off the operation.
+ */
+ if (revents && (pt_continuation_inprogress == op->status)
+ && (op->function(op, revents)))
+ {
+ PR_Lock(pt_tq.ml);
+ op = pt_FinishTimedInternal(op);
+ PR_Unlock(pt_tq.ml);
+ }
+ }
+ }
+ if (NULL != pollingList) PR_DELETE(pollingList);
+} /* ContinuationThread */
+
+static int pt_Continue(pt_Continuation *op)
+{
+ PRStatus rv;
+ /* Finish filling in the blank slots */
+ op->status = pt_continuation_sumbitted;
+ op->complete = PR_NewCondVar(pt_tq.ml);
+
+ PR_Lock(pt_tq.ml); /* we provide the locking */
+
+ pt_InsertTimedInternal(op); /* insert in the structure */
+
+ PR_NotifyCondVar(pt_tq.new_op); /* notify the continuation thread */
+
+ while (pt_continuation_done != op->status) /* wait for completion */
+ {
+ rv = PR_WaitCondVar(op->complete, PR_INTERVAL_NO_TIMEOUT);
+ /*
+ * If we get interrupted, we set state the continuation thread will
+ * see and allow it to finish the I/O operation w/ error. That way
+ * the rule that only the continuation thread is removing elements
+ * from the list is still valid.
+ *
+ * Don't call interrupt on the continuation thread. That'll just
+ * piss him off. He's cycling around at least every mx_select_ticks
+ * anyhow and should notice the request in there.
+ */
+ if ((PR_FAILURE == rv)
+ && (PR_PENDING_INTERRUPT_ERROR == PR_GetError()))
+ op->status = pt_continuation_abort; /* our status */
+ }
+
+ PR_Unlock(pt_tq.ml); /* we provide the locking */
+
+ PR_DestroyCondVar(op->complete);
+
+ return op->result.code; /* and the primary answer */
+} /* pt_Continue */
+
+static PRBool pt_sendto_cont(pt_Continuation *op, PRInt16 revents)
+{
+ PRIntn bytes = sendto(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags,
+ (struct sockaddr*)op->arg5.addr, sizeof(*(op->arg5.addr)));
+ op->syserrno = WSAGetLastError();
+ if (bytes > 0) /* this is progress */
+ {
+ char *bp = op->arg2.buffer;
+ bp += bytes; /* adjust the buffer pointer */
+ op->arg2.buffer = bp;
+ op->result.code += bytes; /* accumulate the number sent */
+ op->arg3.amount -= bytes; /* and reduce the required count */
+ return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE;
+ }
+ else return ((-1 == bytes) && (WSAEWOULDBLOCK == op->syserrno)) ?
+ PR_FALSE : PR_TRUE;
+} /* pt_sendto_cont */
+
+static PRBool pt_recvfrom_cont(pt_Continuation *op, PRInt16 revents)
+{
+ PRIntn addr_len = sizeof(*(op->arg5.addr));
+ op->result.code = recvfrom(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.amount,
+ op->arg4.flags, (struct sockaddr*)op->arg5.addr, &addr_len);
+ op->syserrno = WSAGetLastError();
+ return ((-1 == op->result.code) && (WSAEWOULDBLOCK == op->syserrno)) ?
+ PR_FALSE : PR_TRUE;
+} /* pt_recvfrom_cont */
+
+static PRInt32 pt_SendTo(
+ SOCKET osfd, const void *buf,
+ PRInt32 amount, PRInt32 flags, const PRNetAddr *addr,
+ PRIntn addrlen, PRIntervalTime timeout)
+{
+ PRInt32 bytes = -1, err;
+ PRBool fNeedContinue = PR_FALSE;
+
+ bytes = sendto(
+ osfd, buf, amount, flags,
+ (struct sockaddr*)addr, PR_NETADDR_SIZE(addr));
+ if (bytes == -1) {
+ if ((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ fNeedContinue = PR_TRUE;
+ else
+ _PR_MD_MAP_SENDTO_ERROR(err);
+ }
+ if (fNeedContinue == PR_TRUE)
+ {
+ pt_Continuation op;
+ op.arg1.osfd = osfd;
+ op.arg2.buffer = (void*)buf;
+ op.arg3.amount = amount;
+ op.arg4.flags = flags;
+ op.arg5.addr = addr;
+ op.timeout = timeout;
+ op.result.code = 0; /* initialize the number sent */
+ op.function = pt_sendto_cont;
+ op.event = PR_POLL_WRITE | PR_POLL_EXCEPT;
+ bytes = pt_Continue(&op);
+ if (bytes < 0) {
+ WSASetLastError(op.syserrno);
+ _PR_MD_MAP_SENDTO_ERROR(op.syserrno);
+ }
+ }
+ return bytes;
+} /* pt_SendTo */
+
+static PRInt32 pt_RecvFrom(SOCKET osfd, void *buf, PRInt32 amount,
+ PRInt32 flags, PRNetAddr *addr, PRIntn *addr_len, PRIntervalTime timeout)
+{
+ PRInt32 bytes = -1, err;
+ PRBool fNeedContinue = PR_FALSE;
+
+ bytes = recvfrom(
+ osfd, buf, amount, flags,
+ (struct sockaddr*)addr, addr_len);
+ if (bytes == -1) {
+ if ((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ fNeedContinue = PR_TRUE;
+ else
+ _PR_MD_MAP_RECVFROM_ERROR(err);
+ }
+
+ if (fNeedContinue == PR_TRUE)
+ {
+ pt_Continuation op;
+ op.arg1.osfd = osfd;
+ op.arg2.buffer = buf;
+ op.arg3.amount = amount;
+ op.arg4.flags = flags;
+ op.arg5.addr = addr;
+ op.timeout = timeout;
+ op.function = pt_recvfrom_cont;
+ op.event = PR_POLL_READ | PR_POLL_EXCEPT;
+ bytes = pt_Continue(&op);
+ if (bytes < 0) {
+ WSASetLastError(op.syserrno);
+ _PR_MD_MAP_RECVFROM_ERROR(op.syserrno);
+ }
+ }
+ return bytes;
+} /* pt_RecvFrom */
diff --git a/pr/src/md/windows/ntmisc.c b/pr/src/md/windows/ntmisc.c
new file mode 100644
index 00000000..729bce4d
--- /dev/null
+++ b/pr/src/md/windows/ntmisc.c
@@ -0,0 +1,603 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * ntmisc.c
+ *
+ */
+
+#include "primpl.h"
+
+char *_PR_MD_GET_ENV(const char *name)
+{
+ return getenv(name);
+}
+
+PRIntn _PR_MD_PUT_ENV(const char *name)
+{
+ return putenv(name);
+}
+
+
+/*
+ **************************************************************************
+ **************************************************************************
+ **
+ ** Date and time routines
+ **
+ **************************************************************************
+ **************************************************************************
+ */
+
+#include <sys/timeb.h>
+
+/*
+ *-----------------------------------------------------------------------
+ *
+ * PR_Now --
+ *
+ * Returns the current time in microseconds since the epoch.
+ * The epoch is midnight January 1, 1970 GMT.
+ * The implementation is machine dependent. This is the
+ * implementation for Windows.
+ * Cf. time_t time(time_t *tp)
+ *
+ *-----------------------------------------------------------------------
+ */
+
+PRTime
+PR_Now(void)
+{
+ PRInt64 s, ms, ms2us, s2us;
+ struct timeb b;
+
+ ftime(&b);
+ LL_I2L(ms2us, PR_USEC_PER_MSEC);
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(s, b.time);
+ LL_I2L(ms, b.millitm);
+ LL_MUL(ms, ms, ms2us);
+ LL_MUL(s, s, s2us);
+ LL_ADD(s, s, ms);
+ return s;
+}
+
+/*
+ * The following code works around a bug in NT (Netscape Bugsplat
+ * Defect ID 47942).
+ *
+ * In Windows NT 3.51 and 4.0, if the local time zone does not practice
+ * daylight savings time, e.g., Arizona, Taiwan, and Japan, the global
+ * variables that _ftime() and localtime() depend on have the wrong
+ * default values:
+ * _tzname[0] "PST"
+ * _tzname[1] "PDT"
+ * _daylight 1
+ * _timezone 28800
+ *
+ * So at startup time, we need to invoke _PR_Win32InitTimeZone(), which
+ * on NT sets these global variables to the correct values (obtained by
+ * calling GetTimeZoneInformation().
+ */
+
+#include <time.h> /* for _tzname, _daylight, _timezone */
+
+void
+_PR_Win32InitTimeZone(void)
+{
+ OSVERSIONINFO version;
+ TIME_ZONE_INFORMATION tzinfo;
+
+ version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if (GetVersionEx(&version) != FALSE) {
+ /* Only Windows NT needs this hack */
+ if (version.dwPlatformId != VER_PLATFORM_WIN32_NT) {
+ return;
+ }
+ }
+
+ if (GetTimeZoneInformation(&tzinfo) == 0xffffffff) {
+ return; /* not much we can do if this failed */
+ }
+
+ /*
+ * I feel nervous about modifying these globals. I hope that no
+ * other thread is reading or modifying these globals simultaneously
+ * during nspr initialization.
+ *
+ * I am assuming that _tzname[0] and _tzname[1] point to static buffers
+ * and that the buffers are at least 32 byte long. My experiments show
+ * this is true, but of course this is undocumented. --wtc
+ *
+ * Convert time zone names from WCHAR to CHAR and copy them to
+ * the static buffers pointed to by _tzname[0] and _tzname[1].
+ * Ignore conversion errors, because it is _timezone and _daylight
+ * that _ftime() and localtime() really depend on.
+ */
+
+ WideCharToMultiByte(CP_ACP, 0, tzinfo.StandardName, -1, _tzname[0],
+ 32, NULL, NULL);
+ WideCharToMultiByte(CP_ACP, 0, tzinfo.DaylightName, -1, _tzname[1],
+ 32, NULL, NULL);
+
+ /* _timezone is in seconds. tzinfo.Bias is in minutes. */
+
+ _timezone = tzinfo.Bias * 60;
+ _daylight = tzinfo.DaylightBias ? 1 : 0;
+ return;
+}
+
+/*
+ ***********************************************************************
+ ***********************************************************************
+ *
+ * Process creation routines
+ *
+ ***********************************************************************
+ ***********************************************************************
+ */
+
+/*
+ * Assemble the command line by concatenating the argv array.
+ * On success, this function returns 0 and the resulting command
+ * line is returned in *cmdLine. On failure, it returns -1.
+ */
+static int assembleCmdLine(char *const *argv, char **cmdLine)
+{
+ char *const *arg;
+ char *p, *q;
+ int cmdLineSize;
+ int numBackslashes;
+ int i;
+ int argNeedQuotes;
+
+ /*
+ * Find out how large the command line buffer should be.
+ */
+ cmdLineSize = 0;
+ for (arg = argv; *arg; arg++) {
+ /*
+ * \ and " need to be escaped by a \. In the worst case,
+ * every character is a \ or ", so the string of length
+ * may double. If we quote an argument, that needs two ".
+ * Finally, we need a space between arguments, and
+ * a null byte at the end of command line.
+ */
+ cmdLineSize += 2 * strlen(*arg) /* \ and " need to be escaped */
+ + 2 /* we quote every argument */
+ + 1; /* space in between, or final null */
+ }
+ p = *cmdLine = PR_MALLOC(cmdLineSize);
+ if (p == NULL) {
+ return -1;
+ }
+
+ for (arg = argv; *arg; arg++) {
+ /* Add a space to separates the arguments */
+ if (arg != argv) {
+ *p++ = ' ';
+ }
+ q = *arg;
+ numBackslashes = 0;
+ argNeedQuotes = 0;
+
+ /* If the argument contains white space, it needs to be quoted. */
+ if (strpbrk(*arg, " \f\n\r\t\v")) {
+ argNeedQuotes = 1;
+ }
+
+ if (argNeedQuotes) {
+ *p++ = '"';
+ }
+ while (*q) {
+ if (*q == '\\') {
+ numBackslashes++;
+ q++;
+ } else if (*q == '"') {
+ if (numBackslashes) {
+ /*
+ * Double the backslashes since they are followed
+ * by a quote
+ */
+ for (i = 0; i < 2 * numBackslashes; i++) {
+ *p++ = '\\';
+ }
+ numBackslashes = 0;
+ }
+ /* To escape the quote */
+ *p++ = '\\';
+ *p++ = *q++;
+ } else {
+ if (numBackslashes) {
+ /*
+ * Backslashes are not followed by a quote, so
+ * don't need to double the backslashes.
+ */
+ for (i = 0; i < numBackslashes; i++) {
+ *p++ = '\\';
+ }
+ numBackslashes = 0;
+ }
+ *p++ = *q++;
+ }
+ }
+
+ /* Now we are at the end of this argument */
+ if (numBackslashes) {
+ /*
+ * Double the backslashes if we have a quote string
+ * delimiter at the end.
+ */
+ if (argNeedQuotes) {
+ numBackslashes *= 2;
+ }
+ for (i = 0; i < numBackslashes; i++) {
+ *p++ = '\\';
+ }
+ }
+ if (argNeedQuotes) {
+ *p++ = '"';
+ }
+ }
+
+ *p = '\0';
+ return 0;
+}
+
+/*
+ * Assemble the environment block by concatenating the envp array
+ * (preserving the terminating null byte in each array element)
+ * and adding a null byte at the end.
+ *
+ * Returns 0 on success. The resulting environment block is returned
+ * in *envBlock. Note that if envp is NULL, a NULL pointer is returned
+ * in *envBlock. Returns -1 on failure.
+ */
+static int assembleEnvBlock(char **envp, char **envBlock)
+{
+ char *p;
+ char *q;
+ char **env;
+ char *curEnv;
+ char *cwdStart, *cwdEnd;
+ int envBlockSize;
+
+ if (envp == NULL) {
+ *envBlock = NULL;
+ return 0;
+ }
+
+ curEnv = GetEnvironmentStrings();
+
+ cwdStart = curEnv;
+ while (*cwdStart) {
+ if (cwdStart[0] == '=' && cwdStart[1] != '\0'
+ && cwdStart[2] == ':' && cwdStart[3] == '=') {
+ break;
+ }
+ cwdStart += strlen(cwdStart) + 1;
+ }
+ cwdEnd = cwdStart;
+ if (*cwdEnd) {
+ cwdEnd += strlen(cwdEnd) + 1;
+ while (*cwdEnd) {
+ if (cwdEnd[0] != '=' || cwdEnd[1] == '\0'
+ || cwdEnd[2] != ':' || cwdEnd[3] != '=') {
+ break;
+ }
+ cwdEnd += strlen(cwdEnd) + 1;
+ }
+ }
+ envBlockSize = cwdEnd - cwdStart;
+
+ for (env = envp; *env; env++) {
+ envBlockSize += strlen(*env) + 1;
+ }
+ envBlockSize++;
+
+ p = *envBlock = PR_MALLOC(envBlockSize);
+ if (p == NULL) {
+ FreeEnvironmentStrings(curEnv);
+ return -1;
+ }
+
+ q = cwdStart;
+ while (q < cwdEnd) {
+ *p++ = *q++;
+ }
+ FreeEnvironmentStrings(curEnv);
+
+ for (env = envp; *env; env++) {
+ q = *env;
+ while (*q) {
+ *p++ = *q++;
+ }
+ *p++ = '\0';
+ }
+ *p = '\0';
+ return 0;
+}
+
+/*
+ * For qsort. We sort (case-insensitive) the environment strings
+ * before generating the environment block.
+ */
+static int compare(const void *arg1, const void *arg2)
+{
+ return _stricmp(* (char**)arg1, * (char**)arg2);
+}
+
+PRProcess * _PR_CreateWindowsProcess(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr)
+{
+ STARTUPINFO startupInfo;
+ PROCESS_INFORMATION procInfo;
+ BOOL retVal;
+ char *cmdLine = NULL;
+ char *envBlock = NULL;
+ char **newEnvp;
+ PRProcess *proc = NULL;
+
+ proc = PR_NEW(PRProcess);
+ if (!proc) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ goto errorExit;
+ }
+
+ if (assembleCmdLine(argv, &cmdLine) == -1) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ goto errorExit;
+ }
+
+ if (envp == NULL) {
+ newEnvp = NULL;
+ } else {
+ int i;
+ int numEnv = 0;
+ while (envp[numEnv]) {
+ numEnv++;
+ }
+ newEnvp = (char **) PR_MALLOC((numEnv+1) * sizeof(char *));
+ for (i = 0; i <= numEnv; i++) {
+ newEnvp[i] = envp[i];
+ }
+ qsort((void *) newEnvp, (size_t) numEnv, sizeof(char *), compare);
+ }
+ if (assembleEnvBlock(newEnvp, &envBlock) == -1) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ goto errorExit;
+ }
+
+ ZeroMemory(&startupInfo, sizeof(startupInfo));
+ startupInfo.cb = sizeof(startupInfo);
+
+ if (attr) {
+ PRBool redirected = PR_FALSE;
+
+ /*
+ * XXX the default value for stdin, stdout, and stderr
+ * should probably be the console input and output, not
+ * those of the parent process.
+ */
+ startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ startupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ startupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ if (attr->stdinFd) {
+ startupInfo.hStdInput = (HANDLE) attr->stdinFd->secret->md.osfd;
+ redirected = PR_TRUE;
+ }
+ if (attr->stdoutFd) {
+ startupInfo.hStdOutput = (HANDLE) attr->stdoutFd->secret->md.osfd;
+ redirected = PR_TRUE;
+ }
+ if (attr->stderrFd) {
+ startupInfo.hStdError = (HANDLE) attr->stderrFd->secret->md.osfd;
+ redirected = PR_TRUE;
+ }
+ if (redirected) {
+ startupInfo.dwFlags |= STARTF_USESTDHANDLES;
+ }
+ }
+
+ retVal = CreateProcess(NULL,
+ cmdLine,
+ NULL, /* security attributes for the new
+ * process */
+ NULL, /* security attributes for the primary
+ * thread in the new process */
+ TRUE, /* inherit handles */
+ 0, /* creation flags */
+ envBlock, /* an environment block, consisting
+ * of a null-terminated block of
+ * null-terminated strings. Each
+ * string is in the form:
+ * name=value
+ * XXX: usually NULL */
+ NULL, /* current drive and directory */
+ &startupInfo,
+ &procInfo
+ );
+ if (retVal == FALSE) {
+ /* XXX what error code? */
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ goto errorExit;
+ }
+
+ CloseHandle(procInfo.hThread);
+ proc->md.handle = procInfo.hProcess;
+ proc->md.id = procInfo.dwProcessId;
+
+ PR_DELETE(cmdLine);
+ if (envBlock) {
+ PR_DELETE(envBlock);
+ }
+ return proc;
+
+errorExit:
+ if (cmdLine) {
+ PR_DELETE(cmdLine);
+ }
+ if (envBlock) {
+ PR_DELETE(envBlock);
+ }
+ if (proc) {
+ PR_DELETE(proc);
+ }
+ return NULL;
+} /* _PR_CreateWindowsProcess */
+
+PRStatus _PR_DetachWindowsProcess(PRProcess *process)
+{
+ CloseHandle(process->md.handle);
+ PR_DELETE(process);
+ return PR_SUCCESS;
+}
+
+/*
+ * XXX: This implementation is a temporary quick solution.
+ * It can be called by native threads only (not by fibers).
+ */
+PRStatus _PR_WaitWindowsProcess(PRProcess *process,
+ PRInt32 *exitCode)
+{
+ DWORD dwRetVal;
+
+ dwRetVal = WaitForSingleObject(process->md.handle, INFINITE);
+ if (dwRetVal == WAIT_FAILED) {
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ return PR_FAILURE;
+ }
+ PR_ASSERT(dwRetVal == WAIT_OBJECT_0);
+ if (exitCode != NULL &&
+ GetExitCodeProcess(process->md.handle, exitCode) == FALSE) {
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ return PR_FAILURE;
+ }
+ CloseHandle(process->md.handle);
+ PR_DELETE(process);
+ return PR_SUCCESS;
+}
+
+PRStatus _PR_KillWindowsProcess(PRProcess *process)
+{
+ /*
+ * On Unix, if a process terminates normally, its exit code is
+ * between 0 and 255. So here on Windows, we use the exit code
+ * 256 to indicate that the process is killed.
+ */
+ if (TerminateProcess(process->md.handle, 256)) {
+ return PR_SUCCESS;
+ }
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ return PR_FAILURE;
+}
+
+PRStatus _MD_WindowsGetHostName(char *name, PRUint32 namelen)
+{
+ PRIntn rv;
+ PRInt32 syserror;
+
+ rv = gethostname(name, (PRInt32) namelen);
+ if (0 == rv) {
+ return PR_SUCCESS;
+ }
+ syserror = WSAGetLastError();
+ PR_ASSERT(WSANOTINITIALISED != syserror);
+ _PR_MD_MAP_GETHOSTNAME_ERROR(syserror);
+ return PR_FAILURE;
+}
+
+/*
+ **********************************************************************
+ *
+ * Memory-mapped files
+ *
+ **********************************************************************
+ */
+
+PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size)
+{
+ DWORD dwHi, dwLo;
+ DWORD flProtect;
+
+ dwLo = (DWORD) (size & 0xffffffff);
+ dwHi = (DWORD) (((PRUint64) size >> 32) & 0xffffffff);
+
+ if (fmap->prot == PR_PROT_READONLY) {
+ flProtect = PAGE_READONLY;
+ fmap->md.dwAccess = FILE_MAP_READ;
+ } else if (fmap->prot == PR_PROT_READWRITE) {
+ flProtect = PAGE_READWRITE;
+ fmap->md.dwAccess = FILE_MAP_WRITE;
+ } else {
+ PR_ASSERT(fmap->prot == PR_PROT_WRITECOPY);
+ flProtect = PAGE_WRITECOPY;
+ fmap->md.dwAccess = FILE_MAP_COPY;
+ }
+
+ fmap->md.hFileMap = CreateFileMapping(
+ (HANDLE) fmap->fd->secret->md.osfd,
+ NULL,
+ flProtect,
+ dwHi,
+ dwLo,
+ NULL);
+
+ if (fmap->md.hFileMap == NULL) {
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+void * _MD_MemMap(
+ PRFileMap *fmap,
+ PRInt64 offset,
+ PRUint32 len)
+{
+ DWORD dwHi, dwLo;
+ void *addr;
+
+ dwLo = (DWORD) (offset & 0xffffffff);
+ dwHi = (DWORD) (((PRUint64) offset >> 32) & 0xffffffff);
+ if ((addr = MapViewOfFile(fmap->md.hFileMap, fmap->md.dwAccess,
+ dwHi, dwLo, len)) == NULL) {
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ }
+ return addr;
+}
+
+PRStatus _MD_MemUnmap(void *addr, PRUint32 len)
+{
+ if (UnmapViewOfFile(addr)) {
+ return PR_SUCCESS;
+ } else {
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus _MD_CloseFileMap(PRFileMap *fmap)
+{
+ CloseHandle(fmap->md.hFileMap);
+ PR_DELETE(fmap);
+ return PR_SUCCESS;
+}
diff --git a/pr/src/md/windows/ntsem.c b/pr/src/md/windows/ntsem.c
new file mode 100644
index 00000000..5d2f6c03
--- /dev/null
+++ b/pr/src/md/windows/ntsem.c
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * NT-specific semaphore handling code.
+ *
+ */
+
+
+#include "primpl.h"
+
+
+void
+_PR_MD_NEW_SEM(_MDSemaphore *md, PRUintn value)
+{
+ md->sem = CreateSemaphore(NULL, value, 0x7fffffff, NULL);
+}
+
+void
+_PR_MD_DESTROY_SEM(_MDSemaphore *md)
+{
+ CloseHandle(md->sem);
+}
+
+PRStatus
+_PR_MD_TIMED_WAIT_SEM(_MDSemaphore *md, PRIntervalTime ticks)
+{
+ int rv;
+
+ rv = WaitForSingleObject(md->sem, PR_IntervalToMilliseconds(ticks));
+
+ if (rv == WAIT_OBJECT_0)
+ return PR_SUCCESS;
+ else
+ return PR_FAILURE;
+}
+
+PRStatus
+_PR_MD_WAIT_SEM(_MDSemaphore *md)
+{
+ return _PR_MD_TIMED_WAIT_SEM(md, PR_INTERVAL_NO_TIMEOUT);
+}
+
+void
+_PR_MD_POST_SEM(_MDSemaphore *md)
+{
+ int old_count;
+
+ ReleaseSemaphore(md->sem, 1, &old_count);
+}
diff --git a/pr/src/md/windows/ntthread.c b/pr/src/md/windows/ntthread.c
new file mode 100644
index 00000000..885a0953
--- /dev/null
+++ b/pr/src/md/windows/ntthread.c
@@ -0,0 +1,434 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+#include <process.h> /* for _beginthreadex() */
+
+extern void _PR_Win32InitTimeZone(void); /* defined in ntmisc.c */
+
+/* --- globals ------------------------------------------------ */
+PRLock *_pr_schedLock = NULL;
+_PRInterruptTable _pr_interruptTable[] = { { 0 } };
+__declspec(thread) PRThread *_pr_current_fiber;
+__declspec(thread) PRThread *_pr_fiber_last_run;
+__declspec(thread) _PRCPU *_pr_current_cpu;
+__declspec(thread) PRUintn _pr_ints_off;
+
+_MDLock _nt_idleLock;
+PRCList _nt_idleList;
+PRUint32 _nt_idleCount;
+
+extern __declspec(thread) PRThread *_pr_io_restarted_io;
+
+/* Must check the restarted_io *before* decrementing no_sched to 0 */
+#define POST_SWITCH_WORK() \
+ if (_pr_io_restarted_io) \
+ _nt_handle_restarted_io(_pr_io_restarted_io); \
+ _PR_MD_LAST_THREAD()->no_sched = 0;
+
+void
+_nt_handle_restarted_io(PRThread *restarted_io)
+{
+ /* After the switch we can resume an IO if needed.
+ * XXXMB - this needs to be done in create thread, since that could
+ * be the result for a context switch too..
+ */
+ PR_ASSERT(restarted_io->io_suspended == PR_TRUE);
+
+ _PR_THREAD_LOCK(restarted_io);
+ if (restarted_io->io_pending == PR_FALSE) {
+
+ /* The IO already completed, put us back on the runq. */
+ int pri = restarted_io->priority;
+
+ restarted_io->state = _PR_RUNNABLE;
+ _PR_RUNQ_LOCK(restarted_io->cpu);
+ _PR_ADD_RUNQ(restarted_io, restarted_io->cpu, pri);
+ _PR_RUNQ_UNLOCK(restarted_io->cpu);
+ } else {
+ _PR_SLEEPQ_LOCK(restarted_io->cpu);
+ _PR_ADD_SLEEPQ(restarted_io, restarted_io->sleep);
+ _PR_SLEEPQ_UNLOCK(restarted_io->cpu);
+ }
+ restarted_io->io_suspended = PR_FALSE;
+
+ _PR_THREAD_UNLOCK(restarted_io);
+
+ _pr_io_restarted_io = NULL;
+}
+
+void
+_PR_MD_EARLY_INIT()
+{
+ _MD_NEW_LOCK( &_nt_idleLock );
+ _nt_idleCount = 0;
+ PR_INIT_CLIST(&_nt_idleList);
+ _PR_Win32InitTimeZone();
+
+#if 0
+ /* Make the clock tick at least once per millisecond */
+ if ( timeBeginPeriod(1) == TIMERR_NOCANDO) {
+ /* deep yoghurt; clock doesn't tick fast enough! */
+ PR_ASSERT(0);
+ }
+#endif
+}
+
+void _PR_MD_CLEANUP_BEFORE_EXIT(void)
+{
+ WSACleanup();
+}
+
+void
+_PR_MD_INIT_PRIMORDIAL_THREAD(PRThread *thread)
+{
+ /*
+ ** Warning:
+ ** --------
+ ** NSPR requires a real handle to every thread. GetCurrentThread()
+ ** returns a pseudo-handle which is not suitable for some thread
+ ** operations (ie. suspending). Therefore, get a real handle from
+ ** the pseudo handle via DuplicateHandle(...)
+ */
+ DuplicateHandle( GetCurrentProcess(), /* Process of source handle */
+ GetCurrentThread(), /* Pseudo Handle to dup */
+ GetCurrentProcess(), /* Process of handle */
+ &(thread->md.handle), /* resulting handle */
+ 0L, /* access flags */
+ FALSE, /* Inheritable */
+ DUPLICATE_SAME_ACCESS ); /* Options */
+}
+
+PRStatus
+_PR_MD_INIT_THREAD(PRThread *thread)
+{
+ /* Create the blocking IO semaphore */
+ thread->md.blocked_sema = CreateSemaphore(NULL, 0, 1, NULL);
+ if (thread->md.blocked_sema == NULL)
+ return PR_FAILURE;
+ else
+ return PR_SUCCESS;
+}
+
+PRStatus
+_PR_MD_CREATE_THREAD(PRThread *thread,
+ void (*start)(void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+
+#if 0
+ thread->md.handle = CreateThread(
+ NULL, /* security attrib */
+ thread->stack->stackSize, /* stack size */
+ (LPTHREAD_START_ROUTINE)start, /* startup routine */
+ (void *)thread, /* thread param */
+ CREATE_SUSPENDED, /* create flags */
+ &(thread->id) ); /* thread id */
+#else
+ thread->md.handle = (HANDLE) _beginthreadex(
+ NULL,
+ thread->stack->stackSize,
+ (unsigned (__stdcall *)(void *))start,
+ (void *)thread,
+ CREATE_SUSPENDED,
+ &(thread->id));
+#endif
+ if(!thread->md.handle) {
+ PRErrorCode prerror;
+ thread->md.fiber_last_error = GetLastError();
+ switch (errno) {
+ case ENOMEM:
+ prerror = PR_OUT_OF_MEMORY_ERROR;
+ break;
+ case EAGAIN:
+ prerror = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+ case EINVAL:
+ prerror = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ default:
+ prerror = PR_UNKNOWN_ERROR;
+ }
+ PR_SetError(prerror, errno);
+ return PR_FAILURE;
+ }
+
+ thread->md.id = thread->id;
+
+ /* Activate the thread */
+ if ( ResumeThread( thread->md.handle ) != -1)
+ return PR_SUCCESS;
+
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ return PR_FAILURE;
+}
+
+void
+_PR_MD_YIELD(void)
+{
+ /* Can NT really yield at all? */
+ Sleep(0);
+}
+
+void
+_PR_MD_SET_PRIORITY(_MDThread *thread, PRThreadPriority newPri)
+{
+#if 0
+ /* XXXMB - does this work? Should we really set the priorities of
+ * native threads? */
+ if( newPri < 4 ) {
+ newPri = (PRUintn)THREAD_PRIORITY_IDLE;
+ } else if( newPri < 8 ) {
+ newPri = (PRUintn)THREAD_PRIORITY_LOWEST;
+ } else if( newPri < 12 ) {
+ newPri = (PRUintn)THREAD_PRIORITY_BELOW_NORMAL;
+ } else if( newPri < 16 ) {
+ newPri = (PRUintn)THREAD_PRIORITY_NORMAL;
+ } else if( newPri < 24 ) {
+ newPri = (PRUintn)THREAD_PRIORITY_ABOVE_NORMAL;
+ } else if( newPri < 28 ) {
+ newPri = (PRUintn)THREAD_PRIORITY_HIGHEST;
+ } else if( newPri < 32 ) {
+ newPri = (PRUintn)THREAD_PRIORITY_TIME_CRITICAL;
+ }
+
+ if( ! SetThreadPriority( thread->handle, newPri ) ) {
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("PR_SetThreadPriority: can't set thread priority\n"));
+ }
+#endif
+
+ return;
+}
+
+void
+_PR_MD_CLEAN_THREAD(PRThread *thread)
+{
+ if (thread->md.acceptex_buf) {
+ PR_DELETE(thread->md.acceptex_buf);
+ }
+
+ if (thread->md.xmit_bufs) {
+ PR_DELETE(thread->md.xmit_bufs);
+ }
+
+ if (thread->md.blocked_sema) {
+ CloseHandle(thread->md.blocked_sema);
+ thread->md.blocked_sema = 0;
+ }
+
+ if (thread->md.handle) {
+ CloseHandle(thread->md.handle);
+ thread->md.handle = 0;
+ }
+
+ /* Don't call DeleteFiber on current fiber or we'll kill the whole thread.
+ * Don't call free(thread) until we've switched off the thread.
+ * So put this fiber (or thread) on a list to be deleted by the idle
+ * fiber next time we have a chance.
+ */
+ if (!(thread->flags & (_PR_ATTACHED|_PR_GLOBAL_SCOPE))) {
+ _MD_LOCK(&_nt_idleLock);
+ _nt_idleCount++;
+ PR_APPEND_LINK(&thread->links, &_nt_idleList);
+ _MD_UNLOCK(&_nt_idleLock);
+ }
+}
+
+void
+_PR_MD_EXIT_THREAD(PRThread *thread)
+{
+ if (thread->md.acceptex_buf) {
+ PR_DELETE(thread->md.acceptex_buf);
+ }
+
+ if (thread->md.xmit_bufs) {
+ PR_DELETE(thread->md.xmit_bufs);
+ }
+
+ if (thread->md.blocked_sema) {
+ CloseHandle(thread->md.blocked_sema);
+ thread->md.blocked_sema = 0;
+ }
+
+ if (thread->md.handle) {
+ CloseHandle(thread->md.handle);
+ thread->md.handle = 0;
+ }
+
+ if (thread->flags & _PR_GLOBAL_SCOPE) {
+ _MD_SET_CURRENT_THREAD(NULL);
+ }
+}
+
+
+void
+_PR_MD_EXIT(PRIntn status)
+{
+ _exit(status);
+}
+
+#ifdef HAVE_FIBERS
+
+void
+_pr_fiber_mainline(void *unused)
+{
+ PRThread *fiber = _PR_MD_CURRENT_THREAD();
+
+ POST_SWITCH_WORK();
+
+ fiber->md.fiber_fn(fiber->md.fiber_arg);
+}
+
+PRThread *_PR_MD_CREATE_USER_THREAD(
+ PRUint32 stacksize, void (*start)(void *), void *arg)
+{
+ PRThread *thread;
+
+ if ( (thread = PR_NEW(PRThread)) == NULL ) {
+ return NULL;
+ }
+
+ memset(thread, 0, sizeof(PRThread));
+ thread->md.fiber_fn = start;
+ thread->md.fiber_arg = arg;
+ thread->md.fiber_stacksize = stacksize;
+ return thread;
+}
+
+void
+_PR_MD_CREATE_PRIMORDIAL_USER_THREAD(PRThread *thread)
+{
+ thread->md.fiber_id = ConvertThreadToFiber(NULL);
+ PR_ASSERT(thread->md.fiber_id);
+ thread->flags &= (~_PR_GLOBAL_SCOPE);
+ _MD_SET_CURRENT_THREAD(thread);
+ _MD_SET_LAST_THREAD(thread);
+ thread->no_sched = 1;
+ return;
+}
+
+void
+_PR_MD_INIT_CONTEXT(PRThread *thread, char *top, void (*start) (void), PRBool *status)
+{
+ thread->md.fiber_fn = (void (*)(void *))start;
+ thread->md.fiber_id = CreateFiber(thread->md.fiber_stacksize,
+ (LPFIBER_START_ROUTINE)_pr_fiber_mainline, NULL);
+ if (thread->md.fiber_id != 0)
+ *status = PR_TRUE;
+ else {
+ DWORD oserror = GetLastError();
+ PRErrorCode prerror;
+ if (oserror == ERROR_NOT_ENOUGH_MEMORY) {
+ prerror = PR_OUT_OF_MEMORY_ERROR;
+ } else {
+ prerror = PR_UNKNOWN_ERROR;
+ }
+ PR_SetError(prerror, oserror);
+ *status = PR_FALSE;
+ }
+}
+
+void
+_PR_MD_SWITCH_CONTEXT(PRThread *thread)
+{
+ PR_ASSERT( !_PR_IS_NATIVE_THREAD(thread) );
+
+ thread->md.fiber_last_error = GetLastError();
+ _PR_Schedule();
+}
+
+void
+_PR_MD_RESTORE_CONTEXT(PRThread *thread)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT( !_PR_IS_NATIVE_THREAD(thread) );
+
+ /* The user-level code for yielding will happily add ourselves to the runq
+ * and then switch to ourselves; the NT fibers can't handle switching to
+ * ourselves.
+ */
+ if (thread != me) {
+ SetLastError(thread->md.fiber_last_error);
+ _MD_SET_CURRENT_THREAD(thread);
+ _PR_MD_SET_LAST_THREAD(me);
+ thread->no_sched = 1;
+ SwitchToFiber(thread->md.fiber_id);
+ POST_SWITCH_WORK();
+ }
+}
+
+
+#endif /* HAVE_FIBERS */
+
+PRInt32 _PR_MD_SETTHREADAFFINITYMASK(PRThread *thread, PRUint32 mask )
+{
+ int rv;
+
+ rv = SetThreadAffinityMask(thread->md.handle, mask);
+
+ return rv?0:-1;
+}
+
+PRInt32 _PR_MD_GETTHREADAFFINITYMASK(PRThread *thread, PRUint32 *mask)
+{
+ PRInt32 rv, system_mask;
+
+ rv = GetProcessAffinityMask(GetCurrentProcess(), mask, &system_mask);
+
+ return rv?0:-1;
+}
+
+void
+_PR_MD_SUSPEND_CPU(_PRCPU *cpu)
+{
+ _PR_MD_SUSPEND_THREAD(cpu->thread);
+}
+
+void
+_PR_MD_RESUME_CPU(_PRCPU *cpu)
+{
+ _PR_MD_RESUME_THREAD(cpu->thread);
+}
+
+void
+_PR_MD_SUSPEND_THREAD(PRThread *thread)
+{
+ if (_PR_IS_NATIVE_THREAD(thread)) {
+ /*
+ ** There seems to be some doubt about whether or not SuspendThread
+ ** is a synchronous function. The test afterwards is to help veriry
+ ** that it is, which is what Microsoft says it is.
+ */
+ PRUintn rv = SuspendThread(thread->md.handle);
+ PR_ASSERT(0xffffffffUL != rv);
+ }
+}
+
+void
+_PR_MD_RESUME_THREAD(PRThread *thread)
+{
+ if (_PR_IS_NATIVE_THREAD(thread)) {
+ ResumeThread(thread->md.handle);
+ }
+}
+
diff --git a/pr/src/md/windows/w16callb.c b/pr/src/md/windows/w16callb.c
new file mode 100644
index 00000000..d25f7c19
--- /dev/null
+++ b/pr/src/md/windows/w16callb.c
@@ -0,0 +1,243 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+** w16callb.c -- Implement Win16 Callback functions
+**
+** Functions here are to replace functions normally in
+** LIBC which are not implemented in MSVC's LIBC.
+** Some clients of NSPR expect to statically link
+** to NSPR and get these functions.
+**
+** Some are implemented as callbacks to the .EXE
+** some are implemented directly in this module.
+**
+*/
+
+#include "primpl.h"
+#include "windowsx.h"
+
+/*
+** _pr_callback_funcs -- This is where clients register the
+** callback function structure.
+*/
+struct PRMethodCallbackStr * _pr_callback_funcs;
+
+/*
+** PR_MDInitWin16() -- Register the PRMethodCallback table pointer
+**
+*/
+void PR_MDRegisterCallbacks(struct PRMethodCallbackStr *f)
+{
+ _pr_callback_funcs = f;
+}
+
+/*
+** NSPR re-implenentations of various C runtime functions:
+*/
+
+/*
+** PR_MD_printf() -- exported as printf()
+**
+*/
+int PR_MD_printf(const char *fmt, ...)
+{
+ char buffer[1024];
+ int ret = 0;
+ va_list args;
+
+ va_start(args, fmt);
+
+#ifdef DEBUG
+ PR_vsnprintf(buffer, sizeof(buffer), fmt, args);
+ {
+ if (_pr_callback_funcs != NULL && _pr_callback_funcs->auxOutput != NULL) {
+ (* _pr_callback_funcs->auxOutput)(buffer);
+ } else {
+ OutputDebugString(buffer);
+ }
+ }
+#endif
+
+ va_end(args);
+ return ret;
+}
+
+/*
+** PR_MD_sscanf() -- exported as sscanf()
+**
+*/
+int PR_MD_sscanf(const char *buf, const char *fmt, ...)
+{
+ int retval;
+ va_list arglist;
+
+ va_start(arglist, fmt);
+ retval = vsscanf((const unsigned char *)buf, (const unsigned char *)fmt, arglist);
+ va_end(arglist);
+ return retval;
+}
+
+/*
+** PR_MD_strftime() -- exported as strftime
+**
+*/
+size_t PR_MD_strftime(char *s, size_t len, const char *fmt, const struct tm *p)
+{
+ if( _pr_callback_funcs ) {
+ return (*_pr_callback_funcs->strftime)(s, len, fmt, p);
+ } else {
+ PR_ASSERT(0);
+ return 0;
+ }
+}
+
+
+/*
+** PR_MD_malloc() -- exported as malloc()
+**
+*/
+void *PR_MD_malloc( size_t size )
+{
+ if( _pr_callback_funcs ) {
+ return (*_pr_callback_funcs->malloc)( size );
+ } else {
+ return GlobalAllocPtr(GPTR, (DWORD)size);
+ }
+} /* end malloc() */
+
+/*
+** PR_MD_calloc() -- exported as calloc()
+**
+*/
+void *PR_MD_calloc( size_t n, size_t size )
+{
+ void *p;
+ size_t sz;
+
+ if( _pr_callback_funcs ) {
+ return (*_pr_callback_funcs->calloc)( n, size );
+ } else {
+ sz = n * size;
+ p = GlobalAllocPtr(GPTR, (DWORD)sz );
+ memset( p, 0x00, sz );
+ return p;
+ }
+} /* end calloc() */
+
+/*
+** PR_MD_realloc() -- exported as realloc()
+**
+*/
+void *PR_MD_realloc( void* old_blk, size_t size )
+{
+ if( _pr_callback_funcs ) {
+ return (*_pr_callback_funcs->realloc)( old_blk, size );
+ } else {
+ return GlobalReAllocPtr( old_blk, (DWORD)size, GPTR);
+ }
+} /* end realloc */
+
+/*
+** PR_MD_free() -- exported as free()
+**
+*/
+void PR_MD_free( void *ptr )
+{
+ if( _pr_callback_funcs ) {
+ (*_pr_callback_funcs->free)( ptr );
+ return;
+ } else {
+ GlobalFreePtr( ptr );
+ return;
+ }
+} /* end free() */
+
+/*
+** PR_MD_getenv() -- exported as getenv()
+**
+*/
+char *PR_MD_getenv( const char *name )
+{
+ if( _pr_callback_funcs ) {
+ return (*_pr_callback_funcs->getenv)( name );
+ } else {
+ return 0;
+ }
+} /* end getenv() */
+
+
+/*
+** PR_MD_perror() -- exported as perror()
+**
+** well, not really (lth. 12/5/97).
+** XXX hold this thought.
+**
+*/
+void PR_MD_perror( const char *prefix )
+{
+ return;
+} /* end perror() */
+
+/*
+** PR_MD_putenv() -- exported as putenv()
+**
+*/
+int PR_MD_putenv(const char *assoc)
+{
+ if( _pr_callback_funcs ) {
+ return (*_pr_callback_funcs->putenv)(assoc);
+ } else {
+ PR_ASSERT(0);
+ return NULL;
+ }
+}
+
+/*
+** PR_MD_fprintf() -- exported as fprintf()
+**
+*/
+int PR_MD_fprintf(FILE *fPtr, const char *fmt, ...)
+{
+ char buffer[1024];
+ va_list args;
+
+ va_start(args, fmt);
+ PR_vsnprintf(buffer, sizeof(buffer), fmt, args);
+
+ if (fPtr == NULL)
+ {
+ if (_pr_callback_funcs != NULL && _pr_callback_funcs->auxOutput != NULL)
+ {
+ (* _pr_callback_funcs->auxOutput)(buffer);
+ }
+ else
+ {
+ OutputDebugString(buffer);
+ }
+ }
+ else
+ {
+ fwrite(buffer, 0, strlen(buffer), fPtr); /* XXX Is this a sec. hole? */
+ }
+
+ va_end(args);
+ return 0;
+}
+
+/* end w16callb.c */
diff --git a/pr/src/md/windows/w16error.c b/pr/src/md/windows/w16error.c
new file mode 100644
index 00000000..f3ef99ae
--- /dev/null
+++ b/pr/src/md/windows/w16error.c
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+** Note: A single error mapping function is provided.
+**
+*/
+#include "prerror.h"
+#include <errno.h>
+#include <winsock.h>
+
+
+void _PR_MD_map_error( int err )
+{
+
+ switch ( err )
+ {
+ case ENOENT: /* No such file or directory */
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case E2BIG: /* Argument list too big */
+ PR_SetError( PR_INVALID_ARGUMENT_ERROR, err );
+ break;
+ case ENOEXEC: /* Exec format error */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EBADF: /* Bad file number */
+ PR_SetError( PR_BAD_DESCRIPTOR_ERROR, err );
+ break;
+ case ENOMEM: /* Not enough Memory */
+ PR_SetError( PR_OUT_OF_MEMORY_ERROR, err );
+ break;
+ case EACCES: /* Permission denied */
+ PR_SetError( PR_NO_ACCESS_RIGHTS_ERROR, err );
+ break;
+ case EEXIST: /* File exists */
+
+ /* RESTART here */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EXDEV: /* Cross device link */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EINVAL: /* Invalid argument */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ENFILE: /* File table overflow */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EMFILE: /* Too many open files */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ENOSPC: /* No space left on device */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ /* math errors */
+ case EDOM: /* Argument too large */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ERANGE: /* Result too large */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ /* file locking error */
+ case EDEADLK: /* Resource deadlock would occur */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EINTR: /* Interrupt */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ECHILD: /* Child does not exist */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ /* POSIX errors */
+ case EAGAIN: /* Resource unavailable, try again */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EBUSY: /* Device or Resource is busy */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EFBIG: /* File too large */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EIO: /* I/O error */
+ PR_SetError( PR_IO_ERROR, err );
+ break;
+ case EISDIR: /* Is a directory */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ENOTDIR: /* Not a directory */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EMLINK: /* Too many links */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ENOTBLK: /* Block device required */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ENOTTY: /* Not a character device */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ENXIO: /* No such device or address */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EPERM: /* Not owner */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EPIPE: /* Broken pipe */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EROFS: /* Read only file system */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ESPIPE: /* Illegal seek */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ESRCH: /* No such process */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ETXTBSY: /* Text file busy */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EFAULT: /* Bad address */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ENAMETOOLONG: /* Name too long */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ENODEV: /* No such device */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ENOLCK: /* No locks available on system */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ENOSYS: /* Unknown system call */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ENOTEMPTY: /* Directory not empty */
+ /* Normative Addendum error */
+ case EILSEQ: /* Multibyte/widw character encoding error */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+
+ /* WinSock errors */
+ case WSAEACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case WSAEADDRINUSE:
+ PR_SetError(PR_ADDRESS_IN_USE_ERROR, err);
+ break;
+ case WSAEADDRNOTAVAIL:
+ PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err);
+ break;
+ case WSAEAFNOSUPPORT:
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err);
+ break;
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAEINVAL:
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err);
+ break;
+ case WSAEISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+ case WSAEMFILE:
+ PR_SetError(PR_PROC_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case WSAENETDOWN:
+ case WSAENETUNREACH:
+ PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, err);
+ break;
+ case WSAENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case WSAENOPROTOOPT:
+ case WSAEMSGSIZE:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case WSAENOTCONN:
+ PR_SetError(PR_NOT_CONNECTED_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEOPNOTSUPP:
+ PR_SetError(PR_NOT_TCP_SOCKET_ERROR, err);
+ break;
+ case WSAEPROTONOSUPPORT:
+ PR_SetError(PR_PROTOCOL_NOT_SUPPORTED_ERROR, err);
+ break;
+ case WSAETIMEDOUT:
+ PR_SetError(PR_IO_TIMEOUT_ERROR, err);
+ break;
+ case WSAEINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err );
+ break;
+ case WSASYSNOTREADY:
+ case WSAVERNOTSUPPORTED:
+ PR_SetError(PR_PROTOCOL_NOT_SUPPORTED_ERROR, err);
+ break;
+ case WSAEWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+
+ default:
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ }
+ return;
+} /* end _MD_map_win16_error() */
+
+
diff --git a/pr/src/md/windows/w16fmem.c b/pr/src/md/windows/w16fmem.c
new file mode 100644
index 00000000..836ca80b
--- /dev/null
+++ b/pr/src/md/windows/w16fmem.c
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+/*
+ **********************************************************************
+ *
+ * Memory-mapped files are not implemented on Win16.
+ *
+ **********************************************************************
+ */
+
+PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size)
+{
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+void * _MD_MemMap(
+ PRFileMap *fmap,
+ PRInt64 offset,
+ PRUint32 len)
+{
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+}
+
+PRStatus _MD_MemUnmap(void *addr, PRUint32 len)
+{
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRStatus _MD_CloseFileMap(PRFileMap *fmap)
+{
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
diff --git a/pr/src/md/windows/w16gc.c b/pr/src/md/windows/w16gc.c
new file mode 100644
index 00000000..1a84bef4
--- /dev/null
+++ b/pr/src/md/windows/w16gc.c
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+#include <sys/timeb.h>
+
+PRWord *
+_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ if (isCurrent)
+ {
+ _MD_SAVE_CONTEXT(t);
+ }
+ /*
+ ** In Win16 because the premption is "cooperative" it can never be the
+ ** case that a register holds the sole reference to an object. It
+ ** will always have been pushed onto the stack before the thread
+ ** switch... So don't bother to scan the registers...
+ */
+ *np = 0;
+
+ return (PRWord *) CONTEXT(t);
+}
+
+#if 0
+#ifndef SPORT_MODEL
+
+#define MAX_SEGMENT_SIZE (65536l - 4096l)
+
+/************************************************************************/
+/*
+** Machine dependent GC Heap management routines:
+** _MD_GrowGCHeap
+*/
+/************************************************************************/
+
+extern void *
+_MD_GrowGCHeap(uint32 *sizep)
+{
+ void *addr;
+
+ if( *sizep > MAX_SEGMENT_SIZE ) {
+ *sizep = MAX_SEGMENT_SIZE;
+ }
+
+ addr = malloc((size_t)*sizep);
+ return addr;
+}
+
+#endif /* SPORT_MODEL */
+#endif /* 0 */
+
diff --git a/pr/src/md/windows/w16io.c b/pr/src/md/windows/w16io.c
new file mode 100644
index 00000000..d5758893
--- /dev/null
+++ b/pr/src/md/windows/w16io.c
@@ -0,0 +1,836 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <share.h>
+#include <sys/locking.h>
+
+
+/*
+** Sleep this many milliseconds on each I/O operation
+** to cause an intentional thread switch.
+*/
+#define _PR_MD_WIN16_DELAY 1
+
+
+/*
+** PR_MD_RegisterW16StdioCallbacks() -- Register Win16 stdio callback functions
+**
+** This public function call is unique to Win16.
+** ... Sigh ... So much for platform independence.
+**
+** To get stdio to work from a command line executable, the stdio stream
+** calls must be issued from the .EXE file; calling them from the .DLL
+** sends the output to the bit-bucket. Therefore, the .EXE wanting to
+** do stdio to the console window (must be built as a "quickwin" application)
+** must have the wrapper functions defined in this module statically linked
+** into the .EXE.
+**
+** There appears to be nothing you can do to get stdio to work from a
+** Win16 GUI application. Oh Well!
+**
+*/
+PRStdinRead _pr_md_read_stdin = 0;
+PRStdoutWrite _pr_md_write_stdout = 0;
+PRStderrWrite _pr_md_write_stderr = 0;
+
+PRStatus
+PR_MD_RegisterW16StdioCallbacks( PRStdinRead inReadf, PRStdoutWrite outWritef, PRStderrWrite errWritef )
+{
+ _pr_md_write_stdout = outWritef;
+ _pr_md_write_stderr = errWritef;
+ _pr_md_read_stdin = inReadf;
+
+ return(PR_SUCCESS);
+} /* end PR_MD_RegisterW16StdioCallbacks() */
+
+
+/*
+** _PR_MD_OPEN() -- Open a file
+**
+** Returns: a fileHandle or -1
+**
+**
+*/
+PRInt32
+_PR_MD_OPEN(const char *name, PRIntn osflags, int mode)
+{
+ PRInt32 file;
+ int access = O_BINARY;
+ int rights = 0;
+
+
+ /*
+ ** Map NSPR open flags to os open flags
+ */
+ if (osflags & PR_RDONLY )
+ access |= O_RDONLY;
+ if (osflags & PR_WRONLY )
+ access |= O_WRONLY;
+ if (osflags & PR_RDWR )
+ access |= O_RDWR;
+ if (osflags & PR_CREATE_FILE )
+ {
+ access |= O_CREAT;
+ rights |= S_IRWXU;
+ }
+ if (osflags & PR_TRUNCATE)
+ access |= O_TRUNC;
+ if (osflags & PR_APPEND)
+ access |= O_APPEND;
+ else
+ access |= O_RDONLY;
+
+ /*
+ ** Open the file
+ */
+ file = (PRInt32) sopen( name, access, SH_DENYNO, rights );
+ if ( -1 == (PRInt32)file )
+ {
+ _PR_MD_MAP_OPEN_ERROR( errno );
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return file;
+}
+
+/*
+** _PR_MD_READ() - Read something
+**
+** Returns: bytes read or -1
+**
+*/
+PRInt32
+_PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len)
+{
+ PRInt32 rv;
+
+ if ( (PR_GetDescType(fd) == PR_DESC_FILE) &&
+ ( fd->secret->md.osfd == PR_StandardInput ) &&
+ ( _pr_md_write_stdout ))
+ {
+ rv = (*_pr_md_read_stdin)( buf, len);
+ }
+ else
+ {
+ rv = read( fd->secret->md.osfd, buf, len );
+ }
+
+ if ( rv == -1)
+ {
+ _PR_MD_MAP_READ_ERROR( errno );
+ }
+
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return rv;
+}
+
+/*
+** _PR_MD_WRITE() - Write something
+**
+** Returns: bytes written or -1
+**
+** Note: for file handles 1 and 2 (stdout and stderr)
+** call the Win16 NSPR stdio callback functions, if they are
+** registered.
+**
+*/
+PRInt32
+_PR_MD_WRITE(PRFileDesc *fd, const void *buf, PRInt32 len)
+{
+ PRInt32 rv;
+
+ if ( (PR_GetDescType(fd) == PR_DESC_FILE))
+ {
+ switch ( fd->secret->md.osfd )
+ {
+ case PR_StandardOutput :
+ if ( _pr_md_write_stdout )
+ rv = (*_pr_md_write_stdout)( (void *)buf, len);
+ else
+ rv = len; /* fake success */
+ break;
+
+ case PR_StandardError :
+ if ( _pr_md_write_stderr )
+ rv = (*_pr_md_write_stderr)( (void *)buf, len);
+ else
+ rv = len; /* fake success */
+ break;
+
+ default:
+ rv = write( fd->secret->md.osfd, buf, len );
+ if ( rv == -1 )
+ {
+ _PR_MD_MAP_WRITE_ERROR( errno );
+ }
+ break;
+ }
+ }
+ else
+ {
+ rv = write( fd->secret->md.osfd, buf, len );
+ if ( rv == -1 )
+ {
+ _PR_MD_MAP_WRITE_ERROR( errno );
+ }
+ }
+
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return rv;
+} /* --- end _PR_MD_WRITE() --- */
+
+/*
+** _PR_MD_LSEEK() - Seek to position in a file
+**
+** Note: 'whence' maps directly to PR_...
+**
+** Returns:
+**
+*/
+PRInt32
+_PR_MD_LSEEK(PRFileDesc *fd, PRInt32 offset, int whence)
+{
+ PRInt32 rv;
+
+ rv = lseek( fd->secret->md.osfd, offset, whence );
+ if ( rv == -1 )
+ {
+ _PR_MD_MAP_LSEEK_ERROR( errno );
+
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return( rv );
+}
+
+/*
+** _PR_MD_LSEEK64() -- Seek to position in file, 64bit offset.
+**
+*/
+PRInt64
+_PR_MD_LSEEK64( PRFileDesc *fd, PRInt64 offset, int whence )
+{
+ PRInt64 test;
+ PRInt32 rv, off;
+ LL_SHR(test, offset, 32);
+ if (!LL_IS_ZERO(test))
+ {
+ PR_SetError(PR_FILE_TOO_BIG_ERROR, 0);
+ LL_I2L(test, -1);
+ return test;
+ }
+ LL_L2I(off, offset);
+ rv = _PR_MD_LSEEK(fd, off, whence);
+ LL_I2L(test, rv);
+ return test;
+} /* end _PR_MD_LSEEK64() */
+
+/*
+** _PR_MD_FSYNC() - Flush file buffers.
+**
+** Returns:
+**
+**
+*/
+PRInt32
+_PR_MD_FSYNC(PRFileDesc *fd)
+{
+ PRInt32 rv;
+
+ rv = (PRInt32) fsync( fd->secret->md.osfd );
+ if ( rv == -1 )
+ {
+ _PR_MD_MAP_FSYNC_ERROR( errno );
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return(rv);
+}
+
+/*
+** _PR_MD_CLOSE() - Close an open file handle
+**
+** Returns:
+**
+**
+*/
+PRInt32
+_PR_MD_CLOSE_FILE(PRInt32 osfd)
+{
+ PRInt32 rv;
+
+ rv = (PRInt32) close( osfd );
+ if ( rv == -1 )
+ {
+ _PR_MD_MAP_CLOSE_ERROR( errno );
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return(rv);
+} /* --- end _MD_CloseFile() --- */
+
+
+/* --- DIR IO ------------------------------------------------------------ */
+#define GetFileFromDIR(d) (d)->d_entry.cFileName
+
+/*
+** FlipSlashes() - Make forward slashes ('/') into backslashes
+**
+** Returns: void
+**
+**
+*/
+void FlipSlashes(char *cp, int len)
+{
+ while (--len >= 0) {
+ if (cp[0] == '/') {
+ cp[0] = PR_DIRECTORY_SEPARATOR;
+ }
+ cp++;
+ }
+}
+
+
+/*
+** _PR_MD_OPEN_DIR() - Open a Directory.
+**
+** Returns:
+**
+**
+*/
+PRStatus
+_PR_MD_OPEN_DIR(_MDDir *d, const char *name)
+{
+ d->dir = opendir( name );
+
+ if ( d->dir == NULL )
+ {
+ _PR_MD_MAP_OPENDIR_ERROR( errno );
+ return( PR_FAILURE );
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return( PR_SUCCESS );
+}
+
+
+/*
+** _PR_MD_READ_DIR() - read next directory entry
+**
+**
+*/
+char *
+_PR_MD_READ_DIR(_MDDir *d, PRIntn flags)
+{
+ struct dirent *de;
+ int err;
+
+ for (;;)
+ {
+ de = readdir( d->dir );
+ if ( de == NULL ) {
+ _PR_MD_MAP_READDIR_ERROR( errno);
+ return 0;
+ }
+ if ((flags & PR_SKIP_DOT) &&
+ (de->d_name[0] == '.') && (de->d_name[1] == 0))
+ continue;
+ if ((flags & PR_SKIP_DOT_DOT) &&
+ (de->d_name[0] == '.') && (de->d_name[1] == '.') &&
+ (de->d_name[2] == 0))
+ continue;
+ break;
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return de->d_name;
+}
+
+/*
+** _PR_MD_CLOSE_DIR() - Close a directory.
+**
+**
+*/
+PRInt32
+_PR_MD_CLOSE_DIR(_MDDir *d)
+{
+ PRInt32 rv;
+
+ if ( d->dir )
+ {
+ rv = closedir( d->dir );
+ if (rv != 0)
+ {
+ _PR_MD_MAP_CLOSEDIR_ERROR( errno );
+ }
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return rv;
+}
+
+
+/*
+** _PR_MD_DELETE() - Delete a file.
+**
+** Returns:
+**
+**
+*/
+PRInt32
+_PR_MD_DELETE(const char *name)
+{
+ PRInt32 rv;
+
+ rv = (PRInt32) remove( name );
+ if ( rv != 0 )
+ {
+ _PR_MD_MAP_DELETE_ERROR( errno );
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return(rv);
+}
+
+
+/*
+** _PR_MD_STAT() - Get file attributes by filename
+**
+** Returns:
+**
+**
+*/
+PRInt32
+_PR_MD_STAT(const char *fn, struct stat *info)
+{
+ PRInt32 rv;
+
+ rv = _stat(fn, (struct _stat *)info);
+ if ( rv == -1 )
+ {
+ _PR_MD_MAP_STAT_ERROR( errno );
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return( rv );
+}
+
+/*
+** _PR_MD_GETFILEINFO() - Get file attributes by filename
+**
+** Returns:
+**
+**
+*/
+PRInt32
+_PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info)
+{
+ struct _stat sb;
+ PRInt32 rv;
+
+ if ( (rv = _stat(fn, &sb)) == 0 ) {
+ if (info) {
+ if (S_IFREG & sb.st_mode)
+ info->type = PR_FILE_FILE ;
+ else if (S_IFDIR & sb.st_mode)
+ info->type = PR_FILE_DIRECTORY;
+ else
+ info->type = PR_FILE_OTHER;
+ info->size = sb.st_size;
+ LL_I2L(info->modifyTime, sb.st_mtime);
+ LL_I2L(info->creationTime, sb.st_ctime);
+ }
+ }
+ else
+ {
+ _PR_MD_MAP_STAT_ERROR( errno );
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return rv;
+}
+
+PRInt32
+_PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info)
+{
+ PRFileInfo info32;
+
+ PRInt32 rv = _PR_MD_GETFILEINFO(fn, &info32);
+ if (0 == rv)
+ {
+ info->type = info32.type;
+ info->modifyTime = info32.modifyTime;
+ info->creationTime = info32.creationTime;
+ LL_I2L(info->size, info32.size);
+ }
+ return(rv);
+}
+
+/*
+** _PR_MD_GETOPENFILEINFO() - Get file attributes from an open file handle
+**
+** Returns:
+**
+**
+*/
+PRInt32
+_PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info)
+{
+ struct stat statBuf;
+ PRInt32 rv = PR_SUCCESS;
+
+ rv = fstat( fd->secret->md.osfd, &statBuf );
+ if ( rv == 0)
+ {
+ if (statBuf.st_mode & S_IFREG )
+ info->type = PR_FILE_FILE;
+ else if ( statBuf.st_mode & S_IFDIR )
+ info->type = PR_FILE_DIRECTORY;
+ else
+ info->type = PR_FILE_OTHER;
+ info->size = statBuf.st_size;
+ LL_I2L(info->modifyTime, statBuf.st_mtime);
+ LL_I2L(info->creationTime, statBuf.st_ctime);
+
+ }
+ else
+ {
+ _PR_MD_MAP_FSTAT_ERROR( errno );
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return(rv);
+}
+
+PRInt32
+_PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info)
+{
+ PRFileInfo info32;
+
+ PRInt32 rv = _PR_MD_GETOPENFILEINFO(fd, &info32);
+ if (0 == rv)
+ {
+ info->type = info32.type;
+ info->modifyTime = info32.modifyTime;
+ info->creationTime = info32.creationTime;
+ LL_I2L(info->size, info32.size);
+ }
+ return(rv);
+}
+
+/*
+** _PR_MD_RENAME() - Rename a file
+**
+** Returns:
+**
+**
+*/
+PRInt32
+_PR_MD_RENAME(const char *from, const char *to)
+{
+ PRInt32 rv;
+
+ rv = rename( from, to );
+ if ( rv == -1 )
+ {
+ _PR_MD_MAP_RENAME_ERROR( errno );
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return( rv );
+}
+
+/*
+** _PR_MD_ACCESS() - Return file acesss attribute.
+**
+** Returns:
+**
+**
+*/
+PRInt32
+_PR_MD_ACCESS(const char *name, PRIntn how)
+{
+ PRInt32 rv;
+ int mode = 0;
+
+ if ( how & PR_ACCESS_WRITE_OK )
+ mode |= W_OK;
+ if ( how & PR_ACCESS_READ_OK )
+ mode |= R_OK;
+
+ rv = (PRInt32) access( name, mode );
+ if ( rv == -1 )
+ {
+ _PR_MD_MAP_ACCESS_ERROR( errno );
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return(rv);
+}
+
+/*
+** _PR_MD_MKDIR() - Make a directory
+**
+** Returns:
+**
+**
+*/
+PRInt32
+_PR_MD_MKDIR(const char *name, PRIntn mode)
+{
+ PRInt32 rv;
+
+ rv = mkdir( name );
+ if ( rv == 0 )
+ {
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return PR_SUCCESS;
+ }
+ else
+ {
+ _PR_MD_MAP_MKDIR_ERROR( errno );
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return PR_FAILURE;
+ }
+}
+
+/*
+** _PR_MD_RMDIR() - Delete a directory
+**
+** Returns:
+**
+**
+*/
+PRInt32
+_PR_MD_RMDIR(const char *name)
+{
+ PRInt32 rv;
+
+ rv = (PRInt32) rmdir( name );
+ if ( rv == -1 )
+ {
+ _PR_MD_MAP_RMDIR_ERROR( errno );
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return(rv);
+}
+
+/*
+** _PR_MD_LOCKFILE() - Lock a file.
+**
+** The _locking() call locks relative to the current file pointer.
+** This function is required to lock all of the file, so,
+** 1. Seek to the beginning of the file, preserving the original position.
+** 2. Lock the file, pausing if it is locked by someone else, and
+** try again.
+** 3. Re-position to the original position in the file.
+**
+** For unlocking, a similar protocol of positioning is required.
+**
+*/
+PRStatus
+_PR_MD_LOCKFILE(PRInt32 f)
+{
+ PRInt32 rv = PR_SUCCESS; /* What we return to our caller */
+ long seekOrigin; /* original position in file */
+ PRInt32 rc; /* what the system call returns to us */
+
+ /*
+ ** Seek to beginning of file, saving original position.
+ */
+ seekOrigin = lseek( f, 0l, SEEK_SET );
+ if ( rc == -1 )
+ {
+ _PR_MD_MAP_LSEEK_ERROR( errno );
+ return( PR_FAILURE );
+ }
+
+ /*
+ ** Attempt to lock the file.
+ ** If someone else has it, Sleep-a-while and try again.
+ */
+ for( rc = -1; rc != 0; )
+ {
+ rc = _locking( f, _LK_NBLCK , 0x7fffffff );
+ if ( rc == -1 )
+ {
+ if ( errno == EACCES )
+ {
+ PR_Sleep( 100 );
+ continue;
+ }
+ else
+ {
+ _PR_MD_MAP_LOCKF_ERROR( errno );
+ rv = PR_FAILURE;
+ break;
+ }
+ }
+ } /* end for() */
+
+ /*
+ ** Now that the file is locked, re-position to
+ ** the original file position.
+ **
+ */
+ rc = lseek( f, seekOrigin, SEEK_SET );
+ if ( rc == -1 )
+ {
+ _PR_MD_MAP_LSEEK_ERROR( errno );
+ rv = PR_FAILURE;
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return PR_SUCCESS;
+} /* end _PR_MD_LOCKFILE() */
+
+/*
+** _PR_MD_TLOCKFILE() - Test and Lock file.
+**
+** The _locking() call locks relative to the current file pointer.
+** This function is required to lock all of the file, so,
+** 1. Seek to the beginning of the file, preserving the original position.
+** 2. Attempt to Lock the file.
+** If the file is locked by someone else, try NO MORE.
+** 3. Re-position to the original position in the file.
+**
+** See the discussion of _PR_MD_LOCKFILE
+**
+**
+*/
+PRStatus
+_PR_MD_TLOCKFILE(PRInt32 f)
+{
+ PRInt32 rv = PR_SUCCESS; /* What we return */
+ long seekOrigin; /* original position in file */
+ PRInt32 rc; /* return value from system call */
+
+ /*
+ ** Seek to beginning of file, saving original position.
+ */
+ seekOrigin = lseek( f, 0l, SEEK_SET );
+ if ( rc == -1 )
+ {
+ _PR_MD_MAP_LSEEK_ERROR( errno );
+ return( PR_FAILURE );
+ }
+
+ /*
+ ** Attempt to lock the file. One ping; one ping only, Vasily.
+ ** If someone else has it, Reposition and return failure.
+ */
+ rc = _locking( f, _LK_NBLCK , 0x7fffffff );
+ if ( rc == -1 )
+ {
+ if ( errno != EACCES )
+ _PR_MD_MAP_LOCKF_ERROR( errno );
+ rv = PR_FAILURE;
+ }
+
+ /*
+ ** Now that the file is locked, maybe, re-position to
+ ** the original file position.
+ */
+ rc = lseek( f, seekOrigin, SEEK_SET );
+ if ( rc == -1 )
+ {
+ _PR_MD_MAP_LSEEK_ERROR( errno );
+ rv = PR_FAILURE;
+ }
+
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return rv;
+} /* end _PR_MD_TLOCKFILE() */
+
+
+/*
+** _PR_MD_UNLOCKFILE() - Unlock a file.
+**
+** See the discussion of _PR_MD_LOCKFILE
+**
+*/
+PRStatus
+_PR_MD_UNLOCKFILE(PRInt32 f)
+{
+ PRInt32 rv = PR_SUCCESS; /* What we return */
+ long seekOrigin; /* original position in file */
+ PRInt32 rc; /* return value from system call */
+
+ /*
+ ** Seek to beginning of file, saving original position.
+ */
+ seekOrigin = lseek( f, 0l, SEEK_SET );
+ if ( rc == -1 )
+ {
+ _PR_MD_MAP_LSEEK_ERROR( errno );
+ return( PR_FAILURE );
+ }
+
+ /*
+ ** Unlock the file.
+ */
+ rc = _locking( f, _LK_UNLCK , 0x7fffffff );
+ if ( rc == -1 )
+ {
+ _PR_MD_MAP_LOCKF_ERROR( errno );
+ rv = PR_FAILURE;
+ }
+
+ /*
+ ** Now that the file is unlocked, re-position to
+ ** the original file position.
+ */
+ rc = lseek( f, seekOrigin, SEEK_SET );
+ if ( rc == -1 )
+ {
+ _PR_MD_MAP_LSEEK_ERROR( errno );
+ rv = PR_FAILURE;
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return rv;
+} /* end _PR_MD_UNLOCKFILE() */
+
+/*
+** PR_Stat() -- Return status on a file
+**
+** This is a hack! ... See BugSplat: 98516
+** Basically, this hack takes a name and stat buffer as input.
+** The input stat buffer is presumed to be a Microsoft stat buffer.
+** The functions does a Watcom stat() then maps the result to
+** the MS stat buffer. ...
+**
+*/
+PR_IMPLEMENT(PRInt32) PR_Stat(const char *name, struct stat *buf)
+{
+ PRInt32 rv;
+ _MDMSStat *mssb = (_MDMSStat*) buf; /* this is Microsoft's stat buffer */
+ struct stat statBuf; /* this is Watcom's stat buffer */
+
+ /* First, get Watcom's idea of stat
+ ** then reformat it into a Microsoft idea of stat
+ */
+ rv = (PRInt32) _stat( name, &statBuf);
+ if (rv == 0l )
+ {
+ mssb->st_dev = statBuf.st_dev;
+ mssb->st_ino = statBuf.st_ino; /* not used, really */
+ mssb->st_mode = statBuf.st_mode;
+ mssb->st_nlink = 1; /* always 1, says MS */
+ mssb->st_uid = statBuf.st_uid;
+ mssb->st_gid = statBuf.st_gid;
+ mssb->st_rdev = statBuf.st_rdev; /* please Gh0d! Let these be the same */
+ mssb->st_size = statBuf.st_size;
+ mssb->st_atime = statBuf.st_atime;
+ mssb->st_mtime = statBuf.st_mtime;
+ mssb->st_ctime = statBuf.st_ctime;
+ }
+ return rv;
+} /* end PR_Stat() */
+
+
+
+/* $$ end W16io.c */
diff --git a/pr/src/md/windows/w16mem.c b/pr/src/md/windows/w16mem.c
new file mode 100644
index 00000000..f1fe6a08
--- /dev/null
+++ b/pr/src/md/windows/w16mem.c
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*******************************************************************
+** w16mem.c -- Implement memory segment functions.
+**
+**
+********************************************************************
+*/
+#include "primpl.h"
+
+
+/*
+** Allocate a new memory segment.
+**
+** Return the segment's access rights and size.
+*/
+PRStatus _MD_AllocSegment(PRSegment *seg, PRUint32 size, void *vaddr)
+{
+ PR_ASSERT(seg != 0);
+ PR_ASSERT(size != 0);
+ PR_ASSERT(vaddr == 0);
+
+ /*
+ ** Take the actual memory for the segment out of our Figment heap.
+ */
+
+ seg->vaddr = (char *)malloc(size);
+
+ if (seg->vaddr == NULL) {
+ return PR_FAILURE;
+ }
+
+ seg->access = PR_SEGMENT_RDWR;
+ seg->size = size;
+
+ return PR_SUCCESS;
+} /* --- end _MD_AllocSegment() --- */
+
+
+/*
+** Free previously allocated memory segment.
+*/
+void _MD_FreeSegment(PRSegment *seg)
+{
+ PR_ASSERT((seg->flags & _PR_SEG_VM) == 0);
+
+ if (seg->vaddr != NULL)
+ free( seg->vaddr );
+ return;
+} /* --- end _MD_FreeSegment() --- */
diff --git a/pr/src/md/windows/w16null.c b/pr/src/md/windows/w16null.c
new file mode 100644
index 00000000..a32e83d6
--- /dev/null
+++ b/pr/src/md/windows/w16null.c
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+#include <sys/timeb.h>
+
+
+struct _MDLock _pr_ioq_lock;
+HINSTANCE _pr_hInstance = NULL;
+char * _pr_top_of_task_stack;
+_PRInterruptTable _pr_interruptTable[] = { { 0 } };
+
+/*
+ *-----------------------------------------------------------------------
+ *
+ * PR_Now --
+ *
+ * Returns the current time in microseconds since the epoch.
+ * The epoch is midnight January 1, 1970 GMT.
+ * The implementation is machine dependent. This is the
+ * implementation for Windows.
+ * Cf. time_t time(time_t *tp)
+ *
+ *-----------------------------------------------------------------------
+ */
+
+#if defined(HAVE_WATCOM_BUG_2)
+PRTime __pascal __export __loadds
+#else
+PRTime
+#endif
+PR_Now(void)
+{
+ PRInt64 s, ms, ms2us, s2us;
+ struct timeb b;
+
+ ftime(&b);
+ LL_I2L(ms2us, PR_USEC_PER_MSEC);
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(s, b.time);
+ LL_I2L(ms, (PRInt32)b.millitm);
+ LL_MUL(ms, ms, ms2us);
+ LL_MUL(s, s, s2us);
+ LL_ADD(s, s, ms);
+ return s;
+}
+
+
+
+char *_PR_MD_GET_ENV(const char *name)
+{
+ return NULL;
+}
+
+PRIntn
+_PR_MD_PUT_ENV(const char *name)
+{
+ return NULL;
+}
+
+int CALLBACK LibMain( HINSTANCE hInst, WORD wDataSeg,
+ WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ _pr_hInstance = hInst;
+ return TRUE;
+}
+
+
+
+void
+_PR_MD_EARLY_INIT()
+{
+ _tzset();
+ return;
+}
+
+void
+_PR_MD_WAKEUP_CPUS( void )
+{
+ return;
+}
+
diff --git a/pr/src/md/windows/w16proc.c b/pr/src/md/windows/w16proc.c
new file mode 100644
index 00000000..16032aef
--- /dev/null
+++ b/pr/src/md/windows/w16proc.c
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+#include <sys/timeb.h>
+
+
+/*
+** Create Process.
+*/
+PRProcess * _PR_CreateWindowsProcess(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr)
+{
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+}
+
+PRStatus _PR_DetachWindowsProcess(PRProcess *process)
+{
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRStatus _PR_WaitWindowsProcess(PRProcess *process,
+ PRInt32 *exitCode)
+{
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRStatus _PR_KillWindowsProcess(PRProcess *process)
+{
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
diff --git a/pr/src/md/windows/w16sock.c b/pr/src/md/windows/w16sock.c
new file mode 100644
index 00000000..13231324
--- /dev/null
+++ b/pr/src/md/windows/w16sock.c
@@ -0,0 +1,1151 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+static int winsockNotPresent = 0;
+
+void
+_PR_MD_INIT_IO()
+{
+ int rv;
+
+ WORD WSAVersion = 0x0101;
+ WSADATA WSAData;
+
+ rv = WSAStartup( WSAVersion, &WSAData );
+ if ( rv != 0 )
+ {
+ _PR_MD_MAP_WSASTARTUP_ERROR(WSAGetLastError());
+ winsockNotPresent = 1;
+ }
+ return;
+}
+
+void
+_PR_MD_CLEANUP_BEFORE_EXIT(void)
+{
+ int rv;
+ int err;
+
+ rv = WSACleanup();
+ if ( rv == SOCKET_ERROR )
+ {
+ err = WSAGetLastError();
+ PR_ASSERT(0);
+ }
+ return;
+} /* end _PR_MD_CLEANUP_BEFORE_EXIT() */
+
+/* --- SOCKET IO --------------------------------------------------------- */
+
+PRStatus
+_MD_WindowsGetHostName(char *name, PRUint32 namelen)
+{
+ PRIntn rv;
+ PRInt32 syserror;
+
+ rv = gethostname(name, (PRInt32) namelen);
+ if (0 == rv) {
+ return PR_SUCCESS;
+ }
+ syserror = WSAGetLastError();
+ PR_ASSERT(WSANOTINITIALISED != syserror);
+ _PR_MD_MAP_GETHOSTNAME_ERROR(syserror);
+ return PR_FAILURE;
+}
+
+
+PRInt32
+_PR_MD_SOCKET(int af, int type, int flags)
+{
+ SOCKET sock;
+ PRUint32 one = 1;
+ PRInt32 rv;
+ PRInt32 err;
+
+ if ( winsockNotPresent )
+ return( (PRInt32)INVALID_SOCKET );
+
+ sock = socket(af, type, flags);
+
+ if (sock == INVALID_SOCKET )
+ {
+ int rv = GetLastError();
+ closesocket(sock);
+ _PR_MD_MAP_SOCKET_ERROR(rv);
+ return (PRInt32)INVALID_SOCKET;
+ }
+
+ /*
+ ** Make the socket Non-Blocking
+ */
+ rv = ioctlsocket( sock, FIONBIO, &one);
+ if ( rv != 0 )
+ {
+ err = WSAGetLastError();
+ return -1;
+ }
+
+ return (PRInt32)sock;
+}
+
+
+PRInt32
+_PR_MD_SOCKETAVAILABLE(PRFileDesc *fd)
+{
+ PRUint32 result;
+
+ if (ioctlsocket(fd->secret->md.osfd, FIONREAD, &result) < 0) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, WSAGetLastError());
+ return -1;
+ }
+ return result;
+}
+
+
+/*
+** _MD_CloseSocket() -- Close a socket
+**
+*/
+PRInt32
+_PR_MD_CLOSE_SOCKET(PRInt32 osfd)
+{
+ PRInt32 rv;
+
+ rv = closesocket((SOCKET) osfd );
+ if (rv < 0)
+ _PR_MD_MAP_CLOSE_ERROR(WSAGetLastError());
+
+ return rv;
+}
+
+PRInt32 _PR_MD_LISTEN(PRFileDesc *fd, PRIntn backlog)
+{
+ int rv, err;
+
+ rv = listen(fd->secret->md.osfd, backlog);
+ if ( rv == SOCKET_ERROR ) {
+ _PR_MD_MAP_LISTEN_ERROR(WSAGetLastError());
+ return(-1);
+ }
+ return(rv);
+}
+
+PRInt32
+_PR_MD_ACCEPT(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen,
+ PRIntervalTime timeout )
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 err;
+ PRIntn rv;
+
+ MD_ASSERTINT( *addrlen );
+
+ while ((rv = (SOCKET)accept(osfd, (struct sockaddr *) addr,
+ (int *)addrlen)) == INVALID_SOCKET ) {
+ err = WSAGetLastError();
+ if ( err == WSAEWOULDBLOCK ) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (_PR_WaitForFD(osfd, PR_POLL_READ, timeout) == 0) {
+ if ( _PR_PENDING_INTERRUPT(me))
+ {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ } else
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ rv = -1;
+ goto done;
+ } else if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ } else if ((err == WSAEINTR) && (!_PR_PENDING_INTERRUPT(me))){
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ }
+done:
+ if ( rv == INVALID_SOCKET )
+ return(-1 );
+ else
+ return(rv);
+} /* end _MD_Accept() */
+
+
+PRInt32
+_PR_MD_CONNECT(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 rv, err;
+
+ while ((rv = connect(osfd, (struct sockaddr *)addr, addrlen)) == -1) {
+ err = WSAGetLastError();
+ if (err == WSAEISCONN) {
+ rv = 0;
+ break;
+ }
+ /* for winsock1.1, it reports EALREADY as EINVAL */
+ if ((err == WSAEWOULDBLOCK)
+ ||(err == WSAEALREADY)
+ || (err = WSAEINVAL)) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (_PR_WaitForFD(osfd, PR_POLL_WRITE, timeout) == 0) {
+ if ( _PR_PENDING_INTERRUPT(me))
+ {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ } else
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ rv = -1;
+ goto done;
+ } else if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ } else if ((err == WSAEINTR) && (!_PR_PENDING_INTERRUPT(me))){
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ if (rv < 0) {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ }
+done:
+ return rv;
+}
+
+PRInt32
+_PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen)
+{
+ PRInt32 rv;
+ int one = 1;
+
+ rv = bind(fd->secret->md.osfd, (const struct sockaddr *)&(addr->inet), addrlen);
+
+ if (rv == SOCKET_ERROR) {
+ _PR_MD_MAP_BIND_ERROR(WSAGetLastError());
+ return -1;
+ }
+
+ return 0;
+}
+
+
+PRInt32
+_PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 rv, err;
+
+ while ((rv = recv(osfd,buf,amount,flags)) == -1) {
+ err = WSAGetLastError();
+ if ( err == WSAEWOULDBLOCK ) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (_PR_WaitForFD(osfd, PR_POLL_READ, timeout) == 0) {
+ if ( _PR_PENDING_INTERRUPT(me))
+ {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ } else
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ rv = -1;
+ goto done;
+ } else if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ } else if ((err == WSAEINTR) && (!_PR_PENDING_INTERRUPT(me))){
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_RECV_ERROR(err);
+ }
+done:
+ return(rv);
+}
+
+PRInt32
+_PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 rv, err;
+
+ while ((rv = send(osfd,buf,amount,flags)) == -1) {
+ err = WSAGetLastError();
+ if ( err == WSAEWOULDBLOCK ) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (_PR_WaitForFD(osfd, PR_POLL_WRITE, timeout) == 0) {
+ if ( _PR_PENDING_INTERRUPT(me))
+ {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ } else
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ rv = -1;
+ goto done;
+ } else if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ } else if ((err == WSAEINTR) && (!_PR_PENDING_INTERRUPT(me))){
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_SEND_ERROR(err);
+ }
+done:
+ return rv;
+}
+
+PRInt32
+_PR_MD_SENDTO(PRFileDesc*fd, const void *buf, PRInt32 amount, PRIntn flags,
+ const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 rv, err;
+
+ while ((rv = sendto(osfd, buf, amount, flags,
+ (struct sockaddr *) addr, addrlen)) == -1) {
+ err = WSAGetLastError();
+ if ( err == WSAEWOULDBLOCK ) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (_PR_WaitForFD(osfd, PR_POLL_WRITE, timeout) == 0) {
+ if ( _PR_PENDING_INTERRUPT(me))
+ {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ } else
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ rv = -1;
+ goto done;
+ } else if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ } else if ((err == WSAEINTR) && (!_PR_PENDING_INTERRUPT(me))){
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_SENDTO_ERROR(err);
+ }
+done:
+ return rv;
+}
+
+PRInt32
+_PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 rv, err;
+
+ while ((*addrlen = PR_NETADDR_SIZE(addr)),
+ ((rv = recvfrom(osfd, buf, amount, flags,
+ (struct sockaddr FAR *) addr,(int FAR *)addrlen)) == -1)) {
+ err = WSAGetLastError();
+ if ( err == WSAEWOULDBLOCK ) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (_PR_WaitForFD(osfd, PR_POLL_READ, timeout) == 0) {
+ if ( _PR_PENDING_INTERRUPT(me))
+ {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ } else
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ rv = -1;
+ goto done;
+ } else if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ } else if ((err == WSAEINTR) && (!_PR_PENDING_INTERRUPT(me))){
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_RECVFROM_ERROR(err);
+ }
+done:
+ return(rv);
+}
+
+PRInt32
+_PR_MD_WRITEV(PRFileDesc *fd, PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout)
+{
+ int index;
+ int sent = 0;
+ int rv;
+
+ for (index=0; index < iov_size; index++)
+ {
+
+/*
+ * XXX To be fixed
+ * should call PR_Send
+ */
+
+ rv = _PR_MD_SEND(fd, iov[index].iov_base, iov[index].iov_len, 0, timeout);
+ if (rv > 0)
+ sent += rv;
+ if ( rv != iov[index].iov_len )
+ {
+ if (sent <= 0)
+ return -1;
+ return -1;
+ }
+ }
+ return sent;
+}
+
+PRInt32
+_PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how)
+{
+PRInt32 rv;
+
+ rv = shutdown(fd->secret->md.osfd, how);
+ if (rv < 0)
+ _PR_MD_MAP_SHUTDOWN_ERROR(WSAGetLastError());
+ return rv;
+}
+
+PRStatus
+_PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
+{
+ PRInt32 rv;
+
+ rv = getsockname((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, (int *)len);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_GETSOCKNAME_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
+{
+ PRInt32 rv;
+
+ rv = getpeername((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, (int*)len);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_GETPEERNAME_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen)
+{
+ PRInt32 rv;
+
+ rv = getsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, (int*)optlen);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen)
+{
+ PRInt32 rv;
+
+ rv = setsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_SETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+void
+_PR_MD_MAKE_NONBLOCK(PRFileDesc *f)
+{
+ return; // do nothing!
+}
+
+/*
+** Wait for I/O on a single descriptor.
+ *
+ * return 0, if timed-out, else return 1
+*/
+PRInt32
+_PR_WaitForFD(PRInt32 osfd, PRUintn how, PRIntervalTime timeout)
+{
+ _PRWin16PollDesc *pd;
+ PRPollQueue *pq;
+ PRIntn is;
+ PRInt32 rv = 1;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
+
+ pd = &me->md.thr_pd;
+ pq = &me->md.thr_pq;
+ if (timeout == PR_INTERVAL_NO_WAIT) return 0;
+
+ pd->osfd = osfd;
+ pd->in_flags = how;
+ pd->out_flags = 0;
+
+ pq->pds = pd;
+ pq->npds = 1;
+
+ _PR_INTSOFF(is);
+ _PR_MD_IOQ_LOCK();
+ _PR_THREAD_LOCK(me);
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ _PR_THREAD_UNLOCK(me);
+ _PR_MD_IOQ_UNLOCK();
+ return 0;
+ }
+
+ pq->thr = me;
+ pq->on_ioq = PR_TRUE;
+ pq->timeout = timeout;
+ _PR_ADD_TO_IOQ((*pq), me->cpu);
+ if (how == PR_POLL_READ) {
+ FD_SET(osfd, &_PR_FD_READ_SET(me->cpu));
+ (_PR_FD_READ_CNT(me->cpu))[osfd]++;
+ } else if (how == PR_POLL_WRITE) {
+ FD_SET(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ (_PR_FD_WRITE_CNT(me->cpu))[osfd]++;
+ } else {
+ FD_SET(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ (_PR_FD_EXCEPTION_CNT(me->cpu))[osfd]++;
+ }
+ if (_PR_IOQ_MAX_OSFD(me->cpu) < osfd)
+ _PR_IOQ_MAX_OSFD(me->cpu) = osfd;
+ if (_PR_IOQ_TIMEOUT(me->cpu) > timeout)
+ _PR_IOQ_TIMEOUT(me->cpu) = timeout;
+
+ _PR_THREAD_LOCK(me);
+
+ _PR_SLEEPQ_LOCK(me->cpu);
+ _PR_ADD_SLEEPQ(me, timeout);
+ me->state = _PR_IO_WAIT;
+ me->io_pending = PR_TRUE;
+ me->io_suspended = PR_FALSE;
+ _PR_SLEEPQ_UNLOCK(me->cpu);
+ _PR_THREAD_UNLOCK(me);
+ _PR_MD_IOQ_UNLOCK();
+
+ _PR_MD_WAIT(me, timeout);
+ me->io_pending = PR_FALSE;
+ me->io_suspended = PR_FALSE;
+
+ /*
+ ** If we timed out the pollq might still be on the ioq. Remove it
+ ** before continuing.
+ */
+ if (pq->on_ioq) {
+ _PR_INTSOFF(is);
+ _PR_MD_IOQ_LOCK();
+ /*
+ * Need to check pq.on_ioq again
+ */
+ if (pq->on_ioq) {
+ PR_REMOVE_LINK(&pq->links);
+ if (how == PR_POLL_READ) {
+ if ((--(_PR_FD_READ_CNT(me->cpu))[osfd]) == 0)
+ FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
+
+ } else if (how == PR_POLL_WRITE) {
+ if ((--(_PR_FD_WRITE_CNT(me->cpu))[osfd]) == 0)
+ FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ } else {
+ if ((--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd]) == 0)
+ FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ }
+ }
+ _PR_MD_IOQ_UNLOCK();
+ rv = 0;
+ }
+ _PR_FAST_INTSON(is);
+ return(rv);
+}
+
+/*
+ * Unblock threads waiting for I/O
+ * used when interrupting threads
+ *
+ * NOTE: The thread lock should held when this function is called.
+ * On return, the thread lock is released.
+ */
+void _PR_Unblock_IO_Wait(PRThread *thr)
+{
+ int pri = thr->priority;
+ _PRCPU *cpu = thr->cpu;
+
+ PR_ASSERT(thr->flags & (_PR_ON_SLEEPQ | _PR_ON_PAUSEQ));
+ _PR_SLEEPQ_LOCK(cpu);
+ _PR_DEL_SLEEPQ(thr, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(cpu);
+
+ PR_ASSERT(!(thr->flags & _PR_IDLE_THREAD));
+ thr->state = _PR_RUNNABLE;
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(thr, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ _PR_THREAD_UNLOCK(thr);
+ _PR_MD_WAKEUP_WAITER(thr);
+}
+
+/*
+** Scan through io queue and find any bad fd's that triggered the error
+** from _MD_SELECT
+*/
+static void FindBadFDs(void)
+{
+ PRCList *q;
+ PRThread *me = _MD_CURRENT_THREAD();
+ int sockOpt;
+ int sockOptLen = sizeof(sockOpt);
+
+ PR_ASSERT(!_PR_IS_NATIVE_THREAD(me));
+ q = (_PR_IOQ(me->cpu)).next;
+ _PR_IOQ_MAX_OSFD(me->cpu) = -1;
+ _PR_IOQ_TIMEOUT(me->cpu) = PR_INTERVAL_NO_TIMEOUT;
+ while (q != &_PR_IOQ(me->cpu)) {
+ PRPollQueue *pq = _PR_POLLQUEUE_PTR(q);
+ PRBool notify = PR_FALSE;
+ _PRWin16PollDesc *pds = pq->pds;
+ _PRWin16PollDesc *epds = pds + pq->npds;
+ PRInt32 pq_max_osfd = -1;
+
+ q = q->next;
+ for (; pds < epds; pds++) {
+ PRInt32 osfd = pds->osfd;
+ pds->out_flags = 0;
+ PR_ASSERT(osfd >= 0 || pds->in_flags == 0);
+ if (pds->in_flags == 0) {
+ continue; /* skip this fd */
+ }
+ if ( getsockopt(osfd,
+ (int)SOL_SOCKET,
+ SO_TYPE,
+ (char*)&sockOpt,
+ &sockOptLen) == SOCKET_ERROR )
+ {
+ if ( WSAGetLastError() == WSAENOTSOCK )
+ {
+ PR_LOG(_pr_io_lm, PR_LOG_MAX,
+ ("file descriptor %d is bad", osfd));
+ pds->out_flags = PR_POLL_NVAL;
+ notify = PR_TRUE;
+ }
+ }
+ if (osfd > pq_max_osfd) {
+ pq_max_osfd = osfd;
+ }
+ }
+
+ if (notify) {
+ PRIntn pri;
+ PR_REMOVE_LINK(&pq->links);
+ pq->on_ioq = PR_FALSE;
+
+ /*
+ * Decrement the count of descriptors for each desciptor/event
+ * because this I/O request is being removed from the
+ * ioq
+ */
+ pds = pq->pds;
+ for (; pds < epds; pds++) {
+ PRInt32 osfd = pds->osfd;
+ PRInt16 in_flags = pds->in_flags;
+ PR_ASSERT(osfd >= 0 || in_flags == 0);
+ if (in_flags & PR_POLL_READ) {
+ if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
+ }
+ if (in_flags & PR_POLL_WRITE) {
+ if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ }
+ if (in_flags & PR_POLL_EXCEPT) {
+ if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ }
+ }
+
+ _PR_THREAD_LOCK(pq->thr);
+ if (pq->thr->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) {
+ _PRCPU *cpu = pq->thr->cpu;
+
+ _PR_SLEEPQ_LOCK(pq->thr->cpu);
+ _PR_DEL_SLEEPQ(pq->thr, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(pq->thr->cpu);
+
+ pri = pq->thr->priority;
+ pq->thr->state = _PR_RUNNABLE;
+
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(pq->thr, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ }
+ _PR_THREAD_UNLOCK(pq->thr);
+ } else {
+ if (pq->timeout < _PR_IOQ_TIMEOUT(me->cpu))
+ _PR_IOQ_TIMEOUT(me->cpu) = pq->timeout;
+ if (_PR_IOQ_MAX_OSFD(me->cpu) < pq_max_osfd)
+ _PR_IOQ_MAX_OSFD(me->cpu) = pq_max_osfd;
+ }
+ }
+} /* end FindBadFDs() */
+
+/*
+** Called by the scheduler when there is nothing to do. This means that
+** all threads are blocked on some monitor somewhere.
+**
+** Pause the current CPU. longjmp to the cpu's pause stack
+*/
+PRInt32 _PR_MD_PAUSE_CPU( PRIntervalTime ticks)
+{
+ PRThread *me = _MD_CURRENT_THREAD();
+ struct timeval timeout, *tvp;
+ fd_set r, w, e;
+ fd_set *rp, *wp, *ep;
+ PRInt32 max_osfd, nfd;
+ PRInt32 rv;
+ PRCList *q;
+ PRUint32 min_timeout;
+
+ PR_ASSERT(_PR_MD_GET_INTSOFF() != 0);
+
+ /*
+ * assigment of fd_sets
+ */
+ r = _PR_FD_READ_SET(me->cpu);
+ w = _PR_FD_WRITE_SET(me->cpu);
+ e = _PR_FD_EXCEPTION_SET(me->cpu);
+
+ rp = &r;
+ wp = &w;
+ ep = &e;
+
+ max_osfd = _PR_IOQ_MAX_OSFD(me->cpu) + 1;
+ min_timeout = _PR_IOQ_TIMEOUT(me->cpu);
+ /*
+ ** Compute the minimum timeout value: make it the smaller of the
+ ** timeouts specified by the i/o pollers or the timeout of the first
+ ** sleeping thread.
+ */
+ q = _PR_SLEEPQ(me->cpu).next;
+
+ if (q != &_PR_SLEEPQ(me->cpu)) {
+ PRThread *t = _PR_THREAD_PTR(q);
+
+ if (t->sleep < min_timeout) {
+ min_timeout = t->sleep;
+ }
+ }
+ if (min_timeout > ticks) {
+ min_timeout = ticks;
+ }
+
+ if (min_timeout == PR_INTERVAL_NO_TIMEOUT) {
+ tvp = NULL;
+ } else {
+ timeout.tv_sec = PR_IntervalToSeconds(min_timeout);
+ timeout.tv_usec = PR_IntervalToMicroseconds(min_timeout)
+ % PR_USEC_PER_SEC;
+ tvp = &timeout;
+ }
+
+ _PR_MD_IOQ_UNLOCK();
+ _MD_CHECK_FOR_EXIT();
+ /*
+ * check for i/o operations
+ */
+
+ nfd = _MD_SELECT(max_osfd, rp, wp, ep, tvp);
+
+ _MD_CHECK_FOR_EXIT();
+ _PR_MD_IOQ_LOCK();
+ /*
+ ** Notify monitors that are associated with the selected descriptors.
+ */
+ if (nfd > 0) {
+ q = _PR_IOQ(me->cpu).next;
+ _PR_IOQ_MAX_OSFD(me->cpu) = -1;
+ _PR_IOQ_TIMEOUT(me->cpu) = PR_INTERVAL_NO_TIMEOUT;
+ while (q != &_PR_IOQ(me->cpu)) {
+ PRPollQueue *pq = _PR_POLLQUEUE_PTR(q);
+ PRBool notify = PR_FALSE;
+ _PRWin16PollDesc *pds = pq->pds;
+ _PRWin16PollDesc *epds = pds + pq->npds;
+ PRInt32 pq_max_osfd = -1;
+
+ q = q->next;
+ for (; pds < epds; pds++) {
+ PRInt32 osfd = pds->osfd;
+ PRInt16 in_flags = pds->in_flags;
+ PRInt16 out_flags = 0;
+ PR_ASSERT(osfd >= 0 || in_flags == 0);
+ if ((in_flags & PR_POLL_READ) && FD_ISSET(osfd, rp)) {
+ out_flags |= PR_POLL_READ;
+ }
+ if ((in_flags & PR_POLL_WRITE) && FD_ISSET(osfd, wp)) {
+ out_flags |= PR_POLL_WRITE;
+ }
+ if ((in_flags & PR_POLL_EXCEPT) && FD_ISSET(osfd, ep)) {
+ out_flags |= PR_POLL_EXCEPT;
+ }
+ pds->out_flags = out_flags;
+ if (out_flags) {
+ notify = PR_TRUE;
+ }
+ if (osfd > pq_max_osfd) {
+ pq_max_osfd = osfd;
+ }
+ }
+ if (notify == PR_TRUE) {
+ PRIntn pri;
+ PRThread *thred;
+
+ PR_REMOVE_LINK(&pq->links);
+ pq->on_ioq = PR_FALSE;
+
+ /*
+ * Decrement the count of descriptors for each desciptor/event
+ * because this I/O request is being removed from the
+ * ioq
+ */
+ pds = pq->pds;
+ for (; pds < epds; pds++) {
+ PRInt32 osfd = pds->osfd;
+ PRInt16 in_flags = pds->in_flags;
+ PR_ASSERT(osfd >= 0 || in_flags == 0);
+ if (in_flags & PR_POLL_READ) {
+ if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
+ }
+ if (in_flags & PR_POLL_WRITE) {
+ if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ }
+ if (in_flags & PR_POLL_EXCEPT) {
+ if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ }
+ }
+ thred = pq->thr;
+ _PR_THREAD_LOCK(thred);
+ if (pq->thr->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) {
+ _PRCPU *cpu = thred->cpu;
+ _PR_SLEEPQ_LOCK(pq->thr->cpu);
+ _PR_DEL_SLEEPQ(pq->thr, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(pq->thr->cpu);
+
+ pri = pq->thr->priority;
+ pq->thr->state = _PR_RUNNABLE;
+
+ pq->thr->cpu = cpu;
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(pq->thr, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ if (_pr_md_idle_cpus > 1)
+ _PR_MD_WAKEUP_WAITER(thred);
+ }
+ _PR_THREAD_UNLOCK(thred);
+ } else {
+ if (pq->timeout < _PR_IOQ_TIMEOUT(me->cpu))
+ _PR_IOQ_TIMEOUT(me->cpu) = pq->timeout;
+ if (_PR_IOQ_MAX_OSFD(me->cpu) < pq_max_osfd)
+ _PR_IOQ_MAX_OSFD(me->cpu) = pq_max_osfd;
+ }
+ }
+ } else if (nfd < 0) {
+ if ( WSAGetLastError() == WSAENOTSOCK )
+ {
+ FindBadFDs();
+ } else {
+ PR_LOG(_pr_io_lm, PR_LOG_MAX, ("select() failed with errno %d",
+ errno));
+ }
+ }
+ _PR_MD_IOQ_UNLOCK();
+ return(0);
+
+} /* end _PR_MD_PAUSE_CPU() */
+
+
+/*
+** _MD_pr_poll() -- Implement MD polling
+**
+** The function was snatched (re-used) from the unix implementation.
+**
+** The native thread stuff was deleted.
+** The pollqueue is instantiated on the mdthread structure
+** to keep the stack frame from being corrupted when this
+** thread is waiting on the poll.
+**
+*/
+extern PRInt32
+_MD_PR_POLL(PRPollDesc *pds, PRIntn npds,
+ PRIntervalTime timeout)
+{
+ PRPollDesc *pd, *epd;
+ PRInt32 n, err, pdcnt;
+ PRIntn is;
+ _PRWin16PollDesc *spds, *spd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRPollQueue *pq;
+
+ pq = &me->md.thr_pq;
+
+ /*
+ * XXX
+ * PRPollDesc has a PRFileDesc field, fd, while the IOQ
+ * is a list of PRPollQueue structures, each of which contains
+ * a _PRWin16PollDesc. A _PRWin16PollDesc struct contains
+ * the OS file descriptor, osfd, and not a PRFileDesc.
+ * So, we have allocate memory for _PRWin16PollDesc structures,
+ * copy the flags information from the pds list and have pq
+ * point to this list of _PRWin16PollDesc structures.
+ *
+ * It would be better if the memory allocation can be avoided.
+ */
+
+ spds = (_PRWin16PollDesc*) PR_MALLOC(npds * sizeof(_PRWin16PollDesc));
+ if (!spds) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+ spd = spds;
+
+ _PR_INTSOFF(is);
+ _PR_MD_IOQ_LOCK();
+ _PR_THREAD_LOCK(me);
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ _PR_MD_IOQ_UNLOCK();
+ PR_DELETE(spds);
+ return -1;
+ }
+
+ pdcnt = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ PRInt32 osfd;
+ PRInt16 in_flags = pd->in_flags;
+ PRFileDesc *bottom = pd->fd;
+
+ if ((NULL == bottom) || (in_flags == 0)) {
+ continue;
+ }
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ osfd = bottom->secret->md.osfd;
+
+ PR_ASSERT(osfd >= 0 || in_flags == 0);
+
+ spd->osfd = osfd;
+ spd->in_flags = pd->in_flags;
+ spd++;
+ pdcnt++;
+
+ if (in_flags & PR_POLL_READ) {
+ FD_SET(osfd, &_PR_FD_READ_SET(me->cpu));
+ _PR_FD_READ_CNT(me->cpu)[osfd]++;
+ }
+ if (in_flags & PR_POLL_WRITE) {
+ FD_SET(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ (_PR_FD_WRITE_CNT(me->cpu))[osfd]++;
+ }
+ if (in_flags & PR_POLL_EXCEPT) {
+ FD_SET(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ (_PR_FD_EXCEPTION_CNT(me->cpu))[osfd]++;
+ }
+ if (osfd > _PR_IOQ_MAX_OSFD(me->cpu))
+ _PR_IOQ_MAX_OSFD(me->cpu) = osfd;
+ }
+ if (timeout < _PR_IOQ_TIMEOUT(me->cpu))
+ _PR_IOQ_TIMEOUT(me->cpu) = timeout;
+
+
+ pq->pds = spds;
+ pq->npds = pdcnt;
+
+ pq->thr = me;
+ pq->on_ioq = PR_TRUE;
+ pq->timeout = timeout;
+ _PR_ADD_TO_IOQ((*pq), me->cpu);
+ _PR_SLEEPQ_LOCK(me->cpu);
+ _PR_ADD_SLEEPQ(me, timeout);
+ me->state = _PR_IO_WAIT;
+ me->io_pending = PR_TRUE;
+ me->io_suspended = PR_FALSE;
+ _PR_SLEEPQ_UNLOCK(me->cpu);
+ _PR_THREAD_UNLOCK(me);
+ _PR_MD_IOQ_UNLOCK();
+
+ _PR_MD_WAIT(me, timeout);
+
+ me->io_pending = PR_FALSE;
+ me->io_suspended = PR_FALSE;
+
+ /*
+ * Copy the out_flags from the _PRWin16PollDesc structures to the
+ * user's PRPollDesc structures and free the allocated memory
+ */
+ spd = spds;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ if ((NULL == pd->fd) || (pd->in_flags == 0)) {
+ pd->out_flags = 0;
+ continue;
+ }
+ pd->out_flags = spd->out_flags;
+ spd++;
+ }
+ PR_DELETE(spds);
+
+ /*
+ ** If we timed out the pollq might still be on the ioq. Remove it
+ ** before continuing.
+ */
+ if (pq->on_ioq) {
+ _PR_INTSOFF(is);
+ _PR_MD_IOQ_LOCK();
+ /*
+ * Need to check pq.on_ioq again
+ */
+ if (pq->on_ioq == PR_TRUE) {
+ PR_REMOVE_LINK(&pq->links);
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ PRInt32 osfd;
+ PRInt16 in_flags = pd->in_flags;
+ PRFileDesc *bottom = pd->fd;
+
+ if ((NULL == bottom) || (in_flags == 0)) {
+ continue;
+ }
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ osfd = bottom->secret->md.osfd;
+ PR_ASSERT(osfd >= 0 || in_flags == 0);
+ if (in_flags & PR_POLL_READ) {
+ if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
+ }
+ if (in_flags & PR_POLL_WRITE) {
+ if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ }
+ if (in_flags & PR_POLL_EXCEPT) {
+ if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ }
+ }
+ }
+ _PR_MD_IOQ_UNLOCK();
+ _PR_INTSON(is);
+ }
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ } else {
+ n = 0;
+ if (pq->on_ioq == PR_FALSE) {
+ /* Count the number of ready descriptors */
+ while (--npds >= 0) {
+ if (pds->out_flags) {
+ n++;
+ }
+ pds++;
+ }
+ }
+ return n;
+ }
+} /* end _MD_pr_poll() */
diff --git a/pr/src/md/windows/w16stdio.c b/pr/src/md/windows/w16stdio.c
new file mode 100644
index 00000000..cdd420ae
--- /dev/null
+++ b/pr/src/md/windows/w16stdio.c
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+** w16stdio.c -- Callback functions for Win16 stdio read/write.
+**
+**
+*/
+#include "primpl.h"
+
+/*
+** _PL_MDStdioWrite() -- Win16 hackery to get console output
+**
+** Returns: number of bytes written.
+**
+*/
+PRInt32
+_PL_W16StdioWrite( void *buf, PRInt32 amount )
+{
+ int rc;
+
+ rc = fputs( buf, stdout );
+ if ( rc == EOF )
+ {
+ // something about errno
+ return(PR_FAILURE);
+ }
+ return( strlen(buf));
+} /* end _PL_fputs() */
+
+/*
+** _PL_W16StdioRead() -- Win16 hackery to get console input
+**
+*/
+PRInt32
+_PL_W16StdioRead( void *buf, PRInt32 amount )
+{
+ char *bp;
+
+ bp = fgets( buf, (int) amount, stdin );
+ if ( bp == NULL )
+ {
+ // something about errno
+ return(PR_FAILURE);
+ }
+
+ return( strlen(buf));
+} /* end _PL_fgets() */
+/* --- end w16stdio.c --- */
+
+/*
+** Wrappers, linked into the client, that call
+** functions in LibC
+**
+*/
+
+/*
+** _PL_W16CallBackPuts() -- Wrapper for puts()
+**
+*/
+int PR_CALLBACK _PL_W16CallBackPuts( const char *outputString )
+{
+ return( puts( outputString ));
+} /* end _PL_W16CallBackPuts() */
+
+/*
+** _PL_W16CallBackStrftime() -- Wrapper for strftime()
+**
+*/
+size_t PR_CALLBACK _PL_W16CallBackStrftime(
+ char *s,
+ size_t len,
+ const char *fmt,
+ const struct tm *p )
+{
+ return( strftime( s, len, fmt, p ));
+} /* end _PL_W16CallBackStrftime() */
+
+/*
+** _PL_W16CallBackMalloc() -- Wrapper for malloc()
+**
+*/
+void * PR_CALLBACK _PL_W16CallBackMalloc( size_t size )
+{
+ return( malloc( size ));
+} /* end _PL_W16CallBackMalloc() */
+
+/*
+** _PL_W16CallBackCalloc() -- Wrapper for calloc()
+**
+*/
+void * PR_CALLBACK _PL_W16CallBackCalloc( size_t n, size_t size )
+{
+ return( calloc( n, size ));
+} /* end _PL_W16CallBackCalloc() */
+
+/*
+** _PL_W16CallBackRealloc() -- Wrapper for realloc()
+**
+*/
+void * PR_CALLBACK _PL_W16CallBackRealloc(
+ void *old_blk,
+ size_t size )
+{
+ return( realloc( old_blk, size ));
+} /* end _PL_W16CallBackRealloc() */
+
+/*
+** _PL_W16CallBackFree() -- Wrapper for free()
+**
+*/
+void PR_CALLBACK _PL_W16CallBackFree( void *ptr )
+{
+ free( ptr );
+ return;
+} /* end _PL_W16CallBackFree() */
+
+/*
+** _PL_W16CallBackGetenv() -- Wrapper for getenv()
+**
+*/
+void * PR_CALLBACK _PL_W16CallBackGetenv( const char *name )
+{
+ return( getenv( name ));
+} /* end _PL_W16CallBackGetenv */
+
+
+/*
+** _PL_W16CallBackPutenv() -- Wrapper for putenv()
+**
+*/
+int PR_CALLBACK _PL_W16CallBackPutenv( const char *assoc )
+{
+ return( putenv( assoc ));
+} /* end _PL_W16CallBackGetenv */
diff --git a/pr/src/md/windows/w16thred.c b/pr/src/md/windows/w16thred.c
new file mode 100644
index 00000000..f6716759
--- /dev/null
+++ b/pr/src/md/windows/w16thred.c
@@ -0,0 +1,407 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+#include <sys/timeb.h>
+#include <stdio.h>
+
+/*
+** DispatchTrace -- define a thread dispatch trace entry
+**
+** The DispatchTrace oject(s) are instantiated in a single
+** array. Think of the array as a push-down stack; entry
+** zero is the most recent, entry one the next most recent, etc.
+** For each time PR_MD_RESTORE_CONTEXT() is called, the array
+** is Pushed down and entry zero is overwritten with data
+** for the newly dispatched thread.
+**
+** Function TraceDispatch() manages the DispatchTrace array.
+**
+*/
+typedef struct DispatchTrace
+{
+ PRThread * thread;
+ PRUint32 state;
+ PRInt16 mdThreadNumber;
+ PRInt16 unused;
+ PRThreadPriority priority;
+
+} DispatchTrace, *DispatchTracePtr ;
+
+static void TraceDispatch( PRThread *thread );
+
+
+PRThread *_pr_primordialThread;
+
+/*
+** Note: the static variables must be on the data-segment because
+** the stack is destroyed during shadow-stack copy operations.
+**
+*/
+static char * pSource; /* ptr to sourc of a "shadow-stack" copy */
+static char * pTarget; /* ptr to target of a "shadow-stack" copy */
+static int cxByteCount; /* number of bytes for "shadow-stack" copy */
+static int bytesMoved; /* instrumentation: WRT "shadow-stack" copy */
+static FILE * file1 = 0; /* instrumentation: WRT debug */
+
+#define NUM_DISPATCHTRACE_OBJECTS 24
+static DispatchTrace dt[NUM_DISPATCHTRACE_OBJECTS] = {0}; /* instrumentation: WRT dispatch */
+static PRUint32 dispatchCount = 0; /* instrumentation: number of thread dispatches */
+
+static int OldPriorityOfPrimaryThread = -1;
+static int TimeSlicesOnNonPrimaryThread = 0;
+static PRUint32 threadNumber = 1; /* Instrumentation: monotonically increasing number */
+
+
+
+/*
+** _PR_MD_FINAL_INIT() -- Final MD Initialization
+**
+** Poultry Problems! ... The stack, as allocated by PR_NewStack()
+** is called from here, late in initialization, because PR_NewStack()
+** requires lots of things working. When some elements of the
+** primordial thread are created, early in initialization, the
+** shadow stack is not one of these things. The "shadow stack" is
+** created here, late in initiailization using PR_NewStack(), to
+** ensure consistency in creation of the related objects.
+**
+** A new ThreadStack, and all its affiliated structures, is allocated
+** via the call to PR_NewStack(). The PRThread structure in the
+** new stack is ignored; the old PRThread structure is used (why?).
+** The old PRThreadStack structure is abandoned.
+**
+*/
+void
+_PR_MD_FINAL_INIT()
+{
+ PRThreadStack * stack = 0;
+ PRInt32 stacksize = 0;
+ PRThread * me = _PR_MD_CURRENT_THREAD();
+
+ _PR_ADJUST_STACKSIZE( stacksize );
+ stack = _PR_NewStack( stacksize );
+
+ me->stack = stack;
+ stack->thr = me;
+
+ return;
+} /* --- end _PR_MD_FINAL_INIT() --- */
+
+
+void
+_MD_INIT_RUNNING_CPU( struct _PRCPU *cpu )
+{
+ PR_INIT_CLIST(&(cpu->md.ioQ));
+ cpu->md.ioq_max_osfd = -1;
+ cpu->md.ioq_timeout = PR_INTERVAL_NO_TIMEOUT;
+}
+
+
+void
+_PR_MD_YIELD( void )
+{
+ PR_ASSERT(0);
+}
+
+/*
+** _PR_MD_INIT_STACK() -- Win16 specific Stack initialization.
+**
+**
+*/
+
+void
+_PR_MD_INIT_STACK( PRThreadStack *ts, PRIntn redzone )
+{
+ ts->md.stackTop = ts->stackTop - sizeof(PRThread);
+ ts->md.cxByteCount = 0;
+
+ return;
+} /* --- end _PR_MD_INIT_STACK() --- */
+
+/*
+** _PR_MD_INIT_THREAD() -- Win16 specific Thread initialization.
+**
+*/
+PRStatus
+_PR_MD_INIT_THREAD(PRThread *thread)
+{
+ if ( thread->flags & _PR_PRIMORDIAL)
+ {
+ _pr_primordialThread = thread;
+ thread->md.threadNumber = 1;
+ }
+ else
+ {
+ thread->md.threadNumber = ++threadNumber;
+ }
+
+ thread->md.magic = _MD_MAGIC_THREAD;
+ strcpy( thread->md.guardBand, "GuardBand" );
+
+ return PR_SUCCESS;
+}
+
+
+PRStatus
+_PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ _MD_SWITCH_CONTEXT( thread );
+
+ return( PR_SUCCESS );
+}
+
+void *PR_W16GetExceptionContext(void)
+{
+ return _MD_CURRENT_THREAD()->md.exceptionContext;
+}
+
+void
+PR_W16SetExceptionContext(void *context)
+{
+ _MD_CURRENT_THREAD()->md.exceptionContext = context;
+}
+
+
+
+
+/*
+** _MD_RESTORE_CONTEXT() -- Resume execution of thread 't'.
+**
+** Win16 threading is based on the NSPR 2.0 general model of
+** user threads. It differs from the general model in that a
+** single "real" stack segment is used for execution of all
+** threads. The context of the suspended threads is preserved
+** in the md.context [and related members] of the PRThread
+** structure. The stack context of the suspended thread is
+** preserved in a "shadow stack" object.
+**
+** _MD_RESTORE_CONTEXT() implements most of the thread switching
+** for NSPR's implementation of Win16 theads.
+**
+** Operations Notes:
+**
+** Function PR_NewStack() in prustack.c allocates a new
+** PRThreadStack, PRStack, PRSegment, and a "shadow" stack
+** for a thread. These structures are wired together to
+** form the basis of Win16 threads. The thread and shadow
+** stack structures are created as part of PR_CreateThread().
+**
+** Note! Some special "magic" is applied to the "primordial"
+** thread. The physical layout of the PRThread, PRThreadStack,
+** shadow stack, ... is somewhat different. Watch yourself when
+** mucking around with it. ... See _PR_MD_FINAL_INIT() for most
+** of the special treatment of the primordial thread.
+**
+** Function _PR_MD_INIT_STACK() initializes the value of
+** PRThreadStack member md.cxByteCount to zero; there
+** is no context to be restored for a thread's initial
+** dispatch. The value of member md.stackTop is set to
+** point to the highest usable address on the shadow stack.
+** This point corresponds to _pr_top_of_task_stack on the
+** system's operating stack.
+**
+** _pr_top_of_task_stack points to a place on the system stack
+** considered to be "close to the top". Stack context is preserved
+** relative to this point.
+**
+** Reminder: In x86 architecture, the stack grows "down".
+** That is: the stack pointer (SP register) is decremented
+** to push objects onto the stack or when a call is made.
+**
+** Function _PR_MD_WAIT() invokes macro _MD_SWITCH_CONTEXT();
+** this causes the hardware registers to be preserved in a
+** CATCHBUF structure using function Catch() [see _win16.h],
+** then calls PR_Schedule() to select a new thread for dispatch.
+** PR_Schedule() calls _MD_RESTORE_CONTEXT() to cause the thread
+** being suspended's stack to be preserved, to restore the
+** stack of the to-be-dispactched thread, and to restore the
+** to-be-dispactched thread's hardware registers.
+**
+** At the moment _PR_MD_RESTORE_CONTEXT() is called, the stack
+** pointer (SP) is less than the reference pointer
+** _pr_top_of_task_stack. The distance difference between the SP and
+** _pr_top_of_task_stack is the amount of stack that must be preserved.
+** This value, cxByteCount, is calculated then preserved in the
+** PRThreadStack.md.cxByteCount for later use (size of stack
+** context to restore) when this thread is dispatched again.
+**
+** A C language for() loop is used to copy, byte-by-byte, the
+** stack data being preserved starting at the "address of t"
+** [Note: 't' is the argument passed to _PR_MD_RESTORE_CONTEXT()]
+** for the length of cxByteCount.
+**
+** variables pSource and pTarget are the calculated source and
+** destination pointers for the stack copy operation. These
+** variables are static scope because they cannot be instantiated
+** on the stack itself, since the stack is clobbered by restoring
+** the to-be-dispatched thread's stack context.
+**
+** After preserving the suspended thread's stack and architectural
+** context, the to-be-dispatched thread's stack context is copied
+** from its shadow stack to the system operational stack. The copy
+** is done in a small fragment of in-line assembly language. Note:
+** In NSPR 1.0, a while() loop was used to do the copy; when compiled
+** with the MS C 1.52c compiler, the short while loop used no
+** stack variables. The Watcom compiler, specified for use on NSPR 2.0,
+** uses stack variables to implement the same while loop. This is
+** a no-no! The copy operation clobbers these variables making the
+** results of the copy ... unpredictable ... So, a short piece of
+** inline assembly language is used to effect the copy.
+**
+** Following the restoration of the to-be-dispatched thread's
+** stack context, another short inline piece of assemble language
+** is used to set the SP register to correspond to what it was
+** when the to-be-dispatched thread was suspended. This value
+** uses the thread's stack->md.cxByteCount as a negative offset
+** from _pr_top_of_task_stack as the new value of SP.
+**
+** Finally, Function Throw() is called to restore the architectural
+** context of the to-be-dispatched thread.
+**
+** At this point, the newly dispatched thread appears to resume
+** execution following the _PR_MD_SWITCH_CONTEXT() macro.
+**
+** OK, this ain't rocket-science, but it can confuse you easily.
+** If you have to work on this stuff, please take the time to
+** draw, on paper, the structures (PRThread, PRThreadStack,
+** PRSegment, the "shadow stack", the system stack and the related
+** global variables). Hand step it thru the debugger to make sure
+** you understand it very well before making any changes. ...
+** YMMV.
+**
+*/
+void _MD_RESTORE_CONTEXT(PRThread *t)
+{
+ dispatchCount++;
+ TraceDispatch( t );
+ /*
+ ** This is a good opportunity to make sure that the main
+ ** mozilla thread actually gets some time. If interrupts
+ ** are on, then we know it is safe to check if the main
+ ** thread is being starved. If moz has not been scheduled
+ ** for a long time, then then temporarily bump the fe priority
+ ** up so that it gets to run at least one.
+ */
+// #if 0 // lth. condition off for debug.
+ if (_pr_primordialThread == t) {
+ if (OldPriorityOfPrimaryThread != -1) {
+ PR_SetThreadPriority(_pr_primordialThread, OldPriorityOfPrimaryThread);
+ OldPriorityOfPrimaryThread = -1;
+ }
+ TimeSlicesOnNonPrimaryThread = 0;
+ } else {
+ TimeSlicesOnNonPrimaryThread++;
+ }
+
+ if ((TimeSlicesOnNonPrimaryThread >= 20) && (OldPriorityOfPrimaryThread == -1)) {
+ OldPriorityOfPrimaryThread = PR_GetThreadPriority(_pr_primordialThread);
+ PR_SetThreadPriority(_pr_primordialThread, 31);
+ TimeSlicesOnNonPrimaryThread = 0;
+ }
+// #endif
+ /*
+ ** Save the Task Stack into the "shadow stack" of the current thread
+ */
+ cxByteCount = (int) ((PRUint32) _pr_top_of_task_stack - (PRUint32) &t );
+ pSource = (char *) &t;
+ pTarget = (char *)((PRUint32)_pr_currentThread->stack->md.stackTop
+ - (PRUint32)cxByteCount );
+ _pr_currentThread->stack->md.cxByteCount = cxByteCount;
+
+ for( bytesMoved = 0; bytesMoved < cxByteCount; bytesMoved++ )
+ *(pTarget + bytesMoved ) = *(pSource + bytesMoved );
+
+ /* Mark the new thread as the current thread */
+ _pr_currentThread = t;
+
+ /*
+ ** Now copy the "shadow stack" of the new thread into the Task Stack
+ **
+ ** REMEMBER:
+ ** After the stack has been copied, ALL local variables in this function
+ ** are invalid !!
+ */
+ cxByteCount = t->stack->md.cxByteCount;
+ pSource = t->stack->md.stackTop - cxByteCount;
+ pTarget = _pr_top_of_task_stack - cxByteCount;
+
+ errno = (_pr_currentThread)->md.errcode;
+
+ __asm
+ {
+ mov cx, cxByteCount
+ mov si, WORD PTR [pSource]
+ mov di, WORD PTR [pTarget]
+ mov ax, WORD PTR [pTarget + 2]
+ mov es, ax
+ mov ax, WORD PTR [pSource + 2]
+ mov bx, ds
+ mov ds, ax
+ rep movsb
+ mov ds, bx
+ }
+
+ /*
+ ** IMPORTANT:
+ ** ----------
+ ** SS:SP is now invalid :-( This means that all local variables and
+ ** function arguments are invalid and NO function calls can be
+ ** made !!! We must fix up SS:SP so that function calls can safely
+ ** be made...
+ */
+
+ __asm {
+ mov ax, WORD PTR [_pr_top_of_task_stack]
+ sub ax, cxByteCount
+ mov sp, ax
+ };
+
+ /*
+ ** Resume execution of thread: t by restoring the thread's context.
+ **
+ */
+ Throw((_pr_currentThread)->md.context, 1);
+} /* --- end MD_RESTORE_CONTEXT() --- */
+
+
+static void TraceDispatch( PRThread *thread )
+{
+ int i;
+
+ /*
+ ** push all DispatchTrace objects to down one slot.
+ ** Note: the last entry is lost; last-1 becomes last, etc.
+ */
+ for( i = NUM_DISPATCHTRACE_OBJECTS -2; i >= 0; i-- )
+ {
+ dt[i +1] = dt[i];
+ }
+
+ /*
+ ** Build dt[0] from t
+ */
+ dt->thread = thread;
+ dt->state = thread->state;
+ dt->mdThreadNumber = thread->md.threadNumber;
+ dt->priority = thread->priority;
+
+ return;
+} /* --- end TraceDispatch() --- */
+
+
+/* $$ end W16thred.c */
diff --git a/pr/src/md/windows/w32poll.c b/pr/src/md/windows/w32poll.c
new file mode 100644
index 00000000..e71fd936
--- /dev/null
+++ b/pr/src/md/windows/w32poll.c
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * This file implements _PR_MD_PR_POLL for Win32.
+ */
+
+#include "primpl.h"
+
+#if !defined(_PR_GLOBAL_THREADS_ONLY)
+
+struct select_data_s {
+ PRInt32 status;
+ PRInt32 error;
+ fd_set *rd, *wt, *ex;
+ struct timeval *tv;
+};
+
+static void
+_PR_MD_select_thread(void *cdata)
+{
+ struct select_data_s *cd = (struct select_data_s *)cdata;
+
+ cd->status = select(0, cd->rd, cd->wt, cd->ex, cd->tv);
+
+ if (cd->status == SOCKET_ERROR) {
+ cd->error = WSAGetLastError();
+ }
+}
+
+#endif /* !defined(_PR_GLOBAL_THREADS_ONLY) */
+
+PRInt32
+_PR_MD_PR_POLL(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
+{
+ PRPollDesc *pd, *epd;
+ int n, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ fd_set rd, wt, ex;
+ struct timeval tv, *tvp = NULL;
+
+ FD_ZERO(&rd);
+ FD_ZERO(&wt);
+ FD_ZERO(&ex);
+
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ SOCKET osfd;
+ PRInt16 in_flags = pd->in_flags;
+ PRFileDesc *bottom = pd->fd;
+
+ if ((NULL == bottom) || (in_flags == 0)) {
+ continue;
+ }
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ osfd = (SOCKET) bottom->secret->md.osfd;
+
+ if (in_flags & PR_POLL_READ) {
+ FD_SET(osfd, &rd);
+ }
+ if (in_flags & PR_POLL_WRITE) {
+ FD_SET(osfd, &wt);
+ }
+ if (in_flags & PR_POLL_EXCEPT) {
+ FD_SET(osfd, &ex);
+ }
+ }
+ if (timeout != PR_INTERVAL_NO_TIMEOUT) {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(timeout) % PR_USEC_PER_SEC;
+ tvp = &tv;
+ }
+
+#if defined(_PR_GLOBAL_THREADS_ONLY)
+ n = _MD_SELECT(0, &rd, &wt, &ex, tvp);
+#else
+ if (_PR_IS_NATIVE_THREAD(me)) {
+ n = _MD_SELECT(0, &rd, &wt, &ex, tvp);
+ } else {
+ PRThread *selectThread;
+ struct select_data_s data;
+ data.status = 0;
+ data.error = 0;
+ data.rd = &rd;
+ data.wt = &wt;
+ data.ex = &ex;
+ data.tv = tvp;
+
+ selectThread = PR_CreateThread(PR_USER_THREAD,
+ _PR_MD_select_thread,
+ &data,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (selectThread == NULL) {
+ return -1;
+ }
+ PR_JoinThread(selectThread);
+ n = data.status;
+ if (n == SOCKET_ERROR) {
+ WSASetLastError(data.error);
+ }
+ }
+#endif
+
+ if (n > 0) {
+ n = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ SOCKET osfd;
+ PRInt16 in_flags = pd->in_flags;
+ PRInt16 out_flags = 0;
+ PRFileDesc *bottom = pd->fd;
+
+ if ((NULL == bottom) || (in_flags == 0)) {
+ pd->out_flags = 0;
+ continue;
+ }
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ osfd = (SOCKET) bottom->secret->md.osfd;
+
+ if ((in_flags & PR_POLL_READ) && FD_ISSET(osfd, &rd)) {
+ out_flags |= PR_POLL_READ;
+ }
+ if ((in_flags & PR_POLL_WRITE) && FD_ISSET(osfd, &wt)) {
+ out_flags |= PR_POLL_WRITE;
+ }
+ if ((in_flags & PR_POLL_EXCEPT) && FD_ISSET(osfd, &ex)) {
+ out_flags |= PR_POLL_EXCEPT;
+ }
+ pd->out_flags = out_flags;
+ if (out_flags) {
+ n++;
+ }
+ }
+ PR_ASSERT(n > 0);
+ } else if (n == SOCKET_ERROR) {
+ err = WSAGetLastError();
+ if (err == WSAENOTSOCK) {
+ /* Find the bad fds */
+ n = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ int optval;
+ int optlen = sizeof(optval);
+ PRFileDesc *bottom = pd->fd;
+
+ pd->out_flags = 0;
+ if ((NULL == bottom) || (pd->in_flags == 0)) {
+ continue;
+ }
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ if (getsockopt(bottom->secret->md.osfd, SOL_SOCKET,
+ SO_TYPE, (char *) &optval, &optlen) == -1) {
+ PR_ASSERT(WSAGetLastError() == WSAENOTSOCK);
+ if (WSAGetLastError() == WSAENOTSOCK) {
+ pd->out_flags = PR_POLL_NVAL;
+ n++;
+ }
+ }
+ }
+ PR_ASSERT(n > 0);
+ } else {
+ _PR_MD_MAP_SELECT_ERROR(err);
+ }
+ }
+
+ return n;
+}
diff --git a/pr/src/md/windows/w95cv.c b/pr/src/md/windows/w95cv.c
new file mode 100644
index 00000000..dfbcb05a
--- /dev/null
+++ b/pr/src/md/windows/w95cv.c
@@ -0,0 +1,328 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * w95cv.c -- Windows 95 Machine-Dependent Code for Condition Variables
+ *
+ * We implement our own condition variable wait queue. Each thread
+ * has a semaphore object (thread->md.blocked_sema) to block on while
+ * waiting on a condition variable.
+ *
+ * We use a deferred condition notify algorithm. When PR_NotifyCondVar
+ * or PR_NotifyAllCondVar is called, the condition notifies are simply
+ * recorded in the _MDLock structure. We defer the condition notifies
+ * until right after we unlock the lock. This way the awakened threads
+ * have a better chance to reaquire the lock.
+ */
+
+#include "primpl.h"
+
+/*
+ * AddThreadToCVWaitQueueInternal --
+ *
+ * Add the thread to the end of the condition variable's wait queue.
+ * The CV's lock must be locked when this function is called.
+ */
+
+static void
+AddThreadToCVWaitQueueInternal(PRThread *thred, struct _MDCVar *cv)
+{
+ PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL)
+ || (cv->waitTail == NULL && cv->waitHead == NULL));
+ cv->nwait += 1;
+ thred->md.inCVWaitQueue = PR_TRUE;
+ thred->md.next = NULL;
+ thred->md.prev = cv->waitTail;
+ if (cv->waitHead == NULL) {
+ cv->waitHead = thred;
+ } else {
+ cv->waitTail->md.next = thred;
+ }
+ cv->waitTail = thred;
+}
+
+/*
+ * md_UnlockAndPostNotifies --
+ *
+ * Unlock the lock, and then do the deferred condition notifies.
+ * If waitThred and waitCV are not NULL, waitThred is added to
+ * the wait queue of waitCV before the lock is unlocked.
+ *
+ * This function is called by _PR_MD_WAIT_CV and _PR_MD_UNLOCK,
+ * the two places where a lock is unlocked.
+ */
+static void
+md_UnlockAndPostNotifies(
+ _MDLock *lock,
+ PRThread *waitThred,
+ _MDCVar *waitCV)
+{
+ PRIntn index;
+ _MDNotified post;
+ _MDNotified *notified, *prev = NULL;
+
+ /*
+ * Time to actually notify any conditions that were affected
+ * while the lock was held. Get a copy of the list that's in
+ * the lock structure and then zero the original. If it's
+ * linked to other such structures, we own that storage.
+ */
+ post = lock->notified; /* a safe copy; we own the lock */
+
+#if defined(DEBUG)
+ ZeroMemory(&lock->notified, sizeof(_MDNotified)); /* reset */
+#else
+ lock->notified.length = 0; /* these are really sufficient */
+ lock->notified.link = NULL;
+#endif
+
+ /*
+ * Figure out how many threads we need to wake up.
+ */
+ notified = &post; /* this is where we start */
+ do {
+ for (index = 0; index < notified->length; ++index) {
+ _MDCVar *cv = notified->cv[index].cv;
+ PRThread *thred;
+ int i;
+
+ /* Fast special case: no waiting threads */
+ if (cv->waitHead == NULL) {
+ notified->cv[index].notifyHead = NULL;
+ continue;
+ }
+
+ /* General case */
+ if (-1 == notified->cv[index].times) {
+ /* broadcast */
+ thred = cv->waitHead;
+ while (thred != NULL) {
+ thred->md.inCVWaitQueue = PR_FALSE;
+ thred = thred->md.next;
+ }
+ notified->cv[index].notifyHead = cv->waitHead;
+ cv->waitHead = cv->waitTail = NULL;
+ cv->nwait = 0;
+ } else {
+ thred = cv->waitHead;
+ i = notified->cv[index].times;
+ while (thred != NULL && i > 0) {
+ thred->md.inCVWaitQueue = PR_FALSE;
+ thred = thred->md.next;
+ i--;
+ }
+ notified->cv[index].notifyHead = cv->waitHead;
+ cv->waitHead = thred;
+ if (cv->waitHead == NULL) {
+ cv->waitTail = NULL;
+ } else {
+ if (cv->waitHead->md.prev != NULL) {
+ cv->waitHead->md.prev->md.next = NULL;
+ cv->waitHead->md.prev = NULL;
+ }
+ }
+ cv->nwait -= notified->cv[index].times - i;
+ }
+ }
+ notified = notified->link;
+ } while (NULL != notified);
+
+ if (waitThred) {
+ AddThreadToCVWaitQueueInternal(waitThred, waitCV);
+ }
+
+ /* Release the lock before notifying */
+ LeaveCriticalSection(&lock->mutex);
+
+ notified = &post; /* this is where we start */
+ do {
+ for (index = 0; index < notified->length; ++index) {
+ PRThread *thred;
+ PRThread *next;
+
+ thred = notified->cv[index].notifyHead;
+ while (thred != NULL) {
+ BOOL rv;
+
+ next = thred->md.next;
+ thred->md.prev = thred->md.next = NULL;
+
+ rv = ReleaseSemaphore(thred->md.blocked_sema, 1, NULL);
+ PR_ASSERT(rv != 0);
+ thred = next;
+ }
+ }
+ prev = notified;
+ notified = notified->link;
+ if (&post != prev) PR_DELETE(prev);
+ } while (NULL != notified);
+}
+
+/*
+ * Notifies just get posted to the protecting mutex. The
+ * actual notification is done when the lock is released so that
+ * MP systems don't contend for a lock that they can't have.
+ */
+static void md_PostNotifyToCvar(_MDCVar *cvar, _MDLock *lock,
+ PRBool broadcast)
+{
+ PRIntn index = 0;
+ _MDNotified *notified = &lock->notified;
+
+ while (1) {
+ for (index = 0; index < notified->length; ++index) {
+ if (notified->cv[index].cv == cvar) {
+ if (broadcast) {
+ notified->cv[index].times = -1;
+ } else if (-1 != notified->cv[index].times) {
+ notified->cv[index].times += 1;
+ }
+ return;
+ }
+ }
+ /* if not full, enter new CV in this array */
+ if (notified->length < _MD_CV_NOTIFIED_LENGTH) break;
+
+ /* if there's no link, create an empty array and link it */
+ if (NULL == notified->link) {
+ notified->link = PR_NEWZAP(_MDNotified);
+ }
+
+ notified = notified->link;
+ }
+
+ /* A brand new entry in the array */
+ notified->cv[index].times = (broadcast) ? -1 : 1;
+ notified->cv[index].cv = cvar;
+ notified->length += 1;
+}
+
+/*
+ * _PR_MD_NEW_CV() -- Creating new condition variable
+ * ... Solaris uses cond_init() in similar function.
+ *
+ * returns: -1 on failure
+ * 0 when it succeeds.
+ *
+ */
+PRInt32
+_PR_MD_NEW_CV(_MDCVar *cv)
+{
+ cv->magic = _MD_MAGIC_CV;
+ /*
+ * The waitHead, waitTail, and nwait fields are zeroed
+ * when the PRCondVar structure is created.
+ */
+ return 0;
+}
+
+void _PR_MD_FREE_CV(_MDCVar *cv)
+{
+ cv->magic = (PRUint32)-1;
+ return;
+}
+
+/*
+ * _PR_MD_WAIT_CV() -- Wait on condition variable
+ */
+void _PR_MD_WAIT_CV(_MDCVar *cv, _MDLock *lock, PRIntervalTime timeout )
+{
+ PRThread *thred = _PR_MD_CURRENT_THREAD();
+ DWORD rv;
+ DWORD msecs = (timeout == PR_INTERVAL_NO_TIMEOUT) ?
+ INFINITE : PR_IntervalToMilliseconds(timeout);
+
+ /*
+ * If we have pending notifies, post them now.
+ */
+ if (0 != lock->notified.length) {
+ md_UnlockAndPostNotifies(lock, thred, cv);
+ } else {
+ AddThreadToCVWaitQueueInternal(thred, cv);
+ LeaveCriticalSection(&lock->mutex);
+ }
+
+ /* Wait for notification or timeout; don't really care which */
+ rv = WaitForSingleObject(thred->md.blocked_sema, msecs);
+
+ EnterCriticalSection(&(lock->mutex));
+
+ PR_ASSERT(rv != WAIT_ABANDONED);
+ PR_ASSERT(rv != WAIT_FAILED);
+ PR_ASSERT(rv != WAIT_OBJECT_0 || thred->md.inCVWaitQueue == PR_FALSE);
+
+ if (rv == WAIT_TIMEOUT) {
+ if (thred->md.inCVWaitQueue) {
+ PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL)
+ || (cv->waitTail == NULL && cv->waitHead == NULL));
+ cv->nwait -= 1;
+ thred->md.inCVWaitQueue = PR_FALSE;
+ if (cv->waitHead == thred) {
+ cv->waitHead = thred->md.next;
+ if (cv->waitHead == NULL) {
+ cv->waitTail = NULL;
+ } else {
+ cv->waitHead->md.prev = NULL;
+ }
+ } else {
+ PR_ASSERT(thred->md.prev != NULL);
+ thred->md.prev->md.next = thred->md.next;
+ if (thred->md.next != NULL) {
+ thred->md.next->md.prev = thred->md.prev;
+ } else {
+ PR_ASSERT(cv->waitTail == thred);
+ cv->waitTail = thred->md.prev;
+ }
+ }
+ thred->md.next = thred->md.prev = NULL;
+ } else {
+ /*
+ * This thread must have been notified, but the
+ * ReleaseSemaphore call happens after WaitForSingleObject
+ * times out. Wait on the semaphore again to make it
+ * non-signaled. We assume this wait won't take long.
+ */
+ rv = WaitForSingleObject(thred->md.blocked_sema, INFINITE);
+ PR_ASSERT(rv == WAIT_OBJECT_0);
+ }
+ }
+ PR_ASSERT(thred->md.inCVWaitQueue == PR_FALSE);
+ return;
+} /* --- end _PR_MD_WAIT_CV() --- */
+
+void _PR_MD_NOTIFY_CV(_MDCVar *cv, _MDLock *lock)
+{
+ md_PostNotifyToCvar(cv, lock, PR_FALSE);
+ return;
+}
+
+void _PR_MD_NOTIFYALL_CV(_MDCVar *cv, _MDLock *lock)
+{
+ md_PostNotifyToCvar(cv, lock, PR_TRUE);
+ return;
+}
+
+void _PR_MD_UNLOCK(_MDLock *lock)
+{
+ if (0 != lock->notified.length) {
+ md_UnlockAndPostNotifies(lock, NULL, NULL);
+ } else {
+ LeaveCriticalSection(&lock->mutex);
+ }
+ return;
+}
diff --git a/pr/src/md/windows/w95io.c b/pr/src/md/windows/w95io.c
new file mode 100644
index 00000000..90811721
--- /dev/null
+++ b/pr/src/md/windows/w95io.c
@@ -0,0 +1,871 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/* Windows 95 IO module
+ *
+ * Assumes synchronous I/O.
+ *
+ */
+
+#include "primpl.h"
+#include <direct.h>
+
+struct _MDLock _pr_ioq_lock;
+
+/*
+ * The NSPR epoch (00:00:00 1 Jan 1970 UTC) in FILETIME.
+ * We store the value in a PRTime variable for convenience.
+ * This constant is used by _PR_FileTimeToPRTime().
+ */
+static const PRTime _pr_filetime_offset = 116444736000000000i64;
+
+void
+_PR_MD_INIT_IO()
+{
+ WORD WSAVersion = 0x0101;
+ WSADATA WSAData;
+
+ WSAStartup( WSAVersion, &WSAData );
+
+#ifdef DEBUG
+ /* Doublecheck _pr_filetime_offset's hard-coded value is correct. */
+ {
+ SYSTEMTIME systime;
+ union {
+ PRTime prt;
+ FILETIME ft;
+ } filetime;
+ BOOL rv;
+
+ systime.wYear = 1970;
+ systime.wMonth = 1;
+ /* wDayOfWeek is ignored */
+ systime.wDay = 1;
+ systime.wHour = 0;
+ systime.wMinute = 0;
+ systime.wSecond = 0;
+ systime.wMilliseconds = 0;
+
+ rv = SystemTimeToFileTime(&systime, &filetime.ft);
+ PR_ASSERT(0 != rv);
+ PR_ASSERT(filetime.prt == _pr_filetime_offset);
+ }
+#endif /* DEBUG */
+}
+
+PRStatus
+_PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ DWORD rv;
+
+ PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ?
+ INFINITE : PR_IntervalToMilliseconds(ticks);
+ rv = WaitForSingleObject(thread->md.blocked_sema, msecs);
+ switch(rv)
+ {
+ case WAIT_OBJECT_0:
+ return PR_SUCCESS;
+ break;
+ case WAIT_TIMEOUT:
+ _PR_THREAD_LOCK(thread);
+ if (thread->state == _PR_IO_WAIT) {
+ ;
+ } else {
+ if (thread->wait.cvar != NULL) {
+ thread->wait.cvar = NULL;
+ _PR_THREAD_UNLOCK(thread);
+ } else {
+ /* The CVAR was notified just as the timeout
+ * occurred. This led to us being notified twice.
+ * call WaitForSingleObject() to clear the semaphore.
+ */
+ _PR_THREAD_UNLOCK(thread);
+ rv = WaitForSingleObject(thread->md.blocked_sema, 0);
+ PR_ASSERT(rv == WAIT_OBJECT_0);
+ }
+ }
+ return PR_SUCCESS;
+ break;
+ default:
+ return PR_FAILURE;
+ break;
+ }
+}
+PRStatus
+_PR_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if ( _PR_IS_NATIVE_THREAD(thread) )
+ {
+ if (ReleaseSemaphore(thread->md.blocked_sema, 1, NULL) == FALSE)
+ return PR_FAILURE;
+ else
+ return PR_SUCCESS;
+ }
+}
+
+
+/* --- FILE IO ----------------------------------------------------------- */
+/*
+ * _PR_MD_OPEN() -- Open a file
+ *
+ * returns: a fileHandle
+ *
+ * The NSPR open flags (osflags) are translated into flags for Win95
+ *
+ * Mode seems to be passed in as a unix style file permissions argument
+ * as in 0666, in the case of opening the logFile.
+ *
+ */
+PRInt32
+_PR_MD_OPEN(const char *name, PRIntn osflags, int mode)
+{
+ HANDLE file;
+ PRInt32 access = 0;
+ PRInt32 flags = 0;
+ PRInt32 flag6 = 0;
+
+ if (osflags & PR_SYNC) flag6 = FILE_FLAG_WRITE_THROUGH;
+
+ if (osflags & PR_RDONLY || osflags & PR_RDWR)
+ access |= GENERIC_READ;
+ if (osflags & PR_WRONLY || osflags & PR_RDWR)
+ access |= GENERIC_WRITE;
+ if (osflags & PR_CREATE_FILE)
+ flags = OPEN_ALWAYS;
+ else if (osflags & PR_TRUNCATE)
+ flags = CREATE_ALWAYS;
+ else
+ flags = OPEN_EXISTING;
+
+ file = CreateFile(name,
+ access,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ flags,
+ flag6,
+ NULL);
+ if (file == INVALID_HANDLE_VALUE) {
+ _PR_MD_MAP_OPEN_ERROR(GetLastError());
+ return -1;
+ }
+
+ return (PRInt32)file;
+}
+
+PRInt32
+_PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len)
+{
+ PRUint32 bytes;
+ int rv, err;
+
+ rv = ReadFile((HANDLE)fd->secret->md.osfd,
+ (LPVOID)buf,
+ len,
+ &bytes,
+ NULL);
+
+ if (rv == 0)
+ {
+ err = GetLastError();
+ /* ERROR_HANDLE_EOF can only be returned by async io */
+ PR_ASSERT(err != ERROR_HANDLE_EOF);
+ if (err == ERROR_BROKEN_PIPE)
+ return 0;
+ else {
+ _PR_MD_MAP_READ_ERROR(err);
+ return -1;
+ }
+ }
+ return bytes;
+}
+
+PRInt32
+_PR_MD_WRITE(PRFileDesc *fd, void *buf, PRInt32 len)
+{
+ PRInt32 f = fd->secret->md.osfd;
+ PRInt32 bytes;
+ int rv;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ rv = WriteFile((HANDLE)f,
+ buf,
+ len,
+ &bytes,
+ NULL );
+
+ if (rv == 0)
+ {
+ _PR_MD_MAP_WRITE_ERROR(GetLastError());
+ return -1;
+ }
+ return bytes;
+} /* --- end _PR_MD_WRITE() --- */
+
+PRInt32
+_PR_MD_LSEEK(PRFileDesc *fd, PRInt32 offset, int whence)
+{
+ PRInt32 rv;
+
+ rv = SetFilePointer((HANDLE)fd->secret->md.osfd, offset, 0, whence);
+
+ /*
+ * If the lpDistanceToMoveHigh argument (third argument) is
+ * NULL, SetFilePointer returns 0xffffffff on failure.
+ */
+ if (-1 == rv) {
+ _PR_MD_MAP_LSEEK_ERROR(GetLastError());
+ return -1;
+ } else
+ return rv;
+}
+
+PRInt64
+_PR_MD_LSEEK64(PRFileDesc *fd, PRInt64 offset, int whence)
+{
+ PRInt64 result;
+ PRInt32 rv, low = (PRInt32)offset, hi = (PRInt32)(offset >> 32);
+
+ rv = SetFilePointer((HANDLE)fd->secret->md.osfd, low, &hi, whence);
+
+ /*
+ * If the lpDistanceToMoveHigh argument (third argument) is
+ * NULL, SetFilePointer returns 0xffffffff on failure.
+ */
+ if (-1 == rv)
+ {
+ _PR_MD_MAP_LSEEK_ERROR(GetLastError());
+ return -1;
+ }
+
+ result = (hi << 32) + rv;
+ return result;
+}
+
+/*
+ * This is documented to succeed on read-only files, but Win32's
+ * FlushFileBuffers functions fails with "access denied" in such a
+ * case. So we only signal an error if the error is *not* "access
+ * denied".
+ */
+PRInt32
+_PR_MD_FSYNC(PRFileDesc *fd)
+{
+ /*
+ * From the documentation:
+ *
+ * On Windows NT, the function FlushFileBuffers fails if hFile
+ * is a handle to console output. That is because console
+ * output is not buffered. The function returns FALSE, and
+ * GetLastError returns ERROR_INVALID_HANDLE.
+ *
+ * On the other hand, on Win95, it returns without error. I cannot
+ * assume that 0, 1, and 2 are console, because if someone closes
+ * System.out and then opens a file, they might get file descriptor
+ * 1. An error on *that* version of 1 should be reported, whereas
+ * an error on System.out (which was the original 1) should be
+ * ignored. So I use isatty() to ensure that such an error was due
+ * to this bogosity, and if it was, I ignore the error.
+ */
+
+ long handle = _get_osfhandle(fd->secret->md.osfd);
+ BOOL ok = FlushFileBuffers((HANDLE)handle);
+
+ if (!ok) {
+ DWORD err = GetLastError();
+ if (err != ERROR_ACCESS_DENIED) { // from winerror.h
+ _PR_MD_MAP_FSYNC_ERROR(err);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+PRInt32
+_MD_CloseFile(PRInt32 osfd)
+{
+ PRInt32 rv;
+
+ rv = (CloseHandle((HANDLE)osfd))?0:-1;
+ if (rv == -1)
+ _PR_MD_MAP_CLOSE_ERROR(GetLastError());
+ return rv;
+}
+
+
+/* --- DIR IO ------------------------------------------------------------ */
+#define GetFileFromDIR(d) (d)->d_entry.cFileName
+#define FileIsHidden(d) ((d)->d_entry.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
+
+void FlipSlashes(char *cp, int len)
+{
+ while (--len >= 0) {
+ if (cp[0] == '/') {
+ cp[0] = PR_DIRECTORY_SEPARATOR;
+ }
+ cp++;
+ }
+}
+
+/*
+**
+** Local implementations of standard Unix RTL functions which are not provided
+** by the VC RTL.
+**
+*/
+
+PRStatus
+_PR_MD_CLOSE_DIR(_MDDir *d)
+{
+ if ( d ) {
+ if (FindClose(d->d_hdl)) {
+ d->magic = (PRUint32)-1;
+ return PR_SUCCESS;
+ } else {
+ _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError());
+ return PR_FAILURE;
+ }
+ }
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+}
+
+
+PRStatus
+_PR_MD_OPEN_DIR(_MDDir *d, const char *name)
+{
+ char filename[ MAX_PATH ];
+
+ PR_snprintf(filename, MAX_PATH, "%s%s%s",
+ name, PR_DIRECTORY_SEPARATOR_STR, "*.*");
+ FlipSlashes( filename, strlen(filename) );
+
+ d->d_hdl = FindFirstFile( filename, &(d->d_entry) );
+ if ( d->d_hdl == INVALID_HANDLE_VALUE ) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return PR_FAILURE;
+ }
+ d->firstEntry = PR_TRUE;
+ d->magic = _MD_MAGIC_DIR;
+ return PR_SUCCESS;
+}
+
+char *
+_PR_MD_READ_DIR(_MDDir *d, PRIntn flags)
+{
+ PRInt32 err;
+ BOOL rv;
+ char *fileName;
+
+ if ( d ) {
+ while (1) {
+ if (d->firstEntry) {
+ d->firstEntry = PR_FALSE;
+ rv = 1;
+ } else {
+ rv = FindNextFile(d->d_hdl, &(d->d_entry));
+ }
+ if (rv == 0) {
+ break;
+ }
+ fileName = GetFileFromDIR(d);
+ if ( (flags & PR_SKIP_DOT) &&
+ (fileName[0] == '.') && (fileName[1] == '\0'))
+ continue;
+ if ( (flags & PR_SKIP_DOT_DOT) &&
+ (fileName[0] == '.') && (fileName[1] == '.') &&
+ (fileName[2] == '\0'))
+ continue;
+ if ( (flags & PR_SKIP_HIDDEN) && FileIsHidden(d))
+ continue;
+ return fileName;
+ }
+ err = GetLastError();
+ PR_ASSERT(NO_ERROR != err);
+ _PR_MD_MAP_READDIR_ERROR(err);
+ return NULL;
+ }
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+}
+
+PRInt32
+_PR_MD_DELETE(const char *name)
+{
+ if (DeleteFile(name)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_DELETE_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+static void
+_PR_FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm)
+{
+ PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime));
+ CopyMemory(prtm, filetime, sizeof(PRTime));
+ *prtm = (*prtm - _pr_filetime_offset) / 10i64;
+
+#ifdef DEBUG
+ /* Doublecheck our calculation. */
+ {
+ SYSTEMTIME systime;
+ PRExplodedTime etm;
+ PRTime cmp; /* for comparison */
+ BOOL rv;
+
+ rv = FileTimeToSystemTime(filetime, &systime);
+ PR_ASSERT(0 != rv);
+
+ /*
+ * PR_ImplodeTime ignores wday and yday.
+ */
+ etm.tm_usec = systime.wMilliseconds * PR_USEC_PER_MSEC;
+ etm.tm_sec = systime.wSecond;
+ etm.tm_min = systime.wMinute;
+ etm.tm_hour = systime.wHour;
+ etm.tm_mday = systime.wDay;
+ etm.tm_month = systime.wMonth - 1;
+ etm.tm_year = systime.wYear;
+ /*
+ * It is not well-documented what time zone the FILETIME's
+ * are in. WIN32_FIND_DATA is documented to be in UTC (GMT).
+ * But BY_HANDLE_FILE_INFORMATION is unclear about this.
+ * By our best judgement, we assume that FILETIME is in UTC.
+ */
+ etm.tm_params.tp_gmt_offset = 0;
+ etm.tm_params.tp_dst_offset = 0;
+ cmp = PR_ImplodeTime(&etm);
+
+ /*
+ * SYSTEMTIME is in milliseconds precision, so we convert PRTime's
+ * microseconds to milliseconds before doing the comparison.
+ */
+ PR_ASSERT((cmp / PR_USEC_PER_MSEC) == (*prtm / PR_USEC_PER_MSEC));
+ }
+#endif /* DEBUG */
+}
+
+PRInt32
+_PR_MD_STAT(const char *fn, struct stat *info)
+{
+ PRInt32 rv;
+
+ rv = _stat(fn, (struct _stat *)info);
+ if (-1 == rv) {
+ /*
+ * Check for MSVC runtime library _stat() bug.
+ * (It's really a bug in FindFirstFile().)
+ * If a pathname ends in a backslash or slash,
+ * e.g., c:\temp\ or c:/temp/, _stat() will fail.
+ * Note: a pathname ending in a slash (e.g., c:/temp/)
+ * can be handled by _stat() on NT but not on Win95.
+ *
+ * We remove the backslash or slash at the end and
+ * try again.
+ */
+
+ int len = strlen(fn);
+ if (len > 0 && len <= _MAX_PATH
+ && (fn[len - 1] == '\\' || fn[len - 1] == '/')) {
+ char newfn[_MAX_PATH + 1];
+
+ strcpy(newfn, fn);
+ newfn[len - 1] = '\0';
+ rv = _stat(newfn, (struct _stat *)info);
+ }
+ }
+
+ if (-1 == rv) {
+ _PR_MD_MAP_STAT_ERROR(errno);
+ }
+ return rv;
+}
+
+#define _PR_IS_SLASH(ch) ((ch) == '/' || (ch) == '\\')
+
+/*
+ * IsRootDirectory --
+ *
+ * Return PR_TRUE if the pathname 'fn' is a valid root directory,
+ * else return PR_FALSE. The char buffer pointed to by 'fn' must
+ * be writable. During the execution of this function, the contents
+ * of the buffer pointed to by 'fn' may be modified, but on return
+ * the original contents will be restored. 'buflen' is the size of
+ * the buffer pointed to by 'fn'.
+ *
+ * Root directories come in three formats:
+ * 1. / or \, meaning the root directory of the current drive.
+ * 2. C:/ or C:\, where C is a drive letter.
+ * 3. \\<server name>\<share point name>\ or
+ * \\<server name>\<share point name>, meaning the root directory
+ * of a UNC (Universal Naming Convention) name.
+ */
+
+static PRBool
+IsRootDirectory(char *fn, size_t buflen)
+{
+ char *p;
+ PRBool slashAdded = PR_FALSE;
+ PRBool rv = PR_FALSE;
+
+ if (_PR_IS_SLASH(fn[0]) && fn[1] == '\0') {
+ return PR_TRUE;
+ }
+
+ if (isalpha(fn[0]) && fn[1] == ':' && _PR_IS_SLASH(fn[2])
+ && fn[3] == '\0') {
+ rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
+ return rv;
+ }
+
+ /* The UNC root directory */
+
+ if (_PR_IS_SLASH(fn[0]) && _PR_IS_SLASH(fn[1])) {
+ /* The 'server' part should have at least one character. */
+ p = &fn[2];
+ if (*p == '\0' || _PR_IS_SLASH(*p)) {
+ return PR_FALSE;
+ }
+
+ /* look for the next slash */
+ do {
+ p++;
+ } while (*p != '\0' && !_PR_IS_SLASH(*p));
+ if (*p == '\0') {
+ return PR_FALSE;
+ }
+
+ /* The 'share' part should have at least one character. */
+ p++;
+ if (*p == '\0' || _PR_IS_SLASH(*p)) {
+ return PR_FALSE;
+ }
+
+ /* look for the final slash */
+ do {
+ p++;
+ } while (*p != '\0' && !_PR_IS_SLASH(*p));
+ if (_PR_IS_SLASH(*p) && p[1] != '\0') {
+ return PR_FALSE;
+ }
+ if (*p == '\0') {
+ /*
+ * GetDriveType() doesn't work correctly if the
+ * path is of the form \\server\share, so we add
+ * a final slash temporarily.
+ */
+ if ((p + 1) < (fn + buflen)) {
+ *p++ = '\\';
+ *p = '\0';
+ slashAdded = PR_TRUE;
+ } else {
+ return PR_FALSE; /* name too long */
+ }
+ }
+ rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
+ /* restore the 'fn' buffer */
+ if (slashAdded) {
+ *--p = '\0';
+ }
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info)
+{
+ HANDLE hFindFile;
+ WIN32_FIND_DATA findFileData;
+ char pathbuf[MAX_PATH + 1];
+
+ if (NULL == fn || '\0' == *fn) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+
+ /*
+ * FindFirstFile() expands wildcard characters. So
+ * we make sure the pathname contains no wildcard.
+ */
+ if (NULL != strpbrk(fn, "?*")) {
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, 0);
+ return -1;
+ }
+
+ hFindFile = FindFirstFile(fn, &findFileData);
+ if (INVALID_HANDLE_VALUE == hFindFile) {
+ DWORD len;
+ char *filePart;
+
+ /*
+ * FindFirstFile() does not work correctly on root directories.
+ * It also doesn't work correctly on a pathname that ends in a
+ * slash. So we first check to see if the pathname specifies a
+ * root directory. If not, and if the pathname ends in a slash,
+ * we remove the final slash and try again.
+ */
+
+ /*
+ * If the pathname does not contain ., \, and /, it cannot be
+ * a root directory or a pathname that ends in a slash.
+ */
+ if (NULL == strpbrk(fn, ".\\/")) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return -1;
+ }
+ len = GetFullPathName(fn, sizeof(pathbuf), pathbuf,
+ &filePart);
+ PR_ASSERT(0 != len);
+ if (len > sizeof(pathbuf)) {
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
+ return -1;
+ }
+ if (IsRootDirectory(pathbuf, sizeof(pathbuf))) {
+ info->type = PR_FILE_DIRECTORY;
+ info->size = 0;
+ /*
+ * These timestamps don't make sense for root directories.
+ */
+ info->modifyTime = 0;
+ info->creationTime = 0;
+ return 0;
+ }
+ if (!_PR_IS_SLASH(pathbuf[len - 1])) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return -1;
+ } else {
+ pathbuf[len - 1] = '\0';
+ hFindFile = FindFirstFile(pathbuf, &findFileData);
+ if (INVALID_HANDLE_VALUE == hFindFile) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return -1;
+ }
+ }
+ }
+
+ FindClose(hFindFile);
+
+ if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ info->type = PR_FILE_DIRECTORY;
+ } else {
+ info->type = PR_FILE_FILE;
+ }
+
+ info->size = findFileData.nFileSizeHigh;
+ info->size = (info->size << 32) + findFileData.nFileSizeLow;
+
+ _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime);
+
+ if (0 == findFileData.ftCreationTime.dwLowDateTime &&
+ 0 == findFileData.ftCreationTime.dwHighDateTime) {
+ info->creationTime = info->modifyTime;
+ } else {
+ _PR_FileTimeToPRTime(&findFileData.ftCreationTime,
+ &info->creationTime);
+ }
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info)
+{
+ PRFileInfo64 info64;
+ PRInt32 rv = _PR_MD_GETFILEINFO64(fn, &info64);
+ if (0 == rv)
+ {
+ info->type = info64.type;
+ info->size = (PRUint32) info64.size;
+ info->modifyTime = info64.modifyTime;
+ info->creationTime = info64.creationTime;
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info)
+{
+ int rv;
+
+ BY_HANDLE_FILE_INFORMATION hinfo;
+
+ rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo);
+ if (rv == FALSE) {
+ _PR_MD_MAP_FSTAT_ERROR(GetLastError());
+ return -1;
+ }
+
+ if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ info->type = PR_FILE_DIRECTORY;
+ else
+ info->type = PR_FILE_FILE;
+
+ info->size = hinfo.nFileSizeHigh;
+ info->size = (info->size << 32) + hinfo.nFileSizeLow;
+
+ _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) );
+ _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) );
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info)
+{
+ PRFileInfo64 info64;
+ int rv = _PR_MD_GETOPENFILEINFO64(fd, &info64);
+ if (0 == rv)
+ {
+ info->type = info64.type;
+ info->modifyTime = info64.modifyTime;
+ info->creationTime = info64.creationTime;
+ LL_L2I(info->size, info64.size);
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_RENAME(const char *from, const char *to)
+{
+ /* Does this work with dot-relative pathnames? */
+ if (MoveFile(from, to)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_RENAME_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+PRInt32
+_PR_MD_ACCESS(const char *name, PRIntn how)
+{
+PRInt32 rv;
+ switch (how) {
+ case PR_ACCESS_WRITE_OK:
+ rv = _access(name, 02);
+ break;
+ case PR_ACCESS_READ_OK:
+ rv = _access(name, 04);
+ break;
+ case PR_ACCESS_EXISTS:
+ return _access(name, 00);
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+ if (rv < 0)
+ _PR_MD_MAP_ACCESS_ERROR(errno);
+ return rv;
+}
+
+PRInt32
+_PR_MD_MKDIR(const char *name, PRIntn mode)
+{
+ /* XXXMB - how to translate the "mode"??? */
+ if (CreateDirectory(name, NULL)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_MKDIR_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+PRInt32
+_PR_MD_RMDIR(const char *name)
+{
+ if (RemoveDirectory(name)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_RMDIR_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+PRStatus
+_PR_MD_LOCKFILE(PRInt32 f)
+{
+ PRInt32 rv;
+
+ /*
+ * loop trying to LockFile(),
+ * pause for a few miliseconds when can't get the lock
+ * and try again
+ */
+ for( rv = FALSE; rv == FALSE; /* do nothing */ )
+ {
+
+ rv = LockFile( (HANDLE) f,
+ 0l, 0l,
+ 0x7fffffff, 0xffffffff );
+ if ( rv == FALSE )
+ {
+ DWORD rc = GetLastError();
+ Sleep( 50 ); // Sleep() a few milisecs and try again.
+ }
+ } /* end for() */
+ return PR_SUCCESS;
+} /* end _PR_MD_LOCKFILE() */
+
+PRStatus
+_PR_MD_TLOCKFILE(PRInt32 f)
+{
+ PRInt32 rv;
+
+ /*
+ * loop trying to LockFile(),
+ * pause for a few miliseconds when can't get the lock
+ * and try again
+ */
+ for( rv = FALSE; rv == FALSE; /* do nothing */ )
+ {
+
+ rv = LockFile( (HANDLE) f,
+ 0l, 0l,
+ 0x7fffffff, 0xffffffff );
+ if ( rv == FALSE )
+ {
+ DWORD rc = GetLastError();
+ Sleep( 50 ); // Sleep() a few milisecs and try again.
+ }
+ } /* end for() */
+ return PR_SUCCESS;
+} /* end _PR_MD_TLOCKFILE() */
+
+
+PRStatus
+_PR_MD_UNLOCKFILE(PRInt32 f)
+{
+ PRInt32 rv;
+
+ rv = UnlockFile( (HANDLE) f,
+ 0l, 0l,
+ 0x7fffffff, 0xffffffff );
+
+ if ( rv )
+ {
+ return PR_SUCCESS;
+ }
+ else
+ {
+ int err = GetLastError();
+ return PR_FAILURE;
+ }
+} /* end _PR_MD_UNLOCKFILE() */
+
diff --git a/pr/src/md/windows/w95sock.c b/pr/src/md/windows/w95sock.c
new file mode 100644
index 00000000..c5fbea4c
--- /dev/null
+++ b/pr/src/md/windows/w95sock.c
@@ -0,0 +1,614 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/* Win95 Sockets module
+ *
+ */
+
+#include "primpl.h"
+
+
+/* --- SOCKET IO --------------------------------------------------------- */
+
+
+PRInt32
+_PR_MD_SOCKET(int af, int type, int flags)
+{
+ SOCKET sock;
+ PRUint32 one = 1;
+ PRInt32 rv;
+ PRInt32 err;
+
+ sock = socket(af, type, flags);
+
+ if (sock == INVALID_SOCKET )
+ {
+ int rv = WSAGetLastError();
+ closesocket(sock);
+ _PR_MD_MAP_SOCKET_ERROR(rv);
+ return (PRInt32)INVALID_SOCKET;
+ }
+
+ /*
+ ** Make the socket Non-Blocking
+ */
+ rv = ioctlsocket( sock, FIONBIO, &one);
+ if ( rv != 0 )
+ {
+ err = WSAGetLastError();
+ return -1;
+ }
+
+ return (PRInt32)sock;
+}
+
+/*
+** _MD_CloseSocket() -- Close a socket
+**
+*/
+PRInt32
+_MD_CloseSocket(PRInt32 osfd)
+{
+ PRInt32 rv = SOCKET_ERROR;
+
+ rv = closesocket((SOCKET) osfd );
+ if (rv < 0)
+ _PR_MD_MAP_SOCKET_ERROR(WSAGetLastError());
+
+ return rv;
+}
+
+PRInt32
+_MD_SocketAvailable(PRFileDesc *fd)
+{
+ PRInt32 result;
+
+ if (ioctlsocket(fd->secret->md.osfd, FIONREAD, &result) < 0) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, WSAGetLastError());
+ return -1;
+ }
+ return result;
+}
+
+PRInt32
+_MD_Accept(PRFileDesc *fd, PRNetAddr *raddr, PRUint32 *rlen,
+ PRIntervalTime timeout )
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ fd_set rd;
+ struct timeval tv, *tvp;
+
+ FD_ZERO(&rd);
+ FD_SET((SOCKET)osfd, &rd);
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ {
+ while ((rv = accept(osfd, (struct sockaddr *) raddr, rlen)) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ if ((rv = select(osfd + 1, &rd, NULL, NULL,NULL)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ }
+ else {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ break;
+ }
+ }
+ return(rv);
+ }
+ else if (timeout == PR_INTERVAL_NO_WAIT)
+ {
+ if ((rv = accept(osfd, (struct sockaddr *) raddr, rlen)) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ else
+ {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ }
+ }
+ return(rv);
+ }
+ else
+ {
+retry:
+ if ((rv = accept(osfd, (struct sockaddr *) raddr, rlen)) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+
+ rv = select(osfd + 1, &rd, NULL, NULL, tvp);
+ if (rv > 0) {
+ goto retry;
+ }
+ else if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ } else {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ }
+ } else {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ }
+ }
+ }
+ return(rv);
+} /* end _MD_Accept() */
+
+
+PRInt32
+_PR_MD_CONNECT(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv;
+ int err, len;
+ fd_set wd, ex;
+ struct timeval tv, *tvp;
+
+ if ((rv = connect(osfd, (struct sockaddr *) addr, addrlen)) == -1)
+ {
+ err = WSAGetLastError();
+ if ((!fd->secret->nonblocking) && (err == WSAEWOULDBLOCK))
+ {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ tvp = NULL;
+ else
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ FD_ZERO(&ex);
+ FD_SET((SOCKET)osfd, &ex);
+ rv = select(osfd + 1, NULL, &wd, &ex, tvp);
+ if (rv > 0)
+ {
+ if (FD_ISSET((SOCKET)osfd, &ex))
+ {
+ Sleep(0);
+ len = sizeof(err);
+ if (getsockopt(osfd, SOL_SOCKET, SO_ERROR,
+ (char *) &err, &len) == SOCKET_ERROR)
+ {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
+ return -1;
+ }
+ if (err != 0)
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ else
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return -1;
+ }
+ if (FD_ISSET((SOCKET)osfd, &wd))
+ {
+ /* it's connected */
+ return 0;
+ }
+ }
+ else if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return(-1);
+ } else if (rv < 0)
+ {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ return(-1);
+ }
+ }
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen)
+{
+ PRInt32 rv;
+ int one = 1;
+
+ rv = bind(fd->secret->md.osfd, (const struct sockaddr *)&(addr->inet), addrlen);
+
+ if (rv == SOCKET_ERROR) {
+ _PR_MD_MAP_BIND_ERROR(WSAGetLastError());
+ return -1;
+ }
+
+ return 0;
+}
+
+
+PRInt32
+_PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ struct timeval tv, *tvp;
+ fd_set rd;
+
+ while ((rv = recv( osfd, buf, amount, 0)) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ FD_ZERO(&rd);
+ FD_SET((SOCKET)osfd, &rd);
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ {
+ tvp = NULL;
+ }
+ else
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ if ((rv = select(osfd + 1, &rd, NULL, NULL, tvp)) == -1)
+ {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ return -1;
+ }
+ else if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ }
+ else
+ {
+ _PR_MD_MAP_RECV_ERROR(err);
+ break;
+ }
+ } /* end while() */
+ return(rv);
+}
+
+PRInt32
+_PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ struct timeval tv, *tvp;
+ fd_set wd;
+ PRInt32 bytesSent = 0;
+
+ while(bytesSent < amount )
+ {
+ while ((rv = send( osfd, buf, amount, 0 )) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT )
+ {
+ tvp = NULL;
+ }
+ else
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ if ((rv = select( osfd + 1, NULL, &wd, NULL,tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ }
+ else {
+ _PR_MD_MAP_SEND_ERROR(err);
+ return -1;
+ }
+ }
+ bytesSent += rv;
+ if (fd->secret->nonblocking)
+ {
+ break;
+ }
+ if ((rv >= 0) && (bytesSent < amount ))
+ {
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT )
+ {
+ tvp = NULL;
+ }
+ else
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ if ((rv = select(osfd + 1, NULL, &wd, NULL,tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ }
+ }
+ return bytesSent;
+}
+
+PRInt32
+_PR_MD_SENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ struct timeval tv, *tvp;
+ fd_set wd;
+ PRInt32 bytesSent = 0;
+
+ while(bytesSent < amount)
+ {
+ while ((rv = sendto( osfd, buf, amount, 0, (struct sockaddr *) addr,
+ addrlen)) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT )
+ {
+ tvp = NULL;
+ }
+ else
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ if ((rv = select(osfd + 1, NULL, &wd, NULL, tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ }
+ else {
+ _PR_MD_MAP_SENDTO_ERROR(err);
+ return -1;
+ }
+ }
+ bytesSent += rv;
+ if (fd->secret->nonblocking)
+ {
+ break;
+ }
+ if ((rv >= 0) && (bytesSent < amount ))
+ {
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT )
+ {
+ tvp = NULL;
+ }
+ else
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ if ((rv = select( osfd + 1, NULL, &wd, NULL, tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ }
+ }
+ return bytesSent;
+}
+
+PRInt32
+_PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ struct timeval tv, *tvp;
+ fd_set rd;
+
+ while ((rv = recvfrom( osfd, buf, amount, 0, (struct sockaddr *) addr,
+ addrlen)) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ {
+ tvp = NULL;
+ }
+ else
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&rd);
+ FD_SET((SOCKET)osfd, &rd);
+ if ((rv = select(osfd + 1, &rd, NULL, NULL, tvp)) == -1)
+ {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ return -1;
+ } else if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ }
+ else
+ {
+ _PR_MD_MAP_RECVFROM_ERROR(err);
+ break;
+ }
+ }
+ return(rv);
+}
+
+PRInt32
+_PR_MD_WRITEV(PRFileDesc *fd, PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout)
+{
+ int index;
+ int sent = 0;
+ int rv;
+
+ for (index=0; index < iov_size; index++)
+ {
+ rv = _PR_MD_SEND(fd, iov[index].iov_base, iov[index].iov_len, 0, timeout);
+ if (rv > 0)
+ sent += rv;
+ if ( rv != iov[index].iov_len )
+ {
+ if (rv < 0)
+ {
+ if (fd->secret->nonblocking
+ && (PR_GetError() == PR_WOULD_BLOCK_ERROR)
+ && (sent > 0))
+ {
+ return sent;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ /* Only a nonblocking socket can have partial sends */
+ PR_ASSERT(fd->secret->nonblocking);
+ return sent;
+ }
+ }
+ return sent;
+}
+
+PRInt32
+_PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how)
+{
+PRInt32 rv;
+
+ rv = shutdown(fd->secret->md.osfd, how);
+ if (rv < 0)
+ _PR_MD_MAP_SHUTDOWN_ERROR(WSAGetLastError());
+ return rv;
+}
+
+PRStatus
+_PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
+{
+ PRInt32 rv;
+
+ rv = getsockname((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, len);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_GETSOCKNAME_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
+{
+ PRInt32 rv;
+
+ rv = getpeername((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, len);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_GETPEERNAME_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen)
+{
+ PRInt32 rv;
+
+ rv = getsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen)
+{
+ PRInt32 rv;
+
+ rv = setsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_SETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+void
+_MD_MakeNonblock(PRFileDesc *f)
+{
+ return; // do nothing!
+}
diff --git a/pr/src/md/windows/w95thred.c b/pr/src/md/windows/w95thred.c
new file mode 100644
index 00000000..0b333b85
--- /dev/null
+++ b/pr/src/md/windows/w95thred.c
@@ -0,0 +1,226 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+#include <process.h> /* for _beginthreadex() */
+
+extern void _PR_Win32InitTimeZone(void); /* defined in ntmisc.c */
+
+/* --- globals ------------------------------------------------ */
+__declspec(thread) struct PRThread *_pr_thread_last_run;
+__declspec(thread) struct PRThread *_pr_currentThread;
+__declspec(thread) struct _PRCPU *_pr_currentCPU;
+int _pr_intsOff = 0;
+_PRInterruptTable _pr_interruptTable[] = { { 0 } };
+
+void
+_PR_MD_EARLY_INIT()
+{
+ _PR_Win32InitTimeZone();
+}
+
+void _PR_MD_CLEANUP_BEFORE_EXIT(void)
+{
+ WSACleanup();
+}
+
+void
+_PR_MD_INIT_PRIMORDIAL_THREAD(PRThread *thread)
+{
+ /*
+ ** Warning:
+ ** --------
+ ** NSPR requires a real handle to every thread. GetCurrentThread()
+ ** returns a pseudo-handle which is not suitable for some thread
+ ** operations (ie. suspending). Therefore, get a real handle from
+ ** the pseudo handle via DuplicateHandle(...)
+ */
+ DuplicateHandle( GetCurrentProcess(), /* Process of source handle */
+ GetCurrentThread(), /* Pseudo Handle to dup */
+ GetCurrentProcess(), /* Process of handle */
+ &(thread->md.handle), /* resulting handle */
+ 0L, /* access flags */
+ FALSE, /* Inheritable */
+ DUPLICATE_SAME_ACCESS ); /* Options */
+}
+
+PRStatus
+_PR_MD_INIT_THREAD(PRThread *thread)
+{
+ if (thread->flags & _PR_PRIMORDIAL)
+ _PR_MD_INIT_PRIMORDIAL_THREAD(thread);
+
+ /* Create the blocking IO semaphore */
+ thread->md.blocked_sema = CreateSemaphore(NULL, 0, 1, NULL);
+ if (thread->md.blocked_sema == NULL)
+ return PR_FAILURE;
+ else
+ return PR_SUCCESS;
+}
+
+PRStatus
+_PR_MD_CREATE_THREAD(PRThread *thread,
+ void (*start)(void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+
+ thread->md.handle = (HANDLE) _beginthreadex(
+ NULL,
+ thread->stack->stackSize,
+ (unsigned (__stdcall *)(void *))start,
+ (void *)thread,
+ CREATE_SUSPENDED,
+ &(thread->id));
+ if(!thread->md.handle) {
+ return PR_FAILURE;
+ }
+
+ thread->md.id = thread->id;
+ _PR_MD_SET_PRIORITY(&(thread->md), priority);
+
+ /* Activate the thread */
+ if ( ResumeThread( thread->md.handle ) != -1)
+ return PR_SUCCESS;
+
+ return PR_FAILURE;
+}
+
+void
+_PR_MD_YIELD(void)
+{
+ /* Can NT really yield at all? */
+ Sleep(0);
+}
+
+void
+_PR_MD_SET_PRIORITY(_MDThread *thread, PRThreadPriority newPri)
+{
+ int nativePri;
+ BOOL rv;
+
+ if (newPri < PR_PRIORITY_FIRST) {
+ newPri = PR_PRIORITY_FIRST;
+ } else if (newPri > PR_PRIORITY_LAST) {
+ newPri = PR_PRIORITY_LAST;
+ }
+ switch (newPri) {
+ case PR_PRIORITY_LOW:
+ nativePri = THREAD_PRIORITY_BELOW_NORMAL;
+ break;
+ case PR_PRIORITY_NORMAL:
+ nativePri = THREAD_PRIORITY_NORMAL;
+ break;
+ case PR_PRIORITY_HIGH:
+ nativePri = THREAD_PRIORITY_ABOVE_NORMAL;
+ break;
+ case PR_PRIORITY_URGENT:
+ nativePri = THREAD_PRIORITY_HIGHEST;
+ }
+ rv = SetThreadPriority(thread->handle, nativePri);
+ PR_ASSERT(rv);
+ if (!rv) {
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("PR_SetThreadPriority: can't set thread priority\n"));
+ }
+ return;
+}
+
+void
+_PR_MD_CLEAN_THREAD(PRThread *thread)
+{
+ if (thread->md.blocked_sema) {
+ CloseHandle(thread->md.blocked_sema);
+ thread->md.blocked_sema = 0;
+ }
+
+ if (thread->md.handle) {
+ CloseHandle(thread->md.handle);
+ thread->md.handle = 0;
+ }
+}
+
+void
+_PR_MD_EXIT_THREAD(PRThread *thread)
+{
+ _PR_MD_CLEAN_THREAD(thread);
+ _PR_MD_SET_CURRENT_THREAD(NULL);
+}
+
+
+void
+_PR_MD_EXIT(PRIntn status)
+{
+ _exit(status);
+}
+
+PRInt32 _PR_MD_SETTHREADAFFINITYMASK(PRThread *thread, PRUint32 mask )
+{
+ int rv;
+
+ rv = SetThreadAffinityMask(thread->md.handle, mask);
+
+ return rv?0:-1;
+}
+
+PRInt32 _PR_MD_GETTHREADAFFINITYMASK(PRThread *thread, PRUint32 *mask)
+{
+ PRInt32 rv, system_mask;
+
+ rv = GetProcessAffinityMask(GetCurrentProcess(), mask, &system_mask);
+
+ return rv?0:-1;
+}
+
+void
+_PR_MD_SUSPEND_CPU(_PRCPU *cpu)
+{
+ _PR_MD_SUSPEND_THREAD(cpu->thread);
+}
+
+void
+_PR_MD_RESUME_CPU(_PRCPU *cpu)
+{
+ _PR_MD_RESUME_THREAD(cpu->thread);
+}
+
+void
+_PR_MD_SUSPEND_THREAD(PRThread *thread)
+{
+ if (_PR_IS_NATIVE_THREAD(thread)) {
+ DWORD previousSuspendCount;
+ /* XXXMB - SuspendThread() is not a blocking call; how do we
+ * know when the thread is *REALLY* suspended?
+ */
+ previousSuspendCount = SuspendThread(thread->md.handle);
+ PR_ASSERT(previousSuspendCount == 0);
+ }
+}
+
+void
+_PR_MD_RESUME_THREAD(PRThread *thread)
+{
+ if (_PR_IS_NATIVE_THREAD(thread)) {
+ DWORD previousSuspendCount;
+ previousSuspendCount = ResumeThread(thread->md.handle);
+ PR_ASSERT(previousSuspendCount == 1);
+ }
+}
+
diff --git a/pr/src/md/windows/win32_errors.c b/pr/src/md/windows/win32_errors.c
new file mode 100644
index 00000000..df271678
--- /dev/null
+++ b/pr/src/md/windows/win32_errors.c
@@ -0,0 +1,1104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "prerror.h"
+#include <errno.h>
+
+void _MD_win32_map_opendir_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_INVALID_NAME:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ERROR_DISK_CORRUPT:
+ case ERROR_DISK_OPERATION_FAILED:
+ case ERROR_FILE_CORRUPT:
+ case ERROR_NOT_READY:
+ case ERROR_OPEN_FAILED:
+ case ERROR_OPEN_FILES:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_closedir_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_FILE_INVALID:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_readdir_error(PRInt32 err)
+{
+
+ switch (err) {
+ case ERROR_NO_MORE_FILES:
+ PR_SetError(PR_NO_MORE_FILES_ERROR, err);
+ break;
+ case ERROR_FILE_INVALID:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_DISK_CORRUPT:
+ case ERROR_DISK_OPERATION_FAILED:
+ case ERROR_FILE_CORRUPT:
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_delete_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+/* The error code for stat() is in errno. */
+void _MD_win32_map_stat_error(PRInt32 err)
+{
+ switch (err) {
+ case ENOENT:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ }
+}
+
+void _MD_win32_map_fstat_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_FILE_INVALID:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_DISK_CORRUPT:
+ case ERROR_DISK_OPERATION_FAILED:
+ case ERROR_FILE_CORRUPT:
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_rename_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_INVALID_NAME:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ERROR_DISK_CORRUPT:
+ case ERROR_DISK_OPERATION_FAILED:
+ case ERROR_FILE_CORRUPT:
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_EXISTS:
+ PR_SetError(PR_FILE_EXISTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+/* The error code for access() is in errno. */
+void _MD_win32_map_access_error(PRInt32 err)
+{
+ switch (err) {
+ case ENOENT:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ }
+}
+
+void _MD_win32_map_mkdir_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_EXISTS:
+ PR_SetError(PR_FILE_EXISTS_ERROR, err);
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_INVALID_NAME:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ERROR_DISK_CORRUPT:
+ case ERROR_DISK_OPERATION_FAILED:
+ case ERROR_FILE_CORRUPT:
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_DISK_FULL:
+ case ERROR_HANDLE_DISK_FULL:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_rmdir_error(PRInt32 err)
+{
+
+ switch (err) {
+ case ERROR_FILE_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_INVALID_NAME:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ERROR_DISK_CORRUPT:
+ case ERROR_DISK_OPERATION_FAILED:
+ case ERROR_FILE_CORRUPT:
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_read_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_FILE_INVALID:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_DISK_CORRUPT:
+ case ERROR_DISK_OPERATION_FAILED:
+ case ERROR_FILE_CORRUPT:
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ case ERROR_INVALID_USER_BUFFER:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_QUOTA:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_transmitfile_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_FILE_INVALID:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_DISK_CORRUPT:
+ case ERROR_DISK_OPERATION_FAILED:
+ case ERROR_FILE_CORRUPT:
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ case ERROR_INVALID_USER_BUFFER:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_QUOTA:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_write_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_FILE_INVALID:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ case ERROR_STACK_OVERFLOW:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_DISK_CORRUPT:
+ case ERROR_DISK_OPERATION_FAILED:
+ case ERROR_FILE_CORRUPT:
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ case ERROR_INVALID_USER_BUFFER:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_QUOTA:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, err);
+ break;
+ case ERROR_DISK_FULL:
+ case ERROR_HANDLE_DISK_FULL:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEMSGSIZE:
+ case WSAEINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case WSAENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case WSAECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case WSAEISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_lseek_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_FILE_INVALID:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_SEEK_ON_DEVICE:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_fsync_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_FILE_INVALID:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_DISK_FULL:
+ case ERROR_HANDLE_DISK_FULL:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_close_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_FILE_INVALID:
+ case ERROR_INVALID_HANDLE:
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_DISK_OPERATION_FAILED:
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_socket_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEPROTONOSUPPORT:
+ PR_SetError(PR_PROTOCOL_NOT_SUPPORTED_ERROR, err);
+ break;
+ case WSAEACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ case WSAENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_recv_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_recvfrom_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_send_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEMSGSIZE:
+ case WSAEINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case WSAENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case WSAECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case WSAEISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_sendto_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEMSGSIZE:
+ case WSAEINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case WSAENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case WSAECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case WSAEISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_accept_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEOPNOTSUPP:
+ PR_SetError(PR_NOT_TCP_SOCKET_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAEMFILE:
+ PR_SetError(PR_PROC_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case WSAENOBUFS:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_acceptex_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_FILE_INVALID:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ case ERROR_INVALID_USER_BUFFER:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_connect_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEWOULDBLOCK:
+ PR_SetError(PR_IN_PROGRESS_ERROR, err);
+ break;
+ case WSAEALREADY:
+ case WSAEINVAL:
+ PR_SetError(PR_ALREADY_INITIATED_ERROR, err);
+ break;
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAEADDRNOTAVAIL:
+ PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEAFNOSUPPORT:
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err);
+ break;
+ case WSAEISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+ case WSAETIMEDOUT:
+ PR_SetError(PR_IO_TIMEOUT_ERROR, err);
+ break;
+ case WSAECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case WSAENETUNREACH:
+ PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, err);
+ break;
+ case WSAEADDRINUSE:
+ PR_SetError(PR_ADDRESS_IN_USE_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_bind_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAEADDRNOTAVAIL:
+ PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err);
+ break;
+ case WSAEADDRINUSE:
+ PR_SetError(PR_ADDRESS_IN_USE_ERROR, err);
+ break;
+ case WSAEACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case WSAEINVAL:
+ PR_SetError(PR_SOCKET_ADDRESS_IS_BOUND_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_listen_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEOPNOTSUPP:
+ PR_SetError(PR_NOT_TCP_SOCKET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_shutdown_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAENOTCONN:
+ PR_SetError(PR_NOT_CONNECTED_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_getsockname_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_getpeername_error(PRInt32 err)
+{
+
+ switch (err) {
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAENOTCONN:
+ PR_SetError(PR_NOT_CONNECTED_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_getsockopt_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAENOPROTOOPT:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAEINVAL:
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_setsockopt_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAENOPROTOOPT:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAEINVAL:
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_open_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_EXISTS:
+ PR_SetError(PR_FILE_EXISTS_ERROR, err);
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_INVALID_NAME:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ERROR_DISK_CORRUPT:
+ case ERROR_DISK_OPERATION_FAILED:
+ case ERROR_FILE_CORRUPT:
+ case ERROR_NOT_READY:
+ case ERROR_OPEN_FAILED:
+ case ERROR_OPEN_FILES:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_DISK_FULL:
+ case ERROR_HANDLE_DISK_FULL:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_gethostname_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAENETDOWN:
+ case WSAEINPROGRESS:
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_select_error(PRInt32 err)
+{
+ PRErrorCode prerror;
+
+ switch (err) {
+ /*
+ * Win32 select() only works on sockets. So in this
+ * context, WSAENOTSOCK is equivalent to EBADF on Unix.
+ */
+ case WSAENOTSOCK:
+ prerror = PR_BAD_DESCRIPTOR_ERROR;
+ break;
+ case WSAEINVAL:
+ prerror = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ case WSAEFAULT:
+ prerror = PR_ACCESS_FAULT_ERROR;
+ break;
+ default:
+ prerror = PR_UNKNOWN_ERROR;
+ }
+ PR_SetError(prerror, err);
+}
+
+void _MD_win32_map_lockf_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_FILE_INVALID:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ case ERROR_STACK_OVERFLOW:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_QUOTA:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
diff --git a/pr/src/memory/Makefile b/pr/src/memory/Makefile
new file mode 100644
index 00000000..017c59e2
--- /dev/null
+++ b/pr/src/memory/Makefile
@@ -0,0 +1,42 @@
+#
+# The contents of this file are subject to the Netscape Public License
+# Version 1.0 (the "NPL"); you may not use this file except in
+# compliance with the NPL. You may obtain a copy of the NPL at
+# http://www.mozilla.org/NPL/
+#
+# Software distributed under the NPL is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+# for the specific language governing rights and limitations under the
+# NPL.
+#
+# The Initial Developer of this code under the NPL is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1998 Netscape Communications Corporation. All Rights
+# Reserved.
+#
+
+#! gmake
+
+MOD_DEPTH = ../../..
+
+include $(MOD_DEPTH)/config/config.mk
+
+# Disable optimization of the nspr on SunOS4.1.3
+ifeq ($(OS_ARCH),SunOS)
+ifeq ($(OS_RELEASE),4.1.3_U1)
+OPTIMIZER =
+endif
+endif
+
+CSRCS = prseg.c
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(DIST)/include/private -I$(DIST)/include
+
+include $(MOD_DEPTH)/config/rules.mk
+
+export:: $(TARGETS)
+
+install:: export
+
diff --git a/pr/src/memory/prseg.c b/pr/src/memory/prseg.c
new file mode 100644
index 00000000..6a95bd53
--- /dev/null
+++ b/pr/src/memory/prseg.c
@@ -0,0 +1,192 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+void _PR_InitSegs(void)
+{
+ _PR_MD_INIT_SEGS();
+}
+
+/*
+** Allocate a memory segment. The size value is rounded up to the native
+** system page size and a page aligned portion of memory is returned.
+** This memory is not part of the malloc heap. If "vaddr" is not NULL
+** then PR tries to allocate the segment at the desired virtual address.
+*/
+PR_IMPLEMENT(PRSegment*) PR_NewSegment(PRUint32 size, void *vaddr)
+{
+ PRSegment *seg;
+
+ /* calloc the data structure for the segment */
+ seg = PR_NEWZAP(PRSegment);
+
+ if (seg) {
+ size = ((size + _pr_pageSize - 1) >> _pr_pageShift) << _pr_pageShift;
+ /*
+ ** Now, allocate the actual segment memory (or map under some OS)
+ ** The OS specific code decides from where or how to allocate memory.
+ */
+ if (_PR_MD_ALLOC_SEGMENT(seg, size, vaddr) != PR_SUCCESS) {
+ PR_DELETE(seg);
+ return NULL;
+ }
+ }
+
+ return seg;
+}
+
+/*
+** Free a memory segment.
+*/
+PR_IMPLEMENT(void) PR_DestroySegment(PRSegment *seg)
+{
+ _PR_MD_FREE_SEGMENT(seg);
+ PR_DELETE(seg);
+}
+
+/* XXX Fix the following to make machine-independent */
+#ifdef XP_UNIX
+#include <sys/mman.h>
+#endif
+
+#ifndef PROT_NONE
+#define PROT_NONE 0
+#endif
+
+extern PRInt32 _pr_zero_fd;
+
+/*
+** Attempt to grow/shrink the given segment.
+*/
+PR_IMPLEMENT(PRUint32) PR_GrowSegment(PRSegment *seg, PRInt32 delta)
+{
+ char *oldend, *newend;
+#if 0
+#ifdef XP_UNIX
+ int prot;
+ void *rv = (void *) -1;
+#endif
+#endif /* 0 */
+
+ if (!(seg->flags & _PR_SEG_VM)) {
+ return 0;
+ }
+
+ oldend = (char*)seg->vaddr + seg->size;
+ if (delta > 0) {
+#if 0
+
+ /*
+ * CANNOT use MAP_FIXED because it will replace any existing mappings
+ */
+ /* Growing the segment */
+ delta = ((delta + _pr_pageSize - 1) >> _pr_pageShift) << _pr_pageShift;
+ newend = oldend + delta;
+#ifdef XP_UNIX
+ prot = PROT_READ|PROT_WRITE;
+ rv = mmap(oldend, delta, prot,
+#ifdef OSF1
+ /* XXX need to pick a vaddr to use */
+ MAP_PRIVATE|MAP_FIXED,
+#else
+ MAP_SHARED|MAP_FIXED,
+#endif
+ _pr_zero_fd, 0);
+#endif /* XP_UNIX */
+ if (rv == (void*)-1) {
+ /* Can't extend the heap */
+ return 0;
+ }
+ seg->size = seg->size + delta;
+#endif /* 0 */
+ } else if (delta < 0) {
+ /* Shrinking the segment */
+ delta = -delta;
+ delta = (delta >> _pr_pageShift) << _pr_pageShift; /* trunc */
+ if ((PRUint32)delta >= seg->size) {
+ /* XXX what to do? */
+ return 0;
+ }
+ newend = oldend - delta;
+ if (newend < oldend) {
+#ifdef XP_UNIX
+ (void) munmap(oldend, newend - oldend);
+ seg->size = seg->size + delta;
+#endif
+ }
+ }
+ return delta;
+}
+
+/*
+** Change the mapping on a segment.
+** "how" == PR_SEGMENT_NONE: the segment becomes unmapped
+** "how" == PR_SEGMENT_RDONLY: the segment becomes mapped and readable
+** "how" == PR_SEGMENT_RDWR: the segment becomes mapped read/write
+**
+** If a segment can be read then it is also possible to execute code in
+** it.
+*/
+PR_IMPLEMENT(void) PR_MapSegment(PRSegment *seg, PRSegmentAccess how)
+{
+ if (seg->access == how) return;
+ seg->access = how;
+
+#ifdef XP_UNIX
+ if (seg->flags & _PR_SEG_VM) {
+ int prot;
+ switch (how) {
+ case PR_SEGMENT_NONE:
+ prot = PROT_NONE;
+ break;
+ case PR_SEGMENT_RDONLY:
+ prot = PROT_READ;
+ break;
+ case PR_SEGMENT_RDWR:
+ prot = PROT_READ|PROT_WRITE;
+ break;
+ }
+ (void) mprotect(seg->vaddr, seg->size, prot);
+ }
+#endif
+}
+
+/*
+** Return the size of the segment
+*/
+PR_IMPLEMENT(size_t) PR_GetSegmentSize(PRSegment *seg)
+{
+ return seg->size;
+}
+
+/*
+** Return the virtual address of the segment
+*/
+PR_IMPLEMENT(void*) PR_GetSegmentVaddr(PRSegment *seg)
+{
+ return seg->vaddr;
+}
+
+/*
+** Return a segments current access rights
+*/
+PR_IMPLEMENT(PRSegmentAccess) PR_GetSegmentAccess(PRSegment *seg)
+{
+ return seg->access;
+}
diff --git a/pr/src/misc/Makefile b/pr/src/misc/Makefile
new file mode 100644
index 00000000..015fc259
--- /dev/null
+++ b/pr/src/misc/Makefile
@@ -0,0 +1,64 @@
+#
+# The contents of this file are subject to the Netscape Public License
+# Version 1.0 (the "NPL"); you may not use this file except in
+# compliance with the NPL. You may obtain a copy of the NPL at
+# http://www.mozilla.org/NPL/
+#
+# Software distributed under the NPL is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+# for the specific language governing rights and limitations under the
+# NPL.
+#
+# The Initial Developer of this code under the NPL is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1998 Netscape Communications Corporation. All Rights
+# Reserved.
+#
+
+#! gmake
+
+MOD_DEPTH = ../../..
+
+include $(MOD_DEPTH)/config/config.mk
+
+# Disable optimization of the nspr on SunOS4.1.3
+ifeq ($(OS_ARCH),SunOS)
+ifeq ($(OS_RELEASE),4.1.3_U1)
+OPTIMIZER =
+endif
+endif
+
+CSRCS = \
+ pralarm.c \
+ pratom.c \
+ prdtoa.c \
+ prenv.c \
+ prerror.c \
+ prinit.c \
+ prinrval.c \
+ prlog2.c \
+ prlong.c \
+ prnetdb.c \
+ prsystem.c \
+ prtime.c \
+ prthinfo.c \
+ $(NULL)
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(DIST)/include/private -I$(DIST)/include
+
+include $(MOD_DEPTH)/config/rules.mk
+
+# An AIX Optimization bug causes PR_dtoa() to produce wrong result.
+# This suppresses optimization for this single compilation unit.
+ifeq ($(OS_ARCH), AIX)
+$(OBJDIR)/prdtoa.o:
+ @$(MAKE_OBJDIR)
+ $(CC) -o $@ -c $(filter-out -O, $(CFLAGS)) prdtoa.c
+endif
+
+export:: $(TARGETS)
+
+install:: export
+
diff --git a/pr/src/misc/pralarm.c b/pr/src/misc/pralarm.c
new file mode 100644
index 00000000..8f518943
--- /dev/null
+++ b/pr/src/misc/pralarm.c
@@ -0,0 +1,263 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+/**********************************************************************/
+/******************************* PRALARM ******************************/
+/**********************************************************************/
+
+#ifdef XP_MAC
+#include "pralarm.h"
+#else
+#include "obsolete/pralarm.h"
+#endif
+
+struct PRAlarmID { /* typedef'd in pralarm.h */
+ PRCList list; /* circular list linkage */
+ PRAlarm *alarm; /* back pointer to owning alarm */
+ PRPeriodicAlarmFn function; /* function to call for notify */
+ void *clientData; /* opaque client context */
+ PRIntervalTime period; /* the client defined period */
+ PRUint32 rate; /* rate of notification */
+
+ PRUint32 accumulator; /* keeps track of # notifies */
+ PRIntervalTime epoch; /* when timer was started */
+ PRIntervalTime nextNotify; /* when we'll next do our thing */
+ PRIntervalTime lastNotify; /* when we last did our thing */
+};
+
+typedef enum {alarm_active, alarm_inactive} _AlarmState;
+
+struct PRAlarm { /* typedef'd in pralarm.h */
+ PRCList timers; /* base of alarm ids list */
+ PRLock *lock; /* lock used to protect data */
+ PRCondVar *cond; /* condition that used to wait */
+ PRThread *notifier; /* thread to deliver notifies */
+ PRAlarmID *current; /* current alarm being served */
+ _AlarmState state; /* used to delete the alarm */
+};
+
+static PRAlarmID *pr_getNextAlarm(PRAlarm *alarm, PRAlarmID *id)
+{
+/*
+ * Puts 'id' back into the sorted list iff it's not NULL.
+ * Removes the first element from the list and returns it (or NULL).
+ * List is "assumed" to be short.
+ *
+ * NB: Caller is providing locking
+ */
+ PRCList *timer;
+ PRAlarmID *result = id;
+ PRIntervalTime now = PR_IntervalNow();
+
+ if (!PR_CLIST_IS_EMPTY(&alarm->timers))
+ {
+ if (id != NULL) /* have to put this id back in */
+ {
+ PRIntervalTime idDelta = now - id->nextNotify;
+ timer = &alarm->timers;
+ do
+ {
+ result = (PRAlarmID*)timer;
+ if ((PRIntervalTime)(now - result->nextNotify) > idDelta)
+ {
+ PR_INSERT_BEFORE(&id->list, &alarm->timers);
+ break;
+ }
+ timer = timer->next;
+ } while (timer != alarm->timers.next);
+ }
+ result = (PRAlarmID*)(timer = PR_LIST_HEAD(&alarm->timers));
+ PR_REMOVE_LINK(timer); /* remove it from the list */
+ }
+
+ return result;
+} /* pr_getNextAlarm */
+
+static PRIntervalTime pr_PredictNextNotifyTime(PRAlarmID *id)
+{
+ PRIntervalTime delta;
+ PRFloat64 baseRate = (PRFloat64)id->period / (PRFloat64)id->rate;
+ PRFloat64 offsetFromEpoch = (PRFloat64)id->accumulator * baseRate;
+
+ id->accumulator += 1; /* every call advances to next period */
+ id->lastNotify = id->nextNotify; /* just keeping track of things */
+ id->nextNotify = (PRIntervalTime)(offsetFromEpoch + 0.5);
+
+ delta = id->nextNotify - id->nextNotify;
+ return delta;
+} /* pr_PredictNextNotifyTime */
+
+static void PR_CALLBACK pr_alarmNotifier(void *arg)
+{
+ /*
+ * This is the root of the notifier thread. There is one such thread
+ * for each PRAlarm. It may service an arbitrary (though assumed to be
+ * small) number of alarms using the same thread and structure. It
+ * continues to run until the alarm is destroyed.
+ */
+ PRAlarmID *id = NULL;
+ PRAlarm *alarm = (PRAlarm*)arg;
+ enum {notify, abort, scan} why = scan;
+
+ while (why != abort)
+ {
+ PRIntervalTime pause;
+
+ PR_Lock(alarm->lock);
+ while (why == scan)
+ {
+ alarm->current = NULL; /* reset current id */
+ if (alarm->state == alarm_inactive) why = abort; /* we're toast */
+ else if (why == scan) /* the dominant case */
+ {
+ id = pr_getNextAlarm(alarm, id); /* even if it's the same */
+ if (id == NULL) /* there are no alarms set */
+ (void)PR_WaitCondVar(alarm->cond, PR_INTERVAL_NO_TIMEOUT);
+ else
+ {
+ pause = id->nextNotify - (PR_IntervalNow() - id->epoch);
+ if ((PRInt32)pause <= 0) /* is this one's time up? */
+ {
+ why = notify; /* set up to do our thing */
+ alarm->current = id; /* id we're about to schedule */
+ }
+ else
+ (void)PR_WaitCondVar(alarm->cond, pause); /* dally */
+ }
+ }
+ }
+ PR_Unlock(alarm->lock);
+
+ if (why == notify)
+ {
+ (void)pr_PredictNextNotifyTime(id);
+ if (!id->function(id, id->clientData, ~pause))
+ {
+ /*
+ * Notified function decided not to continue. Free
+ * the alarm id to make sure it doesn't get back on
+ * the list.
+ */
+ PR_DELETE(id); /* free notifier object */
+ id = NULL; /* so it doesn't get back into the list */
+ }
+ why = scan; /* so we can cycle through the loop again */
+ }
+ }
+
+} /* pr_alarm_notifier */
+
+PR_IMPLEMENT(PRAlarm*) PR_CreateAlarm()
+{
+ PRAlarm *alarm = PR_NEWZAP(PRAlarm);
+ if (alarm != NULL)
+ {
+ if ((alarm->lock = PR_NewLock()) == NULL) goto done;
+ if ((alarm->cond = PR_NewCondVar(alarm->lock)) == NULL) goto done;
+ alarm->state = alarm_active;
+ PR_INIT_CLIST(&alarm->timers);
+ alarm->notifier = PR_CreateThread(
+ PR_USER_THREAD, pr_alarmNotifier, alarm,
+ PR_GetThreadPriority(PR_GetCurrentThread()),
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ if (alarm->notifier == NULL) goto done;
+ }
+ return alarm;
+
+done:
+ if (alarm->cond != NULL) PR_DestroyCondVar(alarm->cond);
+ if (alarm->lock != NULL) PR_DestroyLock(alarm->lock);
+ PR_DELETE(alarm);
+ return NULL;
+} /* CreateAlarm */
+
+PR_IMPLEMENT(PRStatus) PR_DestroyAlarm(PRAlarm *alarm)
+{
+ PRStatus rv;
+
+ PR_Lock(alarm->lock);
+ alarm->state = alarm_inactive;
+ rv = PR_NotifyCondVar(alarm->cond);
+ PR_Unlock(alarm->lock);
+
+ if (rv == PR_SUCCESS)
+ rv = PR_JoinThread(alarm->notifier);
+ if (rv == PR_SUCCESS)
+ {
+ PR_DestroyCondVar(alarm->cond);
+ PR_DestroyLock(alarm->lock);
+ PR_DELETE(alarm);
+ }
+ return rv;
+} /* PR_DestroyAlarm */
+
+PR_IMPLEMENT(PRAlarmID*) PR_SetAlarm(
+ PRAlarm *alarm, PRIntervalTime period, PRUint32 rate,
+ PRPeriodicAlarmFn function, void *clientData)
+{
+ /*
+ * Create a new periodic alarm an existing current structure.
+ * Set up the context and compute the first notify time (immediate).
+ * Link the new ID into the head of the list (since it's notifying
+ * immediately).
+ */
+
+ PRAlarmID *id = PR_NEWZAP(PRAlarmID);
+
+ if (!id)
+ return NULL;
+
+ id->alarm = alarm;
+ PR_INIT_CLIST(&id->list);
+ id->function = function;
+ id->clientData = clientData;
+ id->period = period;
+ id->rate = rate;
+ id->epoch = id->nextNotify = PR_IntervalNow();
+ (void)pr_PredictNextNotifyTime(id);
+
+ PR_Lock(alarm->lock);
+ PR_INSERT_BEFORE(&id->list, &alarm->timers);
+ PR_NotifyCondVar(alarm->cond);
+ PR_Unlock(alarm->lock);
+
+ return id;
+} /* PR_SetAlarm */
+
+PR_IMPLEMENT(PRStatus) PR_ResetAlarm(
+ PRAlarmID *id, PRIntervalTime period, PRUint32 rate)
+{
+ /*
+ * Can only be called from within the notify routine. Doesn't
+ * need locking because it can only be called from within the
+ * notify routine.
+ */
+ if (id != id->alarm->current)
+ return PR_FAILURE;
+ id->period = period;
+ id->rate = rate;
+ id->accumulator = 1;
+ id->epoch = PR_IntervalNow();
+ (void)pr_PredictNextNotifyTime(id);
+ return PR_SUCCESS;
+} /* PR_ResetAlarm */
+
+
+
diff --git a/pr/src/misc/pratom.c b/pr/src/misc/pratom.c
new file mode 100644
index 00000000..c7eccae0
--- /dev/null
+++ b/pr/src/misc/pratom.c
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+** PR Atomic operations
+*/
+
+
+#include "pratom.h"
+#include "primpl.h"
+
+/*
+ * The following is a fallback implementation that emulates
+ * emulates atomic operations for platforms without atomic
+ * operations. If a platform has atomic operations,
+ * it should define the macro _PR_HAVE_ATOMIC_OPS, and
+ * the following will not be compiled in.
+ */
+
+#ifndef _PR_HAVE_ATOMIC_OPS
+
+/*
+ * We use a single lock for all the emulated atomic operations.
+ * The lock contention should be acceptable.
+ */
+
+static PRLock *monitor = NULL;
+void _PR_MD_INIT_ATOMIC()
+{
+ if (monitor == NULL) {
+ monitor = PR_NewLock();
+ }
+}
+
+PR_IMPLEMENT(PRInt32)
+_PR_MD_ATOMIC_INCREMENT(PRInt32 *val)
+{
+ PRInt32 rv;
+ PR_Lock(monitor);
+ rv = ++(*val);
+ PR_Unlock(monitor);
+ return rv;
+}
+
+PR_IMPLEMENT(PRInt32)
+_PR_MD_ATOMIC_DECREMENT(PRInt32 *val)
+{
+ PRInt32 rv;
+ PR_Lock(monitor);
+ rv = --(*val);
+ PR_Unlock(monitor);
+ return rv;
+}
+
+PR_IMPLEMENT(PRInt32)
+_PR_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
+{
+ PRInt32 rv;
+ PR_Lock(monitor);
+ rv = *val;
+ *val = newval;
+ PR_Unlock(monitor);
+ return rv;
+}
+
+#endif /* !_PR_HAVE_ATOMIC_OPS */
+
+void _PR_InitAtomic(void)
+{
+ _PR_MD_INIT_ATOMIC();
+}
+
+PR_IMPLEMENT(PRInt32)
+PR_AtomicIncrement(PRInt32 *val)
+{
+ return _PR_MD_ATOMIC_INCREMENT(val);
+}
+
+PR_IMPLEMENT(PRInt32)
+PR_AtomicDecrement(PRInt32 *val)
+{
+ return _PR_MD_ATOMIC_DECREMENT(val);
+}
+
+PR_IMPLEMENT(PRInt32)
+PR_AtomicSet(PRInt32 *val, PRInt32 newval)
+{
+ return _PR_MD_ATOMIC_SET(val, newval);
+}
+
diff --git a/pr/src/misc/prdtoa.c b/pr/src/misc/prdtoa.c
new file mode 100644
index 00000000..a9253617
--- /dev/null
+++ b/pr/src/misc/prdtoa.c
@@ -0,0 +1,3189 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+#include "primpl.h"
+
+/****************************************************************
+ *
+ * The author of this software is David M. Gay.
+ *
+ * Copyright (c) 1991 by AT&T.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR AT&T MAKES ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ ***************************************************************/
+
+/* Please send bug reports to
+ David M. Gay
+ AT&T Bell Laboratories, Room 2C-463
+ 600 Mountain Avenue
+ Murray Hill, NJ 07974-2070
+ U.S.A.
+ dmg@research.att.com or research!dmg
+ */
+
+/* strtod for IEEE-, VAX-, and IBM-arithmetic machines.
+ *
+ * This strtod returns a nearest machine number to the input decimal
+ * string (or sets errno to ERANGE). With IEEE arithmetic, ties are
+ * broken by the IEEE round-even rule. Otherwise ties are broken by
+ * biased rounding (add half and chop).
+ *
+ * Inspired loosely by William D. Clinger's paper "How to Read Floating
+ * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101].
+ *
+ * Modifications:
+ *
+ * 1. We only require IEEE, IBM, or VAX double-precision
+ * arithmetic (not IEEE double-extended).
+ * 2. We get by with floating-point arithmetic in a case that
+ * Clinger missed -- when we're computing d * 10^n
+ * for a small integer d and the integer n is not too
+ * much larger than 22 (the maximum integer k for which
+ * we can represent 10^k exactly), we may be able to
+ * compute (d*10^k) * 10^(e-k) with just one roundoff.
+ * 3. Rather than a bit-at-a-time adjustment of the binary
+ * result in the hard case, we use floating-point
+ * arithmetic to determine the adjustment to within
+ * one bit; only in really hard cases do we need to
+ * compute a second residual.
+ * 4. Because of 3., we don't need a large table of powers of 10
+ * for ten-to-e (just some small tables, e.g. of 10^k
+ * for 0 <= k <= 22).
+ */
+
+/*
+ * #define IEEE_8087 for IEEE-arithmetic machines where the least
+ * significant byte has the lowest address.
+ * #define IEEE_MC68k for IEEE-arithmetic machines where the most
+ * significant byte has the lowest address.
+ * #define Long int on machines with 32-bit ints and 64-bit longs.
+ * #define Sudden_Underflow for IEEE-format machines without gradual
+ * underflow (i.e., that flush to zero on underflow).
+ * #define IBM for IBM mainframe-style floating-point arithmetic.
+ * #define VAX for VAX-style floating-point arithmetic.
+ * #define Unsigned_Shifts if >> does treats its left operand as unsigned.
+ * #define No_leftright to omit left-right logic in fast floating-point
+ * computation of PR_dtoa.
+ * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3.
+ * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines
+ * that use extended-precision instructions to compute rounded
+ * products and quotients) with IBM.
+ * #define ROUND_BIASED for IEEE-format with biased rounding.
+ * #define Inaccurate_Divide for IEEE-format with correctly rounded
+ * products but inaccurate quotients, e.g., for Intel i860.
+ * #define Just_16 to store 16 bits per 32-bit Long when doing high-precision
+ * integer arithmetic. Whether this speeds things up or slows things
+ * down depends on the machine and the number being converted.
+ * #define KR_headers for old-style C function headers.
+ * #define Bad_float_h if your system lacks a float.h or if it does not
+ * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP,
+ * FLT_RADIX, FLT_ROUNDS, and DBL_MAX.
+ * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n)
+ * if memory is available and otherwise does something you deem
+ * appropriate. If MALLOC is undefined, malloc will be invoked
+ * directly -- and assumed always to succeed.
+ */
+#if defined(IS_LITTLE_ENDIAN)
+#define IEEE_8087
+#else
+#define IEEE_MC68k
+#endif
+
+#ifndef Long
+#if PR_BYTES_PER_LONG == 4
+#define Long long
+#elif PR_BYTES_PER_INT == 4
+#define Long int
+#else
+#error "No suitable type for Long"
+#endif
+#endif
+
+#ifdef DEBUG_DTOA
+#include "stdio.h"
+#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);}
+#else
+#define Bug(x)
+#endif
+
+#include "stdlib.h"
+#include "string.h"
+
+#ifdef MALLOC
+extern void *MALLOC(size_t);
+#else
+#define MALLOC PR_MALLOC
+#endif
+
+#include "errno.h"
+#ifdef Bad_float_h
+#undef __STDC__
+#ifdef IEEE_MC68k
+#define IEEE_ARITHMETIC
+#endif
+#ifdef IEEE_8087
+#define IEEE_ARITHMETIC
+#endif
+
+#ifdef IEEE_ARITHMETIC
+#define DBL_DIG 15
+#define DBL_MAX_10_EXP 308
+#define DBL_MAX_EXP 1024
+#define FLT_RADIX 2
+#define FLT_ROUNDS 1
+#define DBL_MAX 1.7976931348623157e+308
+#endif
+
+#ifdef IBM
+#define DBL_DIG 16
+#define DBL_MAX_10_EXP 75
+#define DBL_MAX_EXP 63
+#define FLT_RADIX 16
+#define FLT_ROUNDS 0
+#define DBL_MAX 7.2370055773322621e+75
+#endif
+
+#ifdef VAX
+#define DBL_DIG 16
+#define DBL_MAX_10_EXP 38
+#define DBL_MAX_EXP 127
+#define FLT_RADIX 2
+#define FLT_ROUNDS 1
+#define DBL_MAX 1.7014118346046923e+38
+#endif
+
+#ifndef LONG_MAX
+#define LONG_MAX 2147483647
+#endif
+#else
+#include "float.h"
+#endif
+#ifndef __MATH_H__
+#include "math.h"
+#endif
+
+#ifndef CONST
+#define CONST const
+#endif
+
+#ifdef Unsigned_Shifts
+#define Sign_Extend(a,b) if (b < 0) a |= 0xffff0000;
+#else
+#define Sign_Extend(a,b) /*no-op*/
+#endif
+
+#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1
+Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined.
+#endif
+
+#ifdef IEEE_8087
+#define word0(x) ((unsigned Long *)&x)[1]
+#define word1(x) ((unsigned Long *)&x)[0]
+#else
+#define word0(x) ((unsigned Long *)&x)[0]
+#define word1(x) ((unsigned Long *)&x)[1]
+#endif
+
+/* The following definition of Storeinc is appropriate for MIPS processors.
+ * An alternative that might be better on some machines is
+ * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff)
+ */
+#if defined(IEEE_8087) + defined(VAX)
+#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \
+((unsigned short *)a)[0] = (unsigned short)c, a++)
+#else
+#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \
+((unsigned short *)a)[1] = (unsigned short)c, a++)
+#endif
+
+/* #define P DBL_MANT_DIG */
+/* Ten_pmax = floor(P*log(2)/log(5)) */
+/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */
+/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */
+/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */
+
+#if defined(IEEE_8087) + defined(IEEE_MC68k)
+#define Exp_shift 20
+#define Exp_shift1 20
+#define Exp_msk1 0x100000
+#define Exp_msk11 0x100000
+#define Exp_mask 0x7ff00000
+#define P 53
+#define Bias 1023
+#define IEEE_Arith
+#define Emin (-1022)
+#define Exp_1 0x3ff00000
+#define Exp_11 0x3ff00000
+#define Ebits 11
+#define Frac_mask 0xfffff
+#define Frac_mask1 0xfffff
+#define Ten_pmax 22
+#define Bletch 0x10
+#define Bndry_mask 0xfffff
+#define Bndry_mask1 0xfffff
+#define LSB 1
+#define Sign_bit 0x80000000
+#define Log2P 1
+#define Tiny0 0
+#define Tiny1 1
+#define Quick_max 14
+#define Int_max 14
+#define Infinite(x) (word0(x) == 0x7ff00000) /* sufficient test for here */
+#else
+#undef Sudden_Underflow
+#define Sudden_Underflow
+#ifdef IBM
+#define Exp_shift 24
+#define Exp_shift1 24
+#define Exp_msk1 0x1000000
+#define Exp_msk11 0x1000000
+#define Exp_mask 0x7f000000
+#define P 14
+#define Bias 65
+#define Exp_1 0x41000000
+#define Exp_11 0x41000000
+#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */
+#define Frac_mask 0xffffff
+#define Frac_mask1 0xffffff
+#define Bletch 4
+#define Ten_pmax 22
+#define Bndry_mask 0xefffff
+#define Bndry_mask1 0xffffff
+#define LSB 1
+#define Sign_bit 0x80000000
+#define Log2P 4
+#define Tiny0 0x100000
+#define Tiny1 0
+#define Quick_max 14
+#define Int_max 15
+#else /* VAX */
+#define Exp_shift 23
+#define Exp_shift1 7
+#define Exp_msk1 0x80
+#define Exp_msk11 0x800000
+#define Exp_mask 0x7f80
+#define P 56
+#define Bias 129
+#define Exp_1 0x40800000
+#define Exp_11 0x4080
+#define Ebits 8
+#define Frac_mask 0x7fffff
+#define Frac_mask1 0xffff007f
+#define Ten_pmax 24
+#define Bletch 2
+#define Bndry_mask 0xffff007f
+#define Bndry_mask1 0xffff007f
+#define LSB 0x10000
+#define Sign_bit 0x8000
+#define Log2P 1
+#define Tiny0 0x80
+#define Tiny1 0
+#define Quick_max 15
+#define Int_max 15
+#endif
+#endif
+
+#ifndef IEEE_Arith
+#define ROUND_BIASED
+#endif
+
+#ifdef RND_PRODQUOT
+#define rounded_product(a,b) a = rnd_prod(a, b)
+#define rounded_quotient(a,b) a = rnd_quot(a, b)
+extern double rnd_prod(double, double), rnd_quot(double, double);
+#else
+#define rounded_product(a,b) a *= b
+#define rounded_quotient(a,b) a /= b
+#endif
+
+#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1))
+#define Big1 0xffffffff
+
+#ifndef Just_16
+/* When Pack_32 is not defined, we store 16 bits per 32-bit Long.
+ * This makes some inner loops simpler and sometimes saves work
+ * during multiplications, but it often seems to make things slightly
+ * slower. Hence the default is now to store 32 bits per Long.
+ */
+#ifndef Pack_32
+#define Pack_32
+#endif
+#endif
+
+#define Kmax 15
+
+/*
+ * Note: if you ever change struct Bigint, make sure that the
+ * definition of the Bcopy(x,y) macro is still correct.
+ */
+struct Bigint {
+ struct Bigint *next;
+ PRInt32 k, maxwds, sign, wds;
+ unsigned Long x[1];
+};
+
+typedef struct Bigint Bigint;
+
+static Bigint *freelist[Kmax+1];
+
+static PRLock *freelist_lock;
+
+static Bigint *Balloc(PRInt32 k)
+{
+ PRInt32 x;
+ Bigint *rv;
+
+ PR_Lock(freelist_lock);
+ if ((rv = freelist[k]) != NULL) {
+ freelist[k] = rv->next;
+ }
+ PR_Unlock(freelist_lock);
+ if (rv == NULL) {
+ x = 1 << k;
+ rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(Long));
+ rv->k = k;
+ rv->maxwds = x;
+ }
+ rv->sign = rv->wds = 0;
+ return rv;
+}
+
+static void Bfree (Bigint *v)
+{
+ if (v) {
+ PR_Lock(freelist_lock);
+ v->next = freelist[v->k];
+ freelist[v->k] = v;
+ PR_Unlock(freelist_lock);
+ }
+}
+
+/*
+ * The definition of the Bcopy macro is highly dependent on the
+ * ordering of members in struct Bigint.
+ */
+#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \
+ y->wds*sizeof(Long) + 2*sizeof(PRInt32))
+
+static Bigint *multadd(Bigint *b, PRInt32 m, PRInt32 a) /* multiply by m and add a */
+{
+ PRInt32 i, wds;
+ unsigned Long *x, y;
+#ifdef Pack_32
+ unsigned Long xi, z;
+#endif
+ Bigint *b1;
+
+ wds = b->wds;
+ x = b->x;
+ i = 0;
+ do {
+#ifdef Pack_32
+ xi = *x;
+ y = (xi & 0xffff) * m + a;
+ z = (xi >> 16) * m + (y >> 16);
+ a = (PRInt32)(z >> 16);
+ *x++ = (z << 16) + (y & 0xffff);
+#else
+ y = *x * m + a;
+ a = (PRInt32)(y >> 16);
+ *x++ = y & 0xffff;
+#endif
+ }
+ while(++i < wds);
+ if (a) {
+ if (wds >= b->maxwds) {
+ b1 = Balloc(b->k+1);
+ Bcopy(b1, b);
+ Bfree(b);
+ b = b1;
+ }
+ b->x[wds++] = a;
+ b->wds = wds;
+ }
+ return b;
+}
+
+static Bigint *s2b(CONST char *s, PRInt32 nd0, PRInt32 nd, unsigned Long y9)
+{
+ Bigint *b;
+ PRInt32 i, k;
+ Long x, y;
+
+ x = (nd + 8) / 9;
+ for(k = 0, y = 1; x > y; y <<= 1, k++) ;
+#ifdef Pack_32
+ b = Balloc(k);
+ b->x[0] = y9;
+ b->wds = 1;
+#else
+ b = Balloc(k+1);
+ b->x[0] = y9 & 0xffff;
+ b->wds = (b->x[1] = y9 >> 16) ? 2 : 1;
+#endif
+
+ i = 9;
+ if (9 < nd0) {
+ s += 9;
+ do b = multadd(b, 10, *s++ - '0');
+ while(++i < nd0);
+ s++;
+ }
+ else
+ s += 10;
+ for(; i < nd; i++)
+ b = multadd(b, 10, *s++ - '0');
+ return b;
+}
+
+static PRInt32 hi0bits(register unsigned Long x)
+{
+ register PRInt32 k = 0;
+
+ if (!(x & 0xffff0000)) {
+ k = 16;
+ x <<= 16;
+ }
+ if (!(x & 0xff000000)) {
+ k += 8;
+ x <<= 8;
+ }
+ if (!(x & 0xf0000000)) {
+ k += 4;
+ x <<= 4;
+ }
+ if (!(x & 0xc0000000)) {
+ k += 2;
+ x <<= 2;
+ }
+ if (!(x & 0x80000000)) {
+ k++;
+ if (!(x & 0x40000000))
+ return 32;
+ }
+ return k;
+}
+
+static PRInt32 lo0bits(unsigned Long *y)
+{
+ register PRInt32 k;
+ register unsigned Long x = *y;
+
+ if (x & 7) {
+ if (x & 1)
+ return 0;
+ if (x & 2) {
+ *y = x >> 1;
+ return 1;
+ }
+ *y = x >> 2;
+ return 2;
+ }
+ k = 0;
+ if (!(x & 0xffff)) {
+ k = 16;
+ x >>= 16;
+ }
+ if (!(x & 0xff)) {
+ k += 8;
+ x >>= 8;
+ }
+ if (!(x & 0xf)) {
+ k += 4;
+ x >>= 4;
+ }
+ if (!(x & 0x3)) {
+ k += 2;
+ x >>= 2;
+ }
+ if (!(x & 1)) {
+ k++;
+ x >>= 1;
+ if (!x & 1)
+ return 32;
+ }
+ *y = x;
+ return k;
+}
+
+static Bigint *i2b(PRInt32 i)
+{
+ Bigint *b;
+
+ b = Balloc(1);
+ b->x[0] = i;
+ b->wds = 1;
+ return b;
+}
+
+static Bigint *mult(CONST Bigint *a, CONST Bigint *b)
+{
+ CONST Bigint *t;
+ Bigint *c;
+ PRInt32 k, wa, wb, wc;
+ unsigned Long carry, y, z;
+ unsigned Long *xc, *xc0, *xce;
+ CONST unsigned Long *x, *xa, *xae, *xb, *xbe;
+#ifdef Pack_32
+ unsigned Long z2;
+#endif
+
+ if (a->wds < b->wds) {
+ t = a;
+ a = b;
+ b = t;
+ }
+ k = a->k;
+ wa = a->wds;
+ wb = b->wds;
+ wc = wa + wb;
+ if (wc > a->maxwds)
+ k++;
+ c = Balloc(k);
+ for(xc = c->x, xce = xc + wc; xc < xce; xc++)
+ *xc = 0;
+ xa = a->x;
+ xae = xa + wa;
+ xb = b->x;
+ xbe = xb + wb;
+ xc0 = c->x;
+#ifdef Pack_32
+ for(; xb < xbe; xb++, xc0++) {
+ if ((y = *xb & 0xffff) != 0) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ do {
+ z = (*x & 0xffff) * y + (*xc & 0xffff) + carry;
+ carry = z >> 16;
+ z2 = (*x++ >> 16) * y + (*xc >> 16) + carry;
+ carry = z2 >> 16;
+ Storeinc(xc, z2, z);
+ }
+ while(x < xae);
+ *xc = carry;
+ }
+ if ((y = *xb >> 16) != 0) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ z2 = *xc;
+ do {
+ z = (*x & 0xffff) * y + (*xc >> 16) + carry;
+ carry = z >> 16;
+ Storeinc(xc, z, z2);
+ z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry;
+ carry = z2 >> 16;
+ }
+ while(x < xae);
+ *xc = z2;
+ }
+ }
+#else
+ for(; xb < xbe; xc0++) {
+ if (y = *xb++) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ do {
+ z = *x++ * y + *xc + carry;
+ carry = z >> 16;
+ *xc++ = z & 0xffff;
+ }
+ while(x < xae);
+ *xc = carry;
+ }
+ }
+#endif
+ for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ;
+ c->wds = wc;
+ return c;
+}
+
+/*
+ * 'p5s' points to a linked list of Bigints that are powers of 5.
+ * This list grows on demand, and it can only grow: it won't change
+ * in any other way. So if we read 'p5s' or the 'next' field of
+ * some Bigint on the list, and it is not NULL, we know it won't
+ * change to NULL or some other value. Only when the value of
+ * 'p5s' or 'next' is NULL do we need to acquire the lock and add
+ * a new Bigint to the list.
+ */
+
+static Bigint *p5s;
+
+static PRLock *p5s_lock;
+
+static Bigint *pow5mult(Bigint *b, PRInt32 k)
+{
+ Bigint *b1, *p5, *p51;
+ PRInt32 i;
+ static CONST PRInt32 p05[3] = { 5, 25, 125 };
+
+ if ((i = k & 3) != 0)
+ b = multadd(b, p05[i-1], 0);
+
+ if (!(k >>= 2))
+ return b;
+ if (!(p5 = p5s)) {
+ /*
+ * We take great care to not call i2b() and Bfree()
+ * while holding the lock.
+ */
+ Bigint *wasted_effort = NULL;
+ p5 = i2b(625);
+ /* lock and check again */
+ PR_Lock(p5s_lock);
+ if (!p5s) {
+ /* first time */
+ p5s = p5;
+ p5->next = 0;
+ } else {
+ /* some other thread just beat us */
+ wasted_effort = p5;
+ p5 = p5s;
+ }
+ PR_Unlock(p5s_lock);
+ if (wasted_effort) {
+ Bfree(wasted_effort);
+ }
+ }
+ for(;;) {
+ if (k & 1) {
+ b1 = mult(b, p5);
+ Bfree(b);
+ b = b1;
+ }
+ if (!(k >>= 1))
+ break;
+ if (!(p51 = p5->next)) {
+ Bigint *wasted_effort = NULL;
+ p51 = mult(p5, p5);
+ PR_Lock(p5s_lock);
+ if (!p5->next) {
+ p5->next = p51;
+ p51->next = 0;
+ } else {
+ wasted_effort = p51;
+ p51 = p5->next;
+ }
+ PR_Unlock(p5s_lock);
+ if (wasted_effort) {
+ Bfree(wasted_effort);
+ }
+ }
+ p5 = p51;
+ }
+ return b;
+}
+
+static Bigint *lshift(Bigint *b, PRInt32 k)
+{
+ PRInt32 i, k1, n, n1;
+ Bigint *b1;
+ unsigned Long *x, *x1, *xe, z;
+
+#ifdef Pack_32
+ n = k >> 5;
+#else
+ n = k >> 4;
+#endif
+ k1 = b->k;
+ n1 = n + b->wds + 1;
+ for(i = b->maxwds; n1 > i; i <<= 1)
+ k1++;
+ b1 = Balloc(k1);
+ x1 = b1->x;
+ for(i = 0; i < n; i++)
+ *x1++ = 0;
+ x = b->x;
+ xe = x + b->wds;
+#ifdef Pack_32
+ if (k &= 0x1f) {
+ k1 = 32 - k;
+ z = 0;
+ do {
+ *x1++ = *x << k | z;
+ z = *x++ >> k1;
+ }
+ while(x < xe);
+ if ((*x1 = z) != 0)
+ ++n1;
+ }
+#else
+ if (k &= 0xf) {
+ k1 = 16 - k;
+ z = 0;
+ do {
+ *x1++ = *x << k & 0xffff | z;
+ z = *x++ >> k1;
+ }
+ while(x < xe);
+ if ((*x1 = z) != 0)
+ ++n1;
+ }
+#endif
+ else do
+ *x1++ = *x++;
+ while(x < xe);
+ b1->wds = n1 - 1;
+ Bfree(b);
+ return b1;
+}
+
+static PRInt32 cmp(Bigint *a, Bigint *b)
+{
+ unsigned Long *xa, *xa0, *xb, *xb0;
+ PRInt32 i, j;
+
+ i = a->wds;
+ j = b->wds;
+#ifdef DEBUG_DTOA
+ if ((i > 1 && !a->x[i-1]))
+ Bug("cmp called with a->x[a->wds-1] == 0");
+ if ((j > 1 && !b->x[j-1]))
+ Bug("cmp called with b->x[b->wds-1] == 0");
+#endif
+ if (i -= j)
+ return i;
+ xa0 = a->x;
+ xa = xa0 + j;
+ xb0 = b->x;
+ xb = xb0 + j;
+ for(;;) {
+ if (*--xa != *--xb)
+ return *xa < *xb ? -1 : 1;
+ if (xa <= xa0)
+ break;
+ }
+ return 0;
+}
+
+static Bigint *diff(Bigint *a, Bigint *b)
+{
+ Bigint *c;
+ PRInt32 i, wa, wb;
+ Long borrow, y; /* We need signed shifts here. */
+ unsigned Long *xa, *xae, *xb, *xbe, *xc;
+#ifdef Pack_32
+ Long z;
+#endif
+
+ i = cmp(a,b);
+ if (!i) {
+ c = Balloc(0);
+ c->wds = 1;
+ c->x[0] = 0;
+ return c;
+ }
+ if (i < 0) {
+ c = a;
+ a = b;
+ b = c;
+ i = 1;
+ }
+ else
+ i = 0;
+ c = Balloc(a->k);
+ c->sign = i;
+ wa = a->wds;
+ xa = a->x;
+ xae = xa + wa;
+ wb = b->wds;
+ xb = b->x;
+ xbe = xb + wb;
+ xc = c->x;
+ borrow = 0;
+#ifdef Pack_32
+ do {
+ y = (long)((*xa & 0xffff) - (*xb & 0xffff) + borrow);
+ borrow = y >> 16;
+ Sign_Extend(borrow, y);
+ z = (long)((*xa++ >> 16) - (*xb++ >> 16) + borrow);
+ borrow = z >> 16;
+ Sign_Extend(borrow, z);
+ Storeinc(xc, z, y);
+ }
+ while(xb < xbe);
+ while(xa < xae) {
+ y = (long)((*xa & 0xffff) + borrow);
+ borrow = y >> 16;
+ Sign_Extend(borrow, y);
+ z = (long)((*xa++ >> 16) + borrow);
+ borrow = z >> 16;
+ Sign_Extend(borrow, z);
+ Storeinc(xc, z, y);
+ }
+#else
+ do {
+ y = *xa++ - *xb++ + borrow;
+ borrow = y >> 16;
+ Sign_Extend(borrow, y);
+ *xc++ = y & 0xffff;
+ }
+ while(xb < xbe);
+ while(xa < xae) {
+ y = *xa++ + borrow;
+ borrow = y >> 16;
+ Sign_Extend(borrow, y);
+ *xc++ = y & 0xffff;
+ }
+#endif
+ while(!*--xc)
+ wa--;
+ c->wds = wa;
+ return c;
+}
+
+static double ulp(double x)
+{
+ register Long L;
+ double a;
+
+ L = (long)((word0(x) & Exp_mask) - (P-1)*Exp_msk1);
+#ifndef Sudden_Underflow
+ if (L > 0) {
+#endif
+#ifdef IBM
+ L |= Exp_msk1 >> 4;
+#endif
+ word0(a) = L;
+ word1(a) = 0;
+#ifndef Sudden_Underflow
+ }
+ else {
+ L = -L >> Exp_shift;
+ if (L < Exp_shift) {
+ word0(a) = 0x80000 >> L;
+ word1(a) = 0;
+ }
+ else {
+ word0(a) = 0;
+ L -= Exp_shift;
+ word1(a) = L >= 31 ? 1 : 1 << (31 - L);
+ }
+ }
+#endif
+ return a;
+}
+
+static double
+b2d
+#ifdef KR_headers
+(a, e) Bigint *a; PRInt32 *e;
+#else
+(Bigint *a, PRInt32 *e)
+#endif
+{
+ unsigned Long *xa, *xa0, w, y, z;
+ PRInt32 k;
+ double d;
+#ifdef VAX
+ unsigned Long d0, d1;
+#else
+#define d0 word0(d)
+#define d1 word1(d)
+#endif
+
+ xa0 = a->x;
+ xa = xa0 + a->wds;
+ y = *--xa;
+#ifdef DEBUG_DTOA
+ if (!y) Bug("zero y in b2d");
+#endif
+ k = hi0bits(y);
+ *e = 32 - k;
+#ifdef Pack_32
+ if (k < Ebits) {
+ d0 = Exp_1 | y >> (Ebits - k);
+ w = xa > xa0 ? *--xa : 0;
+ d1 = y << (32 - Ebits + k) | w >> (Ebits - k);
+ goto ret_d;
+ }
+ z = xa > xa0 ? *--xa : 0;
+ if (k -= Ebits) {
+ d0 = Exp_1 | y << k | z >> (32 - k);
+ y = xa > xa0 ? *--xa : 0;
+ d1 = z << k | y >> (32 - k);
+ }
+ else {
+ d0 = Exp_1 | y;
+ d1 = z;
+ }
+#else
+ if (k < Ebits + 16) {
+ z = xa > xa0 ? *--xa : 0;
+ d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k;
+ w = xa > xa0 ? *--xa : 0;
+ y = xa > xa0 ? *--xa : 0;
+ d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k;
+ goto ret_d;
+ }
+ z = xa > xa0 ? *--xa : 0;
+ w = xa > xa0 ? *--xa : 0;
+ k -= Ebits + 16;
+ d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k;
+ y = xa > xa0 ? *--xa : 0;
+ d1 = w << k + 16 | y << k;
+#endif
+ret_d:
+#ifdef VAX
+ word0(d) = d0 >> 16 | d0 << 16;
+ word1(d) = d1 >> 16 | d1 << 16;
+#else
+#undef d0
+#undef d1
+#endif
+ return d;
+}
+
+static Bigint *
+d2b
+#ifdef KR_headers
+(d, e, bits) double d; PRInt32 *e, *bits;
+#else
+(double d, PRInt32 *e, PRInt32 *bits)
+#endif
+{
+ Bigint *b;
+ PRInt32 de, i, k;
+ unsigned Long *x, y, z;
+#ifdef VAX
+ unsigned Long d0, d1;
+ d0 = word0(d) >> 16 | word0(d) << 16;
+ d1 = word1(d) >> 16 | word1(d) << 16;
+#else
+#define d0 word0(d)
+#define d1 word1(d)
+#endif
+
+#ifdef Pack_32
+ b = Balloc(1);
+#else
+ b = Balloc(2);
+#endif
+ x = b->x;
+
+ z = d0 & Frac_mask;
+ d0 &= 0x7fffffff; /* clear sign bit, which we ignore */
+#ifdef Sudden_Underflow
+ de = (PRInt32)(d0 >> Exp_shift);
+#ifndef IBM
+ z |= Exp_msk11;
+#endif
+#else
+ if ((de = (PRInt32)(d0 >> Exp_shift)) != 0)
+ z |= Exp_msk1;
+#endif
+#ifdef Pack_32
+ if ((y = d1) != 0) {
+ if ((k = lo0bits(&y)) != 0) {
+ x[0] = y | z << (32 - k);
+ z >>= k;
+ }
+ else
+ x[0] = y;
+ i = b->wds = (x[1] = z) ? 2 : 1;
+ }
+ else {
+#ifdef DEBUG_DTOA
+ if (!z)
+ Bug("Zero passed to d2b");
+#endif
+ k = lo0bits(&z);
+ x[0] = z;
+ i = b->wds = 1;
+ k += 32;
+ }
+#else
+ if ((y = d1) != 0) {
+ if ((k = lo0bits(&y)) != 0)
+ if (k >= 16) {
+ x[0] = y | z << 32 - k & 0xffff;
+ x[1] = z >> k - 16 & 0xffff;
+ x[2] = z >> k;
+ i = 2;
+ }
+ else {
+ x[0] = y & 0xffff;
+ x[1] = y >> 16 | z << 16 - k & 0xffff;
+ x[2] = z >> k & 0xffff;
+ x[3] = z >> k+16;
+ i = 3;
+ }
+ else {
+ x[0] = y & 0xffff;
+ x[1] = y >> 16;
+ x[2] = z & 0xffff;
+ x[3] = z >> 16;
+ i = 3;
+ }
+ }
+ else {
+#ifdef DEBUG_DTOA
+ if (!z)
+ Bug("Zero passed to d2b");
+#endif
+ k = lo0bits(&z);
+ if (k >= 16) {
+ x[0] = z;
+ i = 0;
+ }
+ else {
+ x[0] = z & 0xffff;
+ x[1] = z >> 16;
+ i = 1;
+ }
+ k += 32;
+ }
+ while(!x[i])
+ --i;
+ b->wds = i + 1;
+#endif
+#ifndef Sudden_Underflow
+ if (de) {
+#endif
+#ifdef IBM
+ *e = (de - Bias - (P-1) << 2) + k;
+ *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask);
+#else
+ *e = de - Bias - (P-1) + k;
+ *bits = P - k;
+#endif
+#ifndef Sudden_Underflow
+ }
+ else {
+ *e = de - Bias - (P-1) + 1 + k;
+#ifdef Pack_32
+ *bits = 32*i - hi0bits(x[i-1]);
+#else
+ *bits = (i+2)*16 - hi0bits(x[i]);
+#endif
+ }
+#endif
+ return b;
+}
+#undef d0
+#undef d1
+
+static double
+ratio
+#ifdef KR_headers
+(a, b) Bigint *a, *b;
+#else
+(Bigint *a, Bigint *b)
+#endif
+{
+ double da, db;
+ PRInt32 k, ka, kb;
+
+ da = b2d(a, &ka);
+ db = b2d(b, &kb);
+#ifdef Pack_32
+ k = ka - kb + 32*(a->wds - b->wds);
+#else
+ k = ka - kb + 16*(a->wds - b->wds);
+#endif
+#ifdef IBM
+ if (k > 0) {
+ word0(da) += (k >> 2)*Exp_msk1;
+ if (k &= 3)
+ da *= 1 << k;
+ }
+ else {
+ k = -k;
+ word0(db) += (k >> 2)*Exp_msk1;
+ if (k &= 3)
+ db *= 1 << k;
+ }
+#else
+ if (k > 0)
+ word0(da) += k*Exp_msk1;
+ else {
+ k = -k;
+ word0(db) += k*Exp_msk1;
+ }
+#endif
+ return da / db;
+}
+
+static CONST double
+tens[] = {
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+ 1e20, 1e21, 1e22
+#ifdef VAX
+ , 1e23, 1e24
+#endif
+};
+
+static CONST double
+#ifdef IEEE_Arith
+bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 };
+static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, 1e-256 };
+#define n_bigtens 5
+#else
+#ifdef IBM
+bigtens[] = { 1e16, 1e32, 1e64 };
+static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64 };
+#define n_bigtens 3
+#else
+bigtens[] = { 1e16, 1e32 };
+static CONST double tinytens[] = { 1e-16, 1e-32 };
+#define n_bigtens 2
+#endif
+#endif
+
+void _PR_InitDtoa(void)
+{
+ freelist_lock = PR_NewLock();
+ p5s_lock = PR_NewLock();
+}
+
+#if defined(HAVE_WATCOM_BUG_1)
+PRFloat64 __pascal __loadds __export
+#else
+PR_IMPLEMENT(PRFloat64)
+#endif
+PR_strtod(CONST char *s00, char **se)
+{
+ PRInt32 bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign,
+ e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign;
+ CONST char *s, *s0, *s1;
+ PRFloat64 aadj, aadj1, adj, rv, rv0;
+ Long L;
+ unsigned Long y, z;
+ Bigint *bb, *bb1, *bd, *bd0, *bs, *delta;
+
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ sign = nz0 = nz = 0;
+ rv = 0.;
+ for(s = s00;;s++) switch(*s) {
+ case '-':
+ sign = 1;
+ /* no break */
+ case '+':
+ if (*++s)
+ goto break2;
+ /* no break */
+ case 0:
+ s = s00;
+ goto ret;
+ case '\t':
+ case '\n':
+ case '\v':
+ case '\f':
+ case '\r':
+ case ' ':
+ continue;
+ default:
+ goto break2;
+ }
+break2:
+ if (*s == '0') {
+ nz0 = 1;
+ while(*++s == '0') ;
+ if (!*s)
+ goto ret;
+ }
+ s0 = s;
+ y = z = 0;
+ for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++)
+ if (nd < 9)
+ y = 10*y + c - '0';
+ else if (nd < 16)
+ z = 10*z + c - '0';
+ nd0 = nd;
+ if (c == '.') {
+ c = *++s;
+ if (!nd) {
+ for(; c == '0'; c = *++s)
+ nz++;
+ if (c > '0' && c <= '9') {
+ s0 = s;
+ nf += nz;
+ nz = 0;
+ goto have_dig;
+ }
+ goto dig_done;
+ }
+ for(; c >= '0' && c <= '9'; c = *++s) {
+ have_dig:
+ nz++;
+ if (c -= '0') {
+ nf += nz;
+ for(i = 1; i < nz; i++)
+ if (nd++ < 9)
+ y *= 10;
+ else if (nd <= DBL_DIG + 1)
+ z *= 10;
+ if (nd++ < 9)
+ y = 10*y + c;
+ else if (nd <= DBL_DIG + 1)
+ z = 10*z + c;
+ nz = 0;
+ }
+ }
+ }
+dig_done:
+ e = 0;
+ if (c == 'e' || c == 'E') {
+ if (!nd && !nz && !nz0) {
+ s = s00;
+ goto ret;
+ }
+ s00 = s;
+ esign = 0;
+ switch(c = *++s) {
+ case '-':
+ esign = 1;
+ case '+':
+ c = *++s;
+ }
+ if (c >= '0' && c <= '9') {
+ while(c == '0')
+ c = *++s;
+ if (c > '0' && c <= '9') {
+ L = c - '0';
+ s1 = s;
+ while((c = *++s) >= '0' && c <= '9')
+ L = 10*L + c - '0';
+ if (s - s1 > 8 || L > 19999)
+ /* Avoid confusion from exponents
+ * so large that e might overflow.
+ */
+ e = 19999; /* safe for 16 bit ints */
+ else
+ e = (PRInt32)L;
+ if (esign)
+ e = -e;
+ }
+ else
+ e = 0;
+ }
+ else
+ s = s00;
+ }
+ if (!nd) {
+ if (!nz && !nz0)
+ s = s00;
+ goto ret;
+ }
+ e1 = e -= nf;
+
+ /* Now we have nd0 digits, starting at s0, followed by a
+ * decimal point, followed by nd-nd0 digits. The number we're
+ * after is the integer represented by those digits times
+ * 10**e */
+
+ if (!nd0)
+ nd0 = nd;
+ k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1;
+ rv = y;
+ if (k > 9)
+ rv = tens[k - 9] * rv + z;
+ bd0 = 0;
+ if (nd <= DBL_DIG
+#ifndef RND_PRODQUOT
+ && FLT_ROUNDS == 1
+#endif
+ ) {
+ if (!e)
+ goto ret;
+ if (e > 0) {
+ if (e <= Ten_pmax) {
+#ifdef VAX
+ goto vax_ovfl_check;
+#else
+ /* rv = */ rounded_product(rv, tens[e]);
+ goto ret;
+#endif
+ }
+ i = DBL_DIG - nd;
+ if (e <= Ten_pmax + i) {
+ /* A fancier test would sometimes let us do
+ * this for larger i values.
+ */
+ e -= i;
+ rv *= tens[i];
+#ifdef VAX
+ /* VAX exponent range is so narrow we must
+ * worry about overflow here...
+ */
+ vax_ovfl_check:
+ word0(rv) -= P*Exp_msk1;
+ /* rv = */ rounded_product(rv, tens[e]);
+ if ((word0(rv) & Exp_mask)
+ > Exp_msk1*(DBL_MAX_EXP+Bias-1-P))
+ goto ovfl;
+ word0(rv) += P*Exp_msk1;
+#else
+ /* rv = */ rounded_product(rv, tens[e]);
+#endif
+ goto ret;
+ }
+ }
+#ifndef Inaccurate_Divide
+ else if (e >= -Ten_pmax) {
+ /* rv = */ rounded_quotient(rv, tens[-e]);
+ goto ret;
+ }
+#endif
+ }
+ e1 += nd - k;
+
+ /* Get starting approximation = rv * 10**e1 */
+
+ if (e1 > 0) {
+ if ((i = e1 & 15) != 0)
+ rv *= tens[i];
+ if (e1 &= ~15) {
+ if (e1 > DBL_MAX_10_EXP) {
+ ovfl:
+ errno = ERANGE;
+#ifdef __STDC__
+ rv = HUGE_VAL;
+#else
+ /* Can't trust HUGE_VAL */
+#ifdef IEEE_Arith
+ word0(rv) = Exp_mask;
+ word1(rv) = 0;
+#else
+ word0(rv) = Big0;
+ word1(rv) = Big1;
+#endif
+#endif
+ if (bd0)
+ goto retfree;
+ goto ret;
+ }
+ if (e1 >>= 4) {
+ for(j = 0; e1 > 1; j++, e1 >>= 1)
+ if (e1 & 1)
+ rv *= bigtens[j];
+ /* The last multiplication could overflow. */
+ word0(rv) -= P*Exp_msk1;
+ rv *= bigtens[j];
+ if ((z = word0(rv) & Exp_mask)
+ > Exp_msk1*(DBL_MAX_EXP+Bias-P))
+ goto ovfl;
+ if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) {
+ /* set to largest number */
+ /* (Can't trust DBL_MAX) */
+ word0(rv) = Big0;
+ word1(rv) = Big1;
+ }
+ else
+ word0(rv) += P*Exp_msk1;
+ }
+
+ }
+ }
+ else if (e1 < 0) {
+ e1 = -e1;
+ if ((i = e1 & 15) != 0)
+ rv /= tens[i];
+ if (e1 &= ~15) {
+ e1 >>= 4;
+ if (e1 >= 1 << n_bigtens)
+ goto undfl;
+ for(j = 0; e1 > 1; j++, e1 >>= 1)
+ if (e1 & 1)
+ rv *= tinytens[j];
+ /* The last multiplication could underflow. */
+ rv0 = rv;
+ rv *= tinytens[j];
+ if (!rv) {
+ rv = 2.*rv0;
+ rv *= tinytens[j];
+ if (!rv) {
+ undfl:
+ rv = 0.;
+ errno = ERANGE;
+ if (bd0)
+ goto retfree;
+ goto ret;
+ }
+ word0(rv) = Tiny0;
+ word1(rv) = Tiny1;
+ /* The refinement below will clean
+ * this approximation up.
+ */
+ }
+ }
+ }
+
+ /* Now the hard part -- adjusting rv to the correct value.*/
+
+ /* Put digits into bd: true value = bd * 10^e */
+
+ bd0 = s2b(s0, nd0, nd, y);
+
+ for(;;) {
+ bd = Balloc(bd0->k);
+ Bcopy(bd, bd0);
+ bb = d2b(rv, &bbe, &bbbits); /* rv = bb * 2^bbe */
+ bs = i2b(1);
+
+ if (e >= 0) {
+ bb2 = bb5 = 0;
+ bd2 = bd5 = e;
+ }
+ else {
+ bb2 = bb5 = -e;
+ bd2 = bd5 = 0;
+ }
+ if (bbe >= 0)
+ bb2 += bbe;
+ else
+ bd2 -= bbe;
+ bs2 = bb2;
+#ifdef Sudden_Underflow
+#ifdef IBM
+ j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3);
+#else
+ j = P + 1 - bbbits;
+#endif
+#else
+ i = bbe + bbbits - 1; /* logb(rv) */
+ if (i < Emin) /* denormal */
+ j = bbe + (P-Emin);
+ else
+ j = P + 1 - bbbits;
+#endif
+ bb2 += j;
+ bd2 += j;
+ i = bb2 < bd2 ? bb2 : bd2;
+ if (i > bs2)
+ i = bs2;
+ if (i > 0) {
+ bb2 -= i;
+ bd2 -= i;
+ bs2 -= i;
+ }
+ if (bb5 > 0) {
+ bs = pow5mult(bs, bb5);
+ bb1 = mult(bs, bb);
+ Bfree(bb);
+ bb = bb1;
+ }
+ if (bb2 > 0)
+ bb = lshift(bb, bb2);
+ if (bd5 > 0)
+ bd = pow5mult(bd, bd5);
+ if (bd2 > 0)
+ bd = lshift(bd, bd2);
+ if (bs2 > 0)
+ bs = lshift(bs, bs2);
+ delta = diff(bb, bd);
+ dsign = delta->sign;
+ delta->sign = 0;
+ i = cmp(delta, bs);
+ if (i < 0) {
+ /* Error is less than half an ulp -- check for
+ * special case of mantissa a power of two.
+ */
+ if (dsign || word1(rv) || word0(rv) & Bndry_mask)
+ break;
+ delta = lshift(delta,Log2P);
+ if (cmp(delta, bs) > 0)
+ goto drop_down;
+ break;
+ }
+ if (i == 0) {
+ /* exactly half-way between */
+ if (dsign) {
+ if ((word0(rv) & Bndry_mask1) == Bndry_mask1
+ && word1(rv) == 0xffffffff) {
+ /*boundary case -- increment exponent*/
+ word0(rv) = (word0(rv) & Exp_mask)
+ + Exp_msk1
+#ifdef IBM
+ | Exp_msk1 >> 4
+#endif
+ ;
+ word1(rv) = 0;
+ break;
+ }
+ }
+ else if (!(word0(rv) & Bndry_mask) && !word1(rv)) {
+ drop_down:
+ /* boundary case -- decrement exponent */
+#ifdef Sudden_Underflow
+ L = word0(rv) & Exp_mask;
+#ifdef IBM
+ if (L < Exp_msk1)
+#else
+ if (L <= Exp_msk1)
+#endif
+ goto undfl;
+ L -= Exp_msk1;
+#else
+ L = (long)((word0(rv) & Exp_mask) - Exp_msk1);
+#endif
+ word0(rv) = L | Bndry_mask1;
+ word1(rv) = 0xffffffff;
+#ifdef IBM
+ goto cont;
+#else
+ break;
+#endif
+ }
+#ifndef ROUND_BIASED
+ if (!(word1(rv) & LSB))
+ break;
+#endif
+ if (dsign)
+ rv += ulp(rv);
+#ifndef ROUND_BIASED
+ else {
+ rv -= ulp(rv);
+#ifndef Sudden_Underflow
+ if (!rv)
+ goto undfl;
+#endif
+ }
+#endif
+ break;
+ }
+ if ((aadj = ratio(delta, bs)) <= 2.) {
+ if (dsign)
+ aadj = aadj1 = 1.;
+ else if (word1(rv) || word0(rv) & Bndry_mask) {
+#ifndef Sudden_Underflow
+ if (word1(rv) == Tiny1 && !word0(rv))
+ goto undfl;
+#endif
+ aadj = 1.;
+ aadj1 = -1.;
+ }
+ else {
+ /* special case -- power of FLT_RADIX to be */
+ /* rounded down... */
+
+ if (aadj < 2./FLT_RADIX)
+ aadj = 1./FLT_RADIX;
+ else
+ aadj *= 0.5;
+ aadj1 = -aadj;
+ }
+ }
+ else {
+ aadj *= 0.5;
+ aadj1 = dsign ? aadj : -aadj;
+#ifdef Check_FLT_ROUNDS
+ switch(FLT_ROUNDS) {
+ case 2: /* towards +infinity */
+ aadj1 -= 0.5;
+ break;
+ case 0: /* towards 0 */
+ case 3: /* towards -infinity */
+ aadj1 += 0.5;
+ }
+#else
+ if (FLT_ROUNDS == 0)
+ aadj1 += 0.5;
+#endif
+ }
+ y = word0(rv) & Exp_mask;
+
+ /* Check for overflow */
+
+ if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) {
+ rv0 = rv;
+ word0(rv) -= P*Exp_msk1;
+ adj = aadj1 * ulp(rv);
+ rv += adj;
+ if ((word0(rv) & Exp_mask) >=
+ Exp_msk1*(DBL_MAX_EXP+Bias-P)) {
+ if (word0(rv0) == Big0 && word1(rv0) == Big1)
+ goto ovfl;
+ word0(rv) = Big0;
+ word1(rv) = Big1;
+ goto cont;
+ }
+ else
+ word0(rv) += P*Exp_msk1;
+ }
+ else {
+#ifdef Sudden_Underflow
+ if ((word0(rv) & Exp_mask) <= P*Exp_msk1) {
+ rv0 = rv;
+ word0(rv) += P*Exp_msk1;
+ adj = aadj1 * ulp(rv);
+ rv += adj;
+#ifdef IBM
+ if ((word0(rv) & Exp_mask) < P*Exp_msk1)
+#else
+ if ((word0(rv) & Exp_mask) <= P*Exp_msk1)
+#endif
+ {
+ if (word0(rv0) == Tiny0
+ && word1(rv0) == Tiny1)
+ goto undfl;
+ word0(rv) = Tiny0;
+ word1(rv) = Tiny1;
+ goto cont;
+ }
+ else
+ word0(rv) -= P*Exp_msk1;
+ }
+ else {
+ adj = aadj1 * ulp(rv);
+ rv += adj;
+ }
+#else
+ /* Compute adj so that the IEEE rounding rules will
+ * correctly round rv + adj in some half-way cases.
+ * If rv * ulp(rv) is denormalized (i.e.,
+ * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid
+ * trouble from bits lost to denormalization;
+ * example: 1.2e-307 .
+ */
+ if (y <= (P-1)*Exp_msk1 && aadj >= 1.) {
+ aadj1 = (double)(PRInt32)(aadj + 0.5);
+ if (!dsign)
+ aadj1 = -aadj1;
+ }
+ adj = aadj1 * ulp(rv);
+ rv += adj;
+#endif
+ }
+ z = word0(rv) & Exp_mask;
+ if (y == z) {
+ /* Can we stop now? */
+ L = (Long) aadj;
+ aadj -= L;
+ /* The tolerances below are conservative. */
+ if (dsign || word1(rv) || word0(rv) & Bndry_mask) {
+ if (aadj < .4999999 || aadj > .5000001)
+ break;
+ }
+ else if (aadj < .4999999/FLT_RADIX)
+ break;
+ }
+ cont:
+ Bfree(bb);
+ Bfree(bd);
+ Bfree(bs);
+ Bfree(delta);
+ }
+retfree:
+ Bfree(bb);
+ Bfree(bd);
+ Bfree(bs);
+ Bfree(bd0);
+ Bfree(delta);
+ret:
+ if (se)
+ *se = (char *)s;
+ return sign ? -rv : rv;
+}
+
+static PRInt32
+quorem(Bigint *b, Bigint *S)
+{
+ PRInt32 n;
+ Long borrow, y;
+ unsigned Long carry, q, ys;
+ unsigned Long *bx, *bxe, *sx, *sxe;
+#ifdef Pack_32
+ Long z;
+ unsigned Long si, zs;
+#endif
+
+ n = S->wds;
+#ifdef DEBUG_DTOA
+ /*debug*/ if (b->wds > n)
+ /*debug*/ Bug("oversize b in quorem");
+#endif
+ if (b->wds < n)
+ return 0;
+ sx = S->x;
+ sxe = sx + --n;
+ bx = b->x;
+ bxe = bx + n;
+ q = *bxe / (*sxe + 1); /* ensure q <= true quotient */
+#ifdef DEBUG_DTOA
+ /*debug*/ if (q > 9)
+ /*debug*/ Bug("oversized quotient in quorem");
+#endif
+ if (q) {
+ borrow = 0;
+ carry = 0;
+ do {
+#ifdef Pack_32
+ si = *sx++;
+ ys = (si & 0xffff) * q + carry;
+ zs = (si >> 16) * q + (ys >> 16);
+ carry = zs >> 16;
+ y = (long)((*bx & 0xffff) - (ys & 0xffff) + borrow);
+ borrow = y >> 16;
+ Sign_Extend(borrow, y);
+ z = (long)((*bx >> 16) - (zs & 0xffff) + borrow);
+ borrow = z >> 16;
+ Sign_Extend(borrow, z);
+ Storeinc(bx, z, y);
+#else
+ ys = *sx++ * q + carry;
+ carry = ys >> 16;
+ y = *bx - (ys & 0xffff) + borrow;
+ borrow = y >> 16;
+ Sign_Extend(borrow, y);
+ *bx++ = y & 0xffff;
+#endif
+ }
+ while(sx <= sxe);
+ if (!*bxe) {
+ bx = b->x;
+ while(--bxe > bx && !*bxe)
+ --n;
+ b->wds = n;
+ }
+ }
+ if (cmp(b, S) >= 0) {
+ q++;
+ borrow = 0;
+ carry = 0;
+ bx = b->x;
+ sx = S->x;
+ do {
+#ifdef Pack_32
+ si = *sx++;
+ ys = (si & 0xffff) + carry;
+ zs = (si >> 16) + (ys >> 16);
+ carry = zs >> 16;
+ y = (long)((*bx & 0xffff) - (ys & 0xffff) + borrow);
+ borrow = y >> 16;
+ Sign_Extend(borrow, y);
+ z = (long)((*bx >> 16) - (zs & 0xffff) + borrow);
+ borrow = z >> 16;
+ Sign_Extend(borrow, z);
+ Storeinc(bx, z, y);
+#else
+ ys = *sx++ + carry;
+ carry = ys >> 16;
+ y = *bx - (ys & 0xffff) + borrow;
+ borrow = y >> 16;
+ Sign_Extend(borrow, y);
+ *bx++ = y & 0xffff;
+#endif
+ }
+ while(sx <= sxe);
+ bx = b->x;
+ bxe = bx + n;
+ if (!*bxe) {
+ while(--bxe > bx && !*bxe)
+ --n;
+ b->wds = n;
+ }
+ }
+ return (int)q;
+}
+
+/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string.
+ *
+ * Inspired by "How to Print Floating-Point Numbers Accurately" by
+ * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101].
+ *
+ * Modifications:
+ * 1. Rather than iterating, we use a simple numeric overestimate
+ * to determine k = floor(log10(d)). We scale relevant
+ * quantities using O(log2(k)) rather than O(k) multiplications.
+ * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't
+ * try to generate digits strictly left to right. Instead, we
+ * compute with fewer bits and propagate the carry if necessary
+ * when rounding the final digit up. This is often faster.
+ * 3. Under the assumption that input will be rounded nearest,
+ * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22.
+ * That is, we allow equality in stopping tests when the
+ * round-nearest rule will give the same floating-point value
+ * as would satisfaction of the stopping test with strict
+ * inequality.
+ * 4. We remove common factors of powers of 2 from relevant
+ * quantities.
+ * 5. When converting floating-point integers less than 1e16,
+ * we use floating-point arithmetic rather than resorting
+ * to multiple-precision integers.
+ * 6. When asked to produce fewer than 15 digits, we first try
+ * to get by with floating-point arithmetic; we resort to
+ * multiple-precision integer arithmetic only if we cannot
+ * guarantee that the floating-point calculation has given
+ * the correctly rounded result. For k requested digits and
+ * "uniformly" distributed input, the probability is
+ * something like 10^(k-15) that we must resort to the Long
+ * calculation.
+ */
+
+PR_IMPLEMENT(char *)
+PR_dtoa(double d, int mode, int ndigits, int *decpt, int *sign, char **rve)
+{
+ /* Arguments ndigits, decpt, sign are similar to those
+ of ecvt and fcvt; trailing zeros are suppressed from
+ the returned string. If not null, *rve is set to point
+ to the end of the return value. If d is +-Infinity or NaN,
+ then *decpt is set to 9999.
+
+ mode:
+ 0 ==> shortest string that yields d when read in
+ and rounded to nearest.
+ 1 ==> like 0, but with Steele & White stopping rule;
+ e.g. with IEEE P754 arithmetic , mode 0 gives
+ 1e23 whereas mode 1 gives 9.999999999999999e22.
+ 2 ==> max(1,ndigits) significant digits. This gives a
+ return value similar to that of ecvt, except
+ that trailing zeros are suppressed.
+ 3 ==> through ndigits past the decimal point. This
+ gives a return value similar to that from fcvt,
+ except that trailing zeros are suppressed, and
+ ndigits can be negative.
+ 4-9 should give the same return values as 2-3, i.e.,
+ 4 <= mode <= 9 ==> same return as mode
+ 2 + (mode & 1). These modes are mainly for
+ debugging; often they run slower but sometimes
+ faster than modes 2-3.
+ 4,5,8,9 ==> left-to-right digit generation.
+ 6-9 ==> don't try fast floating-point estimate
+ (if applicable).
+
+ Values of mode other than 0-9 are treated as mode 0.
+
+ Sufficient space is allocated to the return value
+ to hold the suppressed trailing zeros.
+ */
+
+ PRInt32 bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1,
+ j, j1, k, k0, k_check, leftright, m2, m5, s2, s5,
+ spec_case, try_quick;
+ Long L;
+#ifndef Sudden_Underflow
+ PRInt32 denorm;
+ unsigned Long x;
+#endif
+ Bigint *b, *b1, *delta, *mlo, *mhi, *S;
+ double d2, ds, eps;
+ char *s, *s0;
+ static Bigint *result;
+ static PRInt32 result_k;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ if (result) {
+ result->k = result_k;
+ result->maxwds = 1 << result_k;
+ Bfree(result);
+ result = 0;
+ }
+
+ if (word0(d) & Sign_bit) {
+ /* set sign for everything, including 0's and NaNs */
+ *sign = 1;
+ word0(d) &= ~Sign_bit; /* clear sign bit */
+ }
+ else
+ *sign = 0;
+
+#if defined(IEEE_Arith) + defined(VAX)
+#ifdef IEEE_Arith
+ if ((word0(d) & Exp_mask) == Exp_mask)
+#else
+ if (word0(d) == 0x8000)
+#endif
+ {
+ /* Infinity or NaN */
+ *decpt = 9999;
+ s =
+#ifdef IEEE_Arith
+ !word1(d) && !(word0(d) & 0xfffff) ? "Infinity" :
+#endif
+ "NaN";
+ if (rve)
+ *rve =
+#ifdef IEEE_Arith
+ s[3] ? s + 8 :
+#endif
+ s + 3;
+ return s;
+ }
+#endif
+#ifdef IBM
+ d += 0; /* normalize */
+#endif
+ if (!d) {
+ *decpt = 1;
+ s = "0";
+ if (rve)
+ *rve = s + 1;
+ return s;
+ }
+
+ b = d2b(d, &be, &bbits);
+#ifdef Sudden_Underflow
+ i = (PRInt32)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1));
+#else
+ if ((i = (PRInt32)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1))) != 0) {
+#endif
+ d2 = d;
+ word0(d2) &= Frac_mask1;
+ word0(d2) |= Exp_11;
+#ifdef IBM
+ if (j = 11 - hi0bits(word0(d2) & Frac_mask))
+ d2 /= 1 << j;
+#endif
+
+ /* log(x) ~=~ log(1.5) + (x-1.5)/1.5
+ * log10(x) = log(x) / log(10)
+ * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10))
+ * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2)
+ *
+ * This suggests computing an approximation k to log10(d) by
+ *
+ * k = (i - Bias)*0.301029995663981
+ * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 );
+ *
+ * We want k to be too large rather than too small.
+ * The error in the first-order Taylor series approximation
+ * is in our favor, so we just round up the constant enough
+ * to compensate for any error in the multiplication of
+ * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077,
+ * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14,
+ * adding 1e-13 to the constant term more than suffices.
+ * Hence we adjust the constant term to 0.1760912590558.
+ * (We could get a more accurate k by invoking log10,
+ * but this is probably not worthwhile.)
+ */
+
+ i -= Bias;
+#ifdef IBM
+ i <<= 2;
+ i += j;
+#endif
+#ifndef Sudden_Underflow
+ denorm = 0;
+ }
+ else {
+ /* d is denormalized */
+
+ i = bbits + be + (Bias + (P-1) - 1);
+ x = i > 32 ? word0(d) << (64 - i) | word1(d) >> (i - 32)
+ : word1(d) << (32 - i);
+ d2 = x;
+ word0(d2) -= 31*Exp_msk1; /* adjust exponent */
+ i -= (Bias + (P-1) - 1) + 1;
+ denorm = 1;
+ }
+#endif
+ ds = (d2-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981;
+ k = (PRInt32)ds;
+ if (ds < 0. && ds != k)
+ k--; /* want k = floor(ds) */
+ k_check = 1;
+ if (k >= 0 && k <= Ten_pmax) {
+ if (d < tens[k])
+ k--;
+ k_check = 0;
+ }
+ j = bbits - i - 1;
+ if (j >= 0) {
+ b2 = 0;
+ s2 = j;
+ }
+ else {
+ b2 = -j;
+ s2 = 0;
+ }
+ if (k >= 0) {
+ b5 = 0;
+ s5 = k;
+ s2 += k;
+ }
+ else {
+ b2 -= k;
+ b5 = -k;
+ s5 = 0;
+ }
+ if (mode < 0 || mode > 9)
+ mode = 0;
+ try_quick = 1;
+ if (mode > 5) {
+ mode -= 4;
+ try_quick = 0;
+ }
+ leftright = 1;
+ switch(mode) {
+ case 0:
+ case 1:
+ ilim = ilim1 = -1;
+ i = 18;
+ ndigits = 0;
+ break;
+ case 2:
+ leftright = 0;
+ /* no break */
+ case 4:
+ if (ndigits <= 0)
+ ndigits = 1;
+ ilim = ilim1 = i = ndigits;
+ break;
+ case 3:
+ leftright = 0;
+ /* no break */
+ case 5:
+ i = ndigits + k + 1;
+ ilim = i;
+ ilim1 = i - 1;
+ if (i <= 0)
+ i = 1;
+ }
+ j = sizeof(unsigned Long);
+ for(result_k = 0; sizeof(Bigint) - sizeof(unsigned Long) <= i - j;
+ j <<= 1) result_k++;
+ result = Balloc(result_k);
+ s = s0 = (char *)result;
+
+ if (ilim >= 0 && ilim <= Quick_max && try_quick) {
+
+ /* Try to get by with floating-point arithmetic. */
+
+ i = 0;
+ d2 = d;
+ k0 = k;
+ ilim0 = ilim;
+ ieps = 2; /* conservative */
+ if (k > 0) {
+ ds = tens[k&0xf];
+ j = k >> 4;
+ if (j & Bletch) {
+ /* prevent overflows */
+ j &= Bletch - 1;
+ d /= bigtens[n_bigtens-1];
+ ieps++;
+ }
+ for(; j; j >>= 1, i++)
+ if (j & 1) {
+ ieps++;
+ ds *= bigtens[i];
+ }
+ d /= ds;
+ }
+ else if ((j1 = -k) != 0) {
+ d *= tens[j1 & 0xf];
+ for(j = j1 >> 4; j; j >>= 1, i++)
+ if (j & 1) {
+ ieps++;
+ d *= bigtens[i];
+ }
+ }
+ if (k_check && d < 1. && ilim > 0) {
+ if (ilim1 <= 0)
+ goto fast_failed;
+ ilim = ilim1;
+ k--;
+ d *= 10.;
+ ieps++;
+ }
+ eps = ieps*d + 7.;
+ word0(eps) -= (P-1)*Exp_msk1;
+ if (ilim == 0) {
+ S = mhi = 0;
+ d -= 5.;
+ if (d > eps)
+ goto one_digit;
+ if (d < -eps)
+ goto no_digits;
+ goto fast_failed;
+ }
+#ifndef No_leftright
+ if (leftright) {
+ /* Use Steele & White method of only
+ * generating digits needed.
+ */
+ eps = 0.5/tens[ilim-1] - eps;
+ for(i = 0;;) {
+ L = (Long) d;
+ d -= L;
+ *s++ = '0' + (PRInt32)L;
+ if (d < eps)
+ goto ret1;
+ if (1. - d < eps)
+ goto bump_up;
+ if (++i >= ilim)
+ break;
+ eps *= 10.;
+ d *= 10.;
+ }
+ }
+ else {
+#endif
+ /* Generate ilim digits, then fix them up. */
+ eps *= tens[ilim-1];
+ for(i = 1;; i++, d *= 10.) {
+ L = (Long) d;
+ d -= L;
+ *s++ = '0' + (PRInt32)L;
+ if (i == ilim) {
+ if (d > 0.5 + eps)
+ goto bump_up;
+ else if (d < 0.5 - eps) {
+ while(*--s == '0'){} /* just count -- nothing to execute */
+ s++;
+ goto ret1;
+ }
+ break;
+ }
+ }
+#ifndef No_leftright
+ }
+#endif
+ fast_failed:
+ s = s0;
+ d = d2;
+ k = k0;
+ ilim = ilim0;
+ }
+
+ /* Do we have a "small" integer? */
+
+ if (be >= 0 && k <= Int_max) {
+ /* Yes. */
+ ds = tens[k];
+ if (ndigits < 0 && ilim <= 0) {
+ S = mhi = 0;
+ if (ilim < 0 || d <= 5*ds)
+ goto no_digits;
+ goto one_digit;
+ }
+ for(i = 1;; i++) {
+ L = (Long) (d / ds);
+ d -= L*ds;
+#ifdef Check_FLT_ROUNDS
+ /* If FLT_ROUNDS == 2, L will usually be high by 1 */
+ if (d < 0) {
+ L--;
+ d += ds;
+ }
+#endif
+ *s++ = '0' + (PRInt32)L;
+ if (i == ilim) {
+ d += d;
+ if ((d > ds) || (d == ds && L & 1)) {
+ bump_up:
+ while(*--s == '9')
+ if (s == s0) {
+ k++;
+ *s = '0';
+ break;
+ }
+ ++*s++;
+ }
+ break;
+ }
+ if (!(d *= 10.))
+ break;
+ }
+ goto ret1;
+ }
+
+ m2 = b2;
+ m5 = b5;
+ mhi = mlo = 0;
+ if (leftright) {
+ if (mode < 2) {
+ i =
+#ifndef Sudden_Underflow
+ denorm ? be + (Bias + (P-1) - 1 + 1) :
+#endif
+#ifdef IBM
+ 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3);
+#else
+ 1 + P - bbits;
+#endif
+ }
+ else {
+ j = ilim - 1;
+ if (m5 >= j)
+ m5 -= j;
+ else {
+ s5 += j -= m5;
+ b5 += j;
+ m5 = 0;
+ }
+ if ((i = ilim) < 0) {
+ m2 -= i;
+ i = 0;
+ }
+ }
+ b2 += i;
+ s2 += i;
+ mhi = i2b(1);
+ }
+ if (m2 > 0 && s2 > 0) {
+ i = m2 < s2 ? m2 : s2;
+ b2 -= i;
+ m2 -= i;
+ s2 -= i;
+ }
+ if (b5 > 0) {
+ if (leftright) {
+ if (m5 > 0) {
+ mhi = pow5mult(mhi, m5);
+ b1 = mult(mhi, b);
+ Bfree(b);
+ b = b1;
+ }
+ if ((j = b5 - m5) != 0)
+ b = pow5mult(b, j);
+ }
+ else
+ b = pow5mult(b, b5);
+ }
+ S = i2b(1);
+ if (s5 > 0)
+ S = pow5mult(S, s5);
+
+ /* Check for special case that d is a normalized power of 2. */
+
+ if (mode < 2) {
+ if (!word1(d) && !(word0(d) & Bndry_mask)
+#ifndef Sudden_Underflow
+ && word0(d) & Exp_mask
+#endif
+ ) {
+ /* The special case */
+ b2 += Log2P;
+ s2 += Log2P;
+ spec_case = 1;
+ }
+ else
+ spec_case = 0;
+ }
+
+ /* Arrange for convenient computation of quotients:
+ * shift left if necessary so divisor has 4 leading 0 bits.
+ *
+ * Perhaps we should just compute leading 28 bits of S once
+ * and for all and pass them and a shift to quorem, so it
+ * can do shifts and ors to compute the numerator for q.
+ */
+#ifdef Pack_32
+ if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f) != 0)
+ i = 32 - i;
+#else
+ if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf)
+ i = 16 - i;
+#endif
+ if (i > 4) {
+ i -= 4;
+ b2 += i;
+ m2 += i;
+ s2 += i;
+ }
+ else if (i < 4) {
+ i += 28;
+ b2 += i;
+ m2 += i;
+ s2 += i;
+ }
+ if (b2 > 0)
+ b = lshift(b, b2);
+ if (s2 > 0)
+ S = lshift(S, s2);
+ if (k_check) {
+ if (cmp(b,S) < 0) {
+ k--;
+ b = multadd(b, 10, 0); /* we botched the k estimate */
+ if (leftright)
+ mhi = multadd(mhi, 10, 0);
+ ilim = ilim1;
+ }
+ }
+ if (ilim <= 0 && mode > 2) {
+ if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) {
+ /* no digits, fcvt style */
+ no_digits:
+ k = -1 - ndigits;
+ goto ret;
+ }
+ one_digit:
+ *s++ = '1';
+ k++;
+ goto ret;
+ }
+ if (leftright) {
+ if (m2 > 0)
+ mhi = lshift(mhi, m2);
+
+ /* Compute mlo -- check for special case
+ * that d is a normalized power of 2.
+ */
+
+ mlo = mhi;
+ if (spec_case) {
+ mhi = Balloc(mhi->k);
+ Bcopy(mhi, mlo);
+ mhi = lshift(mhi, Log2P);
+ }
+
+ for(i = 1;;i++) {
+ dig = quorem(b,S) + '0';
+ /* Do we yet have the shortest decimal string
+ * that will round to d?
+ */
+ j = cmp(b, mlo);
+ delta = diff(S, mhi);
+ j1 = delta->sign ? 1 : cmp(b, delta);
+ Bfree(delta);
+#ifndef ROUND_BIASED
+ if (j1 == 0 && !mode && !(word1(d) & 1)) {
+ if (dig == '9')
+ goto round_9_up;
+ if (j > 0)
+ dig++;
+ *s++ = dig;
+ goto ret;
+ }
+#endif
+ if ((j < 0) || ((j == 0) && (!mode)
+#ifndef ROUND_BIASED
+ && (!(word1(d) & 1)))
+#endif
+ ) {
+ if (j1 > 0) {
+ b = lshift(b, 1);
+ j1 = cmp(b, S);
+ if (((j1 > 0) || (j1 == 0 && dig & 1))
+ && (dig++ == '9'))
+ goto round_9_up;
+ }
+ *s++ = dig;
+ goto ret;
+ }
+ if (j1 > 0) {
+ if (dig == '9') { /* possible if i == 1 */
+ round_9_up:
+ *s++ = '9';
+ goto roundoff;
+ }
+ *s++ = dig + 1;
+ goto ret;
+ }
+ *s++ = dig;
+ if (i == ilim)
+ break;
+ b = multadd(b, 10, 0);
+ if (mlo == mhi)
+ mlo = mhi = multadd(mhi, 10, 0);
+ else {
+ mlo = multadd(mlo, 10, 0);
+ mhi = multadd(mhi, 10, 0);
+ }
+ }
+ }
+ else
+ for(i = 1;; i++) {
+ *s++ = dig = quorem(b,S) + '0';
+ if (i >= ilim)
+ break;
+ b = multadd(b, 10, 0);
+ }
+
+ /* Round off last digit */
+
+ b = lshift(b, 1);
+ j = cmp(b, S);
+ if ((j > 0) || (j == 0 && dig & 1)) {
+ roundoff:
+ while(*--s == '9')
+ if (s == s0) {
+ k++;
+ *s++ = '1';
+ goto ret;
+ }
+ ++*s++;
+ }
+ else {
+ while(*--s == '0'){} /* just count -- nothing to execute */
+ s++;
+ }
+ret:
+ Bfree(S);
+ if (mhi) {
+ if (mlo && mlo != mhi)
+ Bfree(mlo);
+ Bfree(mhi);
+ }
+ret1:
+ Bfree(b);
+ *s = 0;
+ *decpt = k + 1;
+ if (rve)
+ *rve = s;
+ return s0;
+}
+
+/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string.
+ *
+ * Inspired by "How to Print Floating-Point Numbers Accurately" by
+ * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101].
+ *
+ * Modifications:
+ * 1. Rather than iterating, we use a simple numeric overestimate
+ * to determine k = floor(log10(d)). We scale relevant
+ * quantities using O(log2(k)) rather than O(k) multiplications.
+ * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't
+ * try to generate digits strictly left to right. Instead, we
+ * compute with fewer bits and propagate the carry if necessary
+ * when rounding the final digit up. This is often faster.
+ * 3. Under the assumption that input will be rounded nearest,
+ * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22.
+ * That is, we allow equality in stopping tests when the
+ * round-nearest rule will give the same floating-point value
+ * as would satisfaction of the stopping test with strict
+ * inequality.
+ * 4. We remove common factors of powers of 2 from relevant
+ * quantities.
+ * 5. When converting floating-point integers less than 1e16,
+ * we use floating-point arithmetic rather than resorting
+ * to multiple-precision integers.
+ * 6. When asked to produce fewer than 15 digits, we first try
+ * to get by with floating-point arithmetic; we resort to
+ * multiple-precision integer arithmetic only if we cannot
+ * guarantee that the floating-point calculation has given
+ * the correctly rounded result. For k requested digits and
+ * "uniformly" distributed input, the probability is
+ * something like 10^(k-15) that we must resort to the Long
+ * calculation.
+ */
+
+PR_IMPLEMENT(PRStatus)
+PR_dtoa_r(double d, int mode, int ndigits,
+ int *decpt, int *sign, char **rve, char *buf, PRSize bufsize)
+{
+ /* Arguments ndigits, decpt, sign are similar to those
+ of ecvt and fcvt; trailing zeros are suppressed from
+ the returned string. If not null, *rve is set to point
+ to the end of the return value. If d is +-Infinity or NaN,
+ then *decpt is set to 9999.
+
+ mode:
+ 0 ==> shortest string that yields d when read in
+ and rounded to nearest.
+ 1 ==> like 0, but with Steele & White stopping rule;
+ e.g. with IEEE P754 arithmetic , mode 0 gives
+ 1e23 whereas mode 1 gives 9.999999999999999e22.
+ 2 ==> max(1,ndigits) significant digits. This gives a
+ return value similar to that of ecvt, except
+ that trailing zeros are suppressed.
+ 3 ==> through ndigits past the decimal point. This
+ gives a return value similar to that from fcvt,
+ except that trailing zeros are suppressed, and
+ ndigits can be negative.
+ 4-9 should give the same return values as 2-3, i.e.,
+ 4 <= mode <= 9 ==> same return as mode
+ 2 + (mode & 1). These modes are mainly for
+ debugging; often they run slower but sometimes
+ faster than modes 2-3.
+ 4,5,8,9 ==> left-to-right digit generation.
+ 6-9 ==> don't try fast floating-point estimate
+ (if applicable).
+
+ Values of mode other than 0-9 are treated as mode 0.
+
+ Sufficient space is allocated to the return value
+ to hold the suppressed trailing zeros.
+ */
+
+ PRInt32 bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1,
+ j, j1, k, k0, k_check, leftright, m2, m5, s2, s5,
+ spec_case, try_quick;
+ Long L;
+#ifndef Sudden_Underflow
+ PRInt32 denorm;
+ unsigned Long x;
+#endif
+ Bigint *b, *b1, *delta, *mlo, *mhi, *S;
+ double d2, ds, eps;
+ char *s, *s0;
+ Bigint *result = 0;
+ PRInt32 result_k;
+ PRStatus retval;
+ PRSize strsize;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ if (word0(d) & Sign_bit) {
+ /* set sign for everything, including 0's and NaNs */
+ *sign = 1;
+ word0(d) &= ~Sign_bit; /* clear sign bit */
+ }
+ else
+ *sign = 0;
+
+#if defined(IEEE_Arith) + defined(VAX)
+#ifdef IEEE_Arith
+ if ((word0(d) & Exp_mask) == Exp_mask)
+#else
+ if (word0(d) == 0x8000)
+#endif
+ {
+ /* Infinity or NaN */
+ *decpt = 9999;
+ s =
+#ifdef IEEE_Arith
+ !word1(d) && !(word0(d) & 0xfffff) ? "Infinity" :
+#endif
+ "NaN";
+ if ((s[0] == 'I' && bufsize < 9) || (s[0] == 'N' && bufsize < 4)) {
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
+ return PR_FAILURE;
+ }
+ strcpy(buf, s);
+ if (rve) {
+ *rve =
+#ifdef IEEE_Arith
+ buf[3] ? buf + 8 :
+#endif
+ buf + 3;
+ PR_ASSERT(**rve == '\0');
+ }
+ return PR_SUCCESS;
+ }
+#endif
+#ifdef IBM
+ d += 0; /* normalize */
+#endif
+ if (!d) {
+ *decpt = 1;
+ if (bufsize < 2) {
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
+ return PR_FAILURE;
+ }
+ buf[0] = '0'; buf[1] = '\0'; /* copy "0" to buffer */
+ if (rve) {
+ *rve = buf + 1;
+ PR_ASSERT(**rve == '\0');
+ }
+ return PR_SUCCESS;
+ }
+
+ b = d2b(d, &be, &bbits);
+#ifdef Sudden_Underflow
+ i = (PRInt32)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1));
+#else
+ if ((i = (PRInt32)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1))) != 0) {
+#endif
+ d2 = d;
+ word0(d2) &= Frac_mask1;
+ word0(d2) |= Exp_11;
+#ifdef IBM
+ if (j = 11 - hi0bits(word0(d2) & Frac_mask))
+ d2 /= 1 << j;
+#endif
+
+ /* log(x) ~=~ log(1.5) + (x-1.5)/1.5
+ * log10(x) = log(x) / log(10)
+ * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10))
+ * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2)
+ *
+ * This suggests computing an approximation k to log10(d) by
+ *
+ * k = (i - Bias)*0.301029995663981
+ * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 );
+ *
+ * We want k to be too large rather than too small.
+ * The error in the first-order Taylor series approximation
+ * is in our favor, so we just round up the constant enough
+ * to compensate for any error in the multiplication of
+ * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077,
+ * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14,
+ * adding 1e-13 to the constant term more than suffices.
+ * Hence we adjust the constant term to 0.1760912590558.
+ * (We could get a more accurate k by invoking log10,
+ * but this is probably not worthwhile.)
+ */
+
+ i -= Bias;
+#ifdef IBM
+ i <<= 2;
+ i += j;
+#endif
+#ifndef Sudden_Underflow
+ denorm = 0;
+ }
+ else {
+ /* d is denormalized */
+
+ i = bbits + be + (Bias + (P-1) - 1);
+ x = i > 32 ? word0(d) << (64 - i) | word1(d) >> (i - 32)
+ : word1(d) << (32 - i);
+ d2 = x;
+ word0(d2) -= 31*Exp_msk1; /* adjust exponent */
+ i -= (Bias + (P-1) - 1) + 1;
+ denorm = 1;
+ }
+#endif
+ ds = (d2-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981;
+ k = (PRInt32)ds;
+ if (ds < 0. && ds != k)
+ k--; /* want k = floor(ds) */
+ k_check = 1;
+ if (k >= 0 && k <= Ten_pmax) {
+ if (d < tens[k])
+ k--;
+ k_check = 0;
+ }
+ j = bbits - i - 1;
+ if (j >= 0) {
+ b2 = 0;
+ s2 = j;
+ }
+ else {
+ b2 = -j;
+ s2 = 0;
+ }
+ if (k >= 0) {
+ b5 = 0;
+ s5 = k;
+ s2 += k;
+ }
+ else {
+ b2 -= k;
+ b5 = -k;
+ s5 = 0;
+ }
+ if (mode < 0 || mode > 9)
+ mode = 0;
+ try_quick = 1;
+ if (mode > 5) {
+ mode -= 4;
+ try_quick = 0;
+ }
+ leftright = 1;
+ switch(mode) {
+ case 0:
+ case 1:
+ ilim = ilim1 = -1;
+ i = 18;
+ ndigits = 0;
+ break;
+ case 2:
+ leftright = 0;
+ /* no break */
+ case 4:
+ if (ndigits <= 0)
+ ndigits = 1;
+ ilim = ilim1 = i = ndigits;
+ break;
+ case 3:
+ leftright = 0;
+ /* no break */
+ case 5:
+ i = ndigits + k + 1;
+ ilim = i;
+ ilim1 = i - 1;
+ if (i <= 0)
+ i = 1;
+ }
+ j = sizeof(unsigned Long);
+ for(result_k = 0; sizeof(Bigint) - sizeof(unsigned Long) <= i - j;
+ j <<= 1) result_k++;
+ result = Balloc(result_k);
+ s = s0 = (char *)result;
+
+ if (ilim >= 0 && ilim <= Quick_max && try_quick) {
+
+ /* Try to get by with floating-point arithmetic. */
+
+ i = 0;
+ d2 = d;
+ k0 = k;
+ ilim0 = ilim;
+ ieps = 2; /* conservative */
+ if (k > 0) {
+ ds = tens[k&0xf];
+ j = k >> 4;
+ if (j & Bletch) {
+ /* prevent overflows */
+ j &= Bletch - 1;
+ d /= bigtens[n_bigtens-1];
+ ieps++;
+ }
+ for(; j; j >>= 1, i++)
+ if (j & 1) {
+ ieps++;
+ ds *= bigtens[i];
+ }
+ d /= ds;
+ }
+ else if ((j1 = -k) != 0) {
+ d *= tens[j1 & 0xf];
+ for(j = j1 >> 4; j; j >>= 1, i++)
+ if (j & 1) {
+ ieps++;
+ d *= bigtens[i];
+ }
+ }
+ if (k_check && d < 1. && ilim > 0) {
+ if (ilim1 <= 0)
+ goto fast_failed;
+ ilim = ilim1;
+ k--;
+ d *= 10.;
+ ieps++;
+ }
+ eps = ieps*d + 7.;
+ word0(eps) -= (P-1)*Exp_msk1;
+ if (ilim == 0) {
+ S = mhi = 0;
+ d -= 5.;
+ if (d > eps)
+ goto one_digit;
+ if (d < -eps)
+ goto no_digits;
+ goto fast_failed;
+ }
+#ifndef No_leftright
+ if (leftright) {
+ /* Use Steele & White method of only
+ * generating digits needed.
+ */
+ eps = 0.5/tens[ilim-1] - eps;
+ for(i = 0;;) {
+ L = (Long) d;
+ d -= L;
+ *s++ = '0' + (PRInt32)L;
+ if (d < eps)
+ goto ret1;
+ if (1. - d < eps)
+ goto bump_up;
+ if (++i >= ilim)
+ break;
+ eps *= 10.;
+ d *= 10.;
+ }
+ }
+ else {
+#endif
+ /* Generate ilim digits, then fix them up. */
+ eps *= tens[ilim-1];
+ for(i = 1;; i++, d *= 10.) {
+ L = (Long) d;
+ d -= L;
+ *s++ = '0' + (PRInt32)L;
+ if (i == ilim) {
+ if (d > 0.5 + eps)
+ goto bump_up;
+ else if (d < 0.5 - eps) {
+ while(*--s == '0'){} /* just count -- nothing to execute */
+ s++;
+ goto ret1;
+ }
+ break;
+ }
+ }
+#ifndef No_leftright
+ }
+#endif
+ fast_failed:
+ s = s0;
+ d = d2;
+ k = k0;
+ ilim = ilim0;
+ }
+
+ /* Do we have a "small" integer? */
+
+ if (be >= 0 && k <= Int_max) {
+ /* Yes. */
+ ds = tens[k];
+ if (ndigits < 0 && ilim <= 0) {
+ S = mhi = 0;
+ if (ilim < 0 || d <= 5*ds)
+ goto no_digits;
+ goto one_digit;
+ }
+ for(i = 1;; i++) {
+ L = (Long) (d / ds);
+ d -= L*ds;
+#ifdef Check_FLT_ROUNDS
+ /* If FLT_ROUNDS == 2, L will usually be high by 1 */
+ if (d < 0) {
+ L--;
+ d += ds;
+ }
+#endif
+ *s++ = '0' + (PRInt32)L;
+ if (i == ilim) {
+ d += d;
+ if ((d > ds) || (d == ds && L & 1)) {
+ bump_up:
+ while(*--s == '9')
+ if (s == s0) {
+ k++;
+ *s = '0';
+ break;
+ }
+ ++*s++;
+ }
+ break;
+ }
+ if (!(d *= 10.))
+ break;
+ }
+ goto ret1;
+ }
+
+ m2 = b2;
+ m5 = b5;
+ mhi = mlo = 0;
+ if (leftright) {
+ if (mode < 2) {
+ i =
+#ifndef Sudden_Underflow
+ denorm ? be + (Bias + (P-1) - 1 + 1) :
+#endif
+#ifdef IBM
+ 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3);
+#else
+ 1 + P - bbits;
+#endif
+ }
+ else {
+ j = ilim - 1;
+ if (m5 >= j)
+ m5 -= j;
+ else {
+ s5 += j -= m5;
+ b5 += j;
+ m5 = 0;
+ }
+ if ((i = ilim) < 0) {
+ m2 -= i;
+ i = 0;
+ }
+ }
+ b2 += i;
+ s2 += i;
+ mhi = i2b(1);
+ }
+ if (m2 > 0 && s2 > 0) {
+ i = m2 < s2 ? m2 : s2;
+ b2 -= i;
+ m2 -= i;
+ s2 -= i;
+ }
+ if (b5 > 0) {
+ if (leftright) {
+ if (m5 > 0) {
+ mhi = pow5mult(mhi, m5);
+ b1 = mult(mhi, b);
+ Bfree(b);
+ b = b1;
+ }
+ if ((j = b5 - m5) != 0)
+ b = pow5mult(b, j);
+ }
+ else
+ b = pow5mult(b, b5);
+ }
+ S = i2b(1);
+ if (s5 > 0)
+ S = pow5mult(S, s5);
+
+ /* Check for special case that d is a normalized power of 2. */
+
+ if (mode < 2) {
+ if (!word1(d) && !(word0(d) & Bndry_mask)
+#ifndef Sudden_Underflow
+ && word0(d) & Exp_mask
+#endif
+ ) {
+ /* The special case */
+ b2 += Log2P;
+ s2 += Log2P;
+ spec_case = 1;
+ }
+ else
+ spec_case = 0;
+ }
+
+ /* Arrange for convenient computation of quotients:
+ * shift left if necessary so divisor has 4 leading 0 bits.
+ *
+ * Perhaps we should just compute leading 28 bits of S once
+ * and for all and pass them and a shift to quorem, so it
+ * can do shifts and ors to compute the numerator for q.
+ */
+#ifdef Pack_32
+ if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f) != 0)
+ i = 32 - i;
+#else
+ if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf)
+ i = 16 - i;
+#endif
+ if (i > 4) {
+ i -= 4;
+ b2 += i;
+ m2 += i;
+ s2 += i;
+ }
+ else if (i < 4) {
+ i += 28;
+ b2 += i;
+ m2 += i;
+ s2 += i;
+ }
+ if (b2 > 0)
+ b = lshift(b, b2);
+ if (s2 > 0)
+ S = lshift(S, s2);
+ if (k_check) {
+ if (cmp(b,S) < 0) {
+ k--;
+ b = multadd(b, 10, 0); /* we botched the k estimate */
+ if (leftright)
+ mhi = multadd(mhi, 10, 0);
+ ilim = ilim1;
+ }
+ }
+ if (ilim <= 0 && mode > 2) {
+ if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) {
+ /* no digits, fcvt style */
+ no_digits:
+ k = -1 - ndigits;
+ goto ret;
+ }
+ one_digit:
+ *s++ = '1';
+ k++;
+ goto ret;
+ }
+ if (leftright) {
+ if (m2 > 0)
+ mhi = lshift(mhi, m2);
+
+ /* Compute mlo -- check for special case
+ * that d is a normalized power of 2.
+ */
+
+ mlo = mhi;
+ if (spec_case) {
+ mhi = Balloc(mhi->k);
+ Bcopy(mhi, mlo);
+ mhi = lshift(mhi, Log2P);
+ }
+
+ for(i = 1;;i++) {
+ dig = quorem(b,S) + '0';
+ /* Do we yet have the shortest decimal string
+ * that will round to d?
+ */
+ j = cmp(b, mlo);
+ delta = diff(S, mhi);
+ j1 = delta->sign ? 1 : cmp(b, delta);
+ Bfree(delta);
+#ifndef ROUND_BIASED
+ if (j1 == 0 && !mode && !(word1(d) & 1)) {
+ if (dig == '9')
+ goto round_9_up;
+ if (j > 0)
+ dig++;
+ *s++ = dig;
+ goto ret;
+ }
+#endif
+ if ((j < 0) || ((j == 0) && (!mode)
+#ifndef ROUND_BIASED
+ && (!(word1(d) & 1)))
+#endif
+ ) {
+ if (j1 > 0) {
+ b = lshift(b, 1);
+ j1 = cmp(b, S);
+ if (((j1 > 0) || (j1 == 0 && dig & 1))
+ && (dig++ == '9'))
+ goto round_9_up;
+ }
+ *s++ = dig;
+ goto ret;
+ }
+ if (j1 > 0) {
+ if (dig == '9') { /* possible if i == 1 */
+ round_9_up:
+ *s++ = '9';
+ goto roundoff;
+ }
+ *s++ = dig + 1;
+ goto ret;
+ }
+ *s++ = dig;
+ if (i == ilim)
+ break;
+ b = multadd(b, 10, 0);
+ if (mlo == mhi)
+ mlo = mhi = multadd(mhi, 10, 0);
+ else {
+ mlo = multadd(mlo, 10, 0);
+ mhi = multadd(mhi, 10, 0);
+ }
+ }
+ }
+ else
+ for(i = 1;; i++) {
+ *s++ = dig = quorem(b,S) + '0';
+ if (i >= ilim)
+ break;
+ b = multadd(b, 10, 0);
+ }
+
+ /* Round off last digit */
+
+ b = lshift(b, 1);
+ j = cmp(b, S);
+ if ((j > 0) || (j == 0 && dig & 1)) {
+ roundoff:
+ while(*--s == '9')
+ if (s == s0) {
+ k++;
+ *s++ = '1';
+ goto ret;
+ }
+ ++*s++;
+ }
+ else {
+ while(*--s == '0'){} /* just count -- nothing to execute */
+ s++;
+ }
+ret:
+ Bfree(S);
+ if (mhi) {
+ if (mlo && mlo != mhi)
+ Bfree(mlo);
+ Bfree(mhi);
+ }
+ret1:
+ Bfree(b);
+ *s = 0;
+ *decpt = k + 1;
+ strsize = (s - s0) + 1;
+ if (strsize <= bufsize) {
+ retval = PR_SUCCESS;
+ memcpy(buf, s0, strsize);
+ if (rve) {
+ *rve = buf + strsize - 1;
+ PR_ASSERT(**rve == '\0');
+ }
+ } else {
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
+ retval = PR_FAILURE;
+ }
+
+ /* cleanup */
+ result->k = result_k;
+ result->maxwds = 1 << result_k;
+ Bfree(result);
+
+ return retval;
+}
+
+/*
+** conversion routines for floating point
+** prcsn - number of digits of precision to generate floating
+** point value.
+** This should be reparameterized so that you can send in a
+** prcn for the positive and negative ranges. For now,
+** conform to the ECMA JavaScript spec which says numbers
+** less than 1e-6 are in scientific notation.
+** Also, the ECMA spec says that there should always be a
+** '+' or '-' after the 'e' in scientific notation
+*/
+PR_IMPLEMENT(void)
+PR_cnvtf(char *buf,int bufsz, int prcsn,double fval)
+{
+ PRIntn decpt, sign, numdigits;
+ char *num, *nump;
+ char *bufp = buf;
+ char *endnum;
+
+ /* If anything fails, we store an empty string in 'buf' */
+ num = (char*)PR_MALLOC(bufsz);
+ if (num == NULL) {
+ buf[0] = '\0';
+ return;
+ }
+ /* XXX Why use mode 1? */
+ if (PR_dtoa_r(fval,1,prcsn,&decpt,&sign,&endnum,num,bufsz)
+ == PR_FAILURE) {
+ buf[0] = '\0';
+ goto done;
+ }
+ numdigits = endnum - num;
+ nump = num;
+
+ if (sign &&
+ !(word0(fval) == Sign_bit && word1(fval) == 0) &&
+ !((word0(fval) & Exp_mask) == Exp_mask &&
+ (word1(fval) || (word0(fval) & 0xfffff)))) {
+ *bufp++ = '-';
+ }
+
+ if(decpt == 9999){
+ while((*bufp++ = *nump++) != 0){} /* nothing to execute */
+ goto done;
+ }
+
+ if(decpt > (prcsn+1) || decpt < -(prcsn-1) || decpt < -5){
+ *bufp++ = *nump++;
+ if(numdigits != 1){
+ *bufp++ = '.';
+ }
+
+ while(*nump != '\0'){
+ *bufp++ = *nump++;
+ }
+ *bufp++ = 'e';
+ PR_snprintf(bufp,bufsz - (bufp - buf), "%+d",decpt-1);
+ }
+ else if(decpt >= 0){
+ while(decpt--){
+ if(*nump != '\0'){
+ *bufp++ = *nump++;
+ }
+ else {
+ *bufp++ = '0';
+ }
+ }
+ if(*nump != '\0'){
+ *bufp++ = '.';
+ while(*nump != '\0'){
+ *bufp++ = *nump++;
+ }
+ }
+ *bufp++ = '\0';
+ }
+ else if(decpt < 0){
+ *bufp++ = '0';
+ *bufp++ = '.';
+ while(decpt++){
+ *bufp++ = '0';
+ }
+
+ while(*nump != '\0'){
+ *bufp++ = *nump++;
+ }
+ *bufp++ = '\0';
+ }
+done:
+ PR_DELETE(num);
+}
diff --git a/pr/src/misc/prenv.c b/pr/src/misc/prenv.c
new file mode 100644
index 00000000..b6b36fad
--- /dev/null
+++ b/pr/src/misc/prenv.c
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include <string.h>
+#include "primpl.h"
+
+/* Lock used to lock the environment */
+#if defined(_PR_NO_PREEMPT)
+#define _PR_NEW_LOCK_ENV()
+#define _PR_LOCK_ENV()
+#define _PR_UNLOCK_ENV()
+#elif defined(_PR_LOCAL_THREADS_ONLY)
+#define _PR_NEW_LOCK_ENV()
+#define _PR_LOCK_ENV() { PRIntn _is; _PR_INTSOFF(_is)
+#define _PR_UNLOCK_ENV() _PR_INTSON(_is); }
+#else
+static PRLock *_pr_envLock;
+#define _PR_NEW_LOCK_ENV() {_pr_envLock = PR_NewLock();}
+#define _PR_LOCK_ENV() PR_Lock(_pr_envLock)
+#define _PR_UNLOCK_ENV() PR_Unlock(_pr_envLock)
+#endif
+
+/************************************************************************/
+
+void _PR_InitEnv()
+{
+ _PR_NEW_LOCK_ENV();
+}
+
+PR_IMPLEMENT(char*) PR_GetEnv(const char *var)
+{
+ char *ev;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ _PR_LOCK_ENV();
+ ev = _PR_MD_GET_ENV(var);
+ _PR_UNLOCK_ENV();
+ return ev;
+}
diff --git a/pr/src/misc/prerror.c b/pr/src/misc/prerror.c
new file mode 100644
index 00000000..c7ea4707
--- /dev/null
+++ b/pr/src/misc/prerror.c
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+#include <memory.h>
+#include <string.h>
+#include <stdlib.h>
+
+PR_IMPLEMENT(PRErrorCode) PR_GetError()
+{
+ PRThread *thread = PR_GetCurrentThread();
+ return thread->errorCode;
+}
+
+PR_IMPLEMENT(PRInt32) PR_GetOSError()
+{
+ PRThread *thread = PR_GetCurrentThread();
+ return thread->osErrorCode;
+}
+
+PR_IMPLEMENT(const char*) PR_GetErrorString()
+{
+ PRThread *thread = PR_GetCurrentThread();
+ return thread->errorString;
+}
+
+PR_IMPLEMENT(void) PR_SetError(PRErrorCode code, PRInt32 osErr)
+{
+ PRThread *thread = PR_GetCurrentThread();
+ thread->errorCode = code;
+ thread->osErrorCode = osErr;
+ thread->errorStringSize = 0;
+ PR_DELETE(thread->errorString);
+}
+
+PR_IMPLEMENT(void) PR_SetErrorText(PRIntn textLength, const char *text)
+{
+ PRThread *thread = PR_GetCurrentThread();
+
+ if (0 == textLength)
+ {
+ if (NULL != thread->errorString)
+ PR_DELETE(thread->errorString);
+ }
+ else
+ {
+ PRIntn size = textLength + 1; /* actual length to allocate */
+ if (thread->errorStringSize < textLength) /* do we have room? */
+ {
+ if (NULL != thread->errorString)
+ PR_DELETE(thread->errorString);
+ thread->errorString = (char*)PR_MALLOC(size);
+ }
+ memcpy(thread->errorString, text, size);
+ }
+ thread->errorStringSize = textLength;
+}
+
+PR_IMPLEMENT(PRInt32) PR_GetErrorTextLength(void)
+{
+ PRThread *thread = PR_GetCurrentThread();
+ return thread->errorStringSize;
+} /* PR_GetErrorTextLength */
+
+PR_IMPLEMENT(PRInt32) PR_GetErrorText(char *text)
+{
+ PRThread *thread = PR_GetCurrentThread();
+ if (0 != thread->errorStringSize)
+ memcpy(text, thread->errorString, thread->errorStringSize + 1);
+ return thread->errorStringSize;
+} /* PR_GetErrorText */
+
+
diff --git a/pr/src/misc/prinit.c b/pr/src/misc/prinit.c
new file mode 100644
index 00000000..247aadb9
--- /dev/null
+++ b/pr/src/misc/prinit.c
@@ -0,0 +1,519 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+#include <string.h>
+
+PRLogModuleInfo *_pr_clock_lm;
+PRLogModuleInfo *_pr_cmon_lm;
+PRLogModuleInfo *_pr_io_lm;
+PRLogModuleInfo *_pr_cvar_lm;
+PRLogModuleInfo *_pr_mon_lm;
+PRLogModuleInfo *_pr_linker_lm;
+PRLogModuleInfo *_pr_sched_lm;
+PRLogModuleInfo *_pr_thread_lm;
+PRLogModuleInfo *_pr_gc_lm;
+
+PRFileDesc *_pr_stdin;
+PRFileDesc *_pr_stdout;
+PRFileDesc *_pr_stderr;
+
+#if !defined(_PR_PTHREADS)
+
+PRCList _pr_active_local_threadQ =
+ PR_INIT_STATIC_CLIST(&_pr_active_local_threadQ);
+PRCList _pr_active_global_threadQ =
+ PR_INIT_STATIC_CLIST(&_pr_active_global_threadQ);
+
+_MDLock _pr_cpuLock; /* lock for the CPU Q */
+PRCList _pr_cpuQ = PR_INIT_STATIC_CLIST(&_pr_cpuQ);
+
+PRUint32 _pr_utid;
+
+PRUintn _pr_userActive;
+PRUintn _pr_systemActive;
+PRUintn _pr_maxPTDs;
+
+#ifdef _PR_LOCAL_THREADS_ONLY
+
+struct _PRCPU *_pr_currentCPU;
+PRThread *_pr_currentThread;
+PRThread *_pr_lastThread;
+PRInt32 _pr_intsOff;
+
+#endif /* _PR_LOCAL_THREADS_ONLY */
+
+/* Lock protecting all "termination" condition variables of all threads */
+PRLock *_pr_terminationCVLock;
+
+#endif /* !defined(_PR_PTHREADS) */
+
+static void _PR_InitCallOnce(void);
+static void _PR_InitStuff(void);
+
+/************************************************************************/
+/**************************IDENTITY AND VERSIONING***********************/
+/************************************************************************/
+PR_IMPLEMENT(PRBool) PR_VersionCheck(const char *importedVersion)
+{
+ /*
+ ** This is the secret handshake algorithm. Right now it requires
+ ** an exact match. Later it should get more tricky.
+ */
+ return ((0 == strcmp(importedVersion, PR_VERSION)) ? PR_TRUE : PR_FALSE);
+} /* PR_VersionCheck */
+
+
+PRBool _pr_initialized = PR_FALSE;
+
+PR_IMPLEMENT(PRBool) PR_Initialized(void)
+{
+ return _pr_initialized;
+}
+
+static void _PR_InitStuff()
+{
+ if (_pr_initialized) return;
+ _pr_initialized = PR_TRUE;
+
+ (void) PR_GetPageSize();
+
+ _pr_clock_lm = PR_NewLogModule("clock");
+ _pr_cmon_lm = PR_NewLogModule("cmon");
+ _pr_io_lm = PR_NewLogModule("io");
+ _pr_mon_lm = PR_NewLogModule("mon");
+ _pr_linker_lm = PR_NewLogModule("linker");
+ _pr_cvar_lm = PR_NewLogModule("cvar");
+ _pr_sched_lm = PR_NewLogModule("sched");
+ _pr_thread_lm = PR_NewLogModule("thread");
+ _pr_gc_lm = PR_NewLogModule("gc");
+
+ /* NOTE: These init's cannot depend on _PR_MD_CURRENT_THREAD() */
+ _PR_MD_EARLY_INIT();
+
+ _PR_InitAtomic();
+ _PR_InitLocks();
+ _PR_InitSegs();
+ _PR_InitStacks();
+ _PR_InitTPD();
+ _PR_InitEnv();
+ _PR_InitLayerCache();
+
+ _PR_InitThreads(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+
+#ifdef WIN16
+ {
+ PRInt32 top; /* artificial top of stack, win16 */
+ _pr_top_of_task_stack = (char *) &top;
+ }
+#endif
+
+#ifndef _PR_GLOBAL_THREADS_ONLY
+ _PR_InitCPUs();
+#endif
+
+/*
+ * XXX: call _PR_InitMem only on those platforms for which nspr implements
+ * malloc, for now.
+ */
+#ifdef _PR_OVERRIDE_MALLOC
+ _PR_InitMem();
+#endif
+
+ _PR_InitCMon();
+ _PR_InitIO();
+ _PR_InitNet();
+ _PR_InitClock();
+#ifdef PR_LOGGING
+ _PR_InitLog();
+#endif
+ _PR_InitLinker();
+ _PR_InitCallOnce();
+ _PR_InitDtoa();
+
+ _PR_MD_FINAL_INIT();
+}
+
+void _PR_ImplicitInitialization()
+{
+ _PR_InitStuff();
+
+ /* Enable interrupts */
+#ifndef _PR_GLOBAL_THREADS_ONLY
+ _PR_MD_START_INTERRUPTS();
+#endif
+
+}
+
+PR_IMPLEMENT(void) PR_DisableClockInterrupts(void)
+{
+#if !defined(_PR_PTHREADS)
+ if (!_pr_initialized) {
+ _PR_InitStuff();
+ } else {
+ _PR_MD_DISABLE_CLOCK_INTERRUPTS();
+ }
+#endif
+}
+
+PR_IMPLEMENT(void) PR_BlockClockInterrupts(void)
+{
+#if !defined(_PR_PTHREADS)
+ _PR_MD_BLOCK_CLOCK_INTERRUPTS();
+#endif
+}
+
+PR_IMPLEMENT(void) PR_UnblockClockInterrupts(void)
+{
+#if !defined(_PR_PTHREADS)
+ _PR_MD_UNBLOCK_CLOCK_INTERRUPTS();
+#endif
+}
+
+PR_IMPLEMENT(void) PR_Init(
+ PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs)
+{
+#if defined(XP_MAC)
+#pragma unused (type, priority, maxPTDs)
+#endif
+
+ _PR_ImplicitInitialization();
+}
+
+PR_IMPLEMENT(PRIntn) PR_Initialize(
+ PRPrimordialFn prmain, PRIntn argc, char **argv, PRUintn maxPTDs)
+{
+#if defined(XP_MAC)
+#pragma unused (maxPTDs)
+#endif
+
+ PRIntn rv;
+ _PR_ImplicitInitialization();
+ rv = prmain(argc, argv);
+ PR_Cleanup();
+ return rv;
+} /* PR_Initialize */
+
+/*
+ *-----------------------------------------------------------------------
+ *
+ * _PR_CleanupBeforeExit --
+ *
+ * Perform the cleanup work before exiting the process.
+ * We first do the cleanup generic to all platforms. Then
+ * we call _PR_MD_CLEANUP_BEFORE_EXIT(), where platform-dependent
+ * cleanup is done. This function is used by PR_Cleanup().
+ *
+ * See also: PR_Cleanup().
+ *
+ *-----------------------------------------------------------------------
+ */
+#if defined(_PR_PTHREADS)
+ /* see ptthread.c */
+#else
+static void
+_PR_CleanupBeforeExit(void)
+{
+/*
+Do not make any calls here other than to destroy resources. For example,
+do not make any calls that eventually may end up in PR_Lock. Because the
+thread is destroyed, can not access current thread any more.
+*/
+ _PR_CleanupTPD();
+ if (_pr_terminationCVLock)
+ /*
+ * In light of the comment above, this looks real suspicious.
+ * I'd go so far as to say it's just a problem waiting to happen.
+ */
+ PR_DestroyLock(_pr_terminationCVLock);
+
+ _PR_MD_CLEANUP_BEFORE_EXIT();
+}
+#endif /* defined(_PR_PTHREADS) */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PR_Cleanup --
+ *
+ * Perform a graceful shutdown of the NSPR runtime. PR_Cleanup() may
+ * only be called from the primordial thread, typically at the
+ * end of the main() function. It returns when it has completed
+ * its platform-dependent duty and the process must not make any other
+ * NSPR library calls prior to exiting from main().
+ *
+ * PR_Cleanup() first blocks the primordial thread until all the
+ * other user (non-system) threads, if any, have terminated.
+ * Then it performs cleanup in preparation for exiting the process.
+ * PR_Cleanup() does not exit the primordial thread (which would
+ * in turn exit the process).
+ *
+ * PR_Cleanup() only responds when it is called by the primordial
+ * thread. Calls by any other thread are silently ignored.
+ *
+ * See also: PR_ExitProcess()
+ *
+ *----------------------------------------------------------------------
+ */
+#if defined(_PR_PTHREADS)
+ /* see ptthread.c */
+#else
+
+PR_IMPLEMENT(PRStatus) PR_Cleanup()
+{
+ PRThread *me = PR_GetCurrentThread();
+ PR_ASSERT((NULL != me) && (me->flags & _PR_PRIMORDIAL));
+ if ((NULL != me) && (me->flags & _PR_PRIMORDIAL))
+ {
+
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("PR_Cleanup: shutting down NSPR"));
+
+ /*
+ * No more recycling of threads
+ */
+ _pr_recycleThreads = 0;
+
+ /*
+ * Wait for all other user (non-system/daemon) threads
+ * to terminate.
+ */
+ PR_Lock(_pr_activeLock);
+ while (_pr_userActive > _pr_primordialExitCount) {
+ PR_WaitCondVar(_pr_primordialExitCVar, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(_pr_activeLock);
+
+#if defined(WIN16)
+ _PR_ShutdownLinker();
+#endif
+ /* Release the primordial thread's private data, etc. */
+ _PR_CleanupThread(me);
+
+ _PR_MD_STOP_INTERRUPTS();
+
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("PR_Cleanup: clean up before destroying thread"));
+ #ifdef PR_LOGGING
+ _PR_LogCleanup();
+ #endif
+
+ /*
+ * This part should look like the end of _PR_NativeRunThread
+ * and _PR_UserRunThread.
+ */
+ if (_PR_IS_NATIVE_THREAD(me)) {
+ _PR_MD_EXIT_THREAD(me);
+ _PR_NativeDestroyThread(me);
+ } else {
+ _PR_UserDestroyThread(me);
+ PR_DELETE(me->stack);
+ PR_DELETE(me);
+ }
+
+ /*
+ * XXX: We are freeing the heap memory here so that Purify won't
+ * complain, but we should also free other kinds of resources
+ * that are allocated by the _PR_InitXXX() functions.
+ * Ideally, for each _PR_InitXXX(), there should be a corresponding
+ * _PR_XXXCleanup() that we can call here.
+ */
+ _PR_CleanupBeforeExit();
+ return PR_SUCCESS;
+ }
+ return PR_FAILURE;
+}
+#endif /* defined(_PR_PTHREADS) */
+
+/*
+ *------------------------------------------------------------------------
+ * PR_ProcessExit --
+ *
+ * Cause an immediate, nongraceful, forced termination of the process.
+ * It takes a PRIntn argument, which is the exit status code of the
+ * process.
+ *
+ * See also: PR_Cleanup()
+ *
+ *------------------------------------------------------------------------
+ */
+
+#if defined(_PR_PTHREADS)
+ /* see ptthread.c */
+#else
+PR_IMPLEMENT(void) PR_ProcessExit(PRIntn status)
+{
+ _PR_MD_EXIT(status);
+}
+
+#endif /* defined(_PR_PTHREADS) */
+
+PR_IMPLEMENT(PRProcessAttr *)
+PR_NewProcessAttr(void)
+{
+ PRProcessAttr *attr;
+
+ attr = PR_NEWZAP(PRProcessAttr);
+ if (!attr) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ }
+ return attr;
+}
+
+PR_IMPLEMENT(void)
+PR_ResetProcessAttr(PRProcessAttr *attr)
+{
+ memset(attr, 0, sizeof(*attr));
+}
+
+PR_IMPLEMENT(void)
+PR_DestroyProcessAttr(PRProcessAttr *attr)
+{
+ PR_DELETE(attr);
+}
+
+PR_IMPLEMENT(void)
+PR_SetStdioRedirect(
+ PRProcessAttr *attr,
+ PRSpecialFD stdioFd,
+ PRFileDesc *redirectFd
+)
+{
+ switch (stdioFd) {
+ case PR_StandardInput:
+ attr->stdinFd = redirectFd;
+ break;
+ case PR_StandardOutput:
+ attr->stdoutFd = redirectFd;
+ break;
+ case PR_StandardError:
+ attr->stderrFd = redirectFd;
+ break;
+ default:
+ PR_ASSERT(0);
+ }
+}
+
+PR_IMPLEMENT(PRProcess*) PR_CreateProcess(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr)
+{
+ return _PR_MD_CREATE_PROCESS(path, argv, envp, attr);
+} /* PR_CreateProcess */
+
+PR_IMPLEMENT(PRStatus) PR_CreateProcessDetached(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr)
+{
+ PRProcess *process;
+ PRStatus rv;
+
+ process = PR_CreateProcess(path, argv, envp, attr);
+ if (NULL == process) {
+ return PR_FAILURE;
+ }
+ rv = PR_DetachProcess(process);
+ PR_ASSERT(PR_SUCCESS == rv);
+ if (rv == PR_FAILURE) {
+ PR_DELETE(process);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus) PR_DetachProcess(PRProcess *process)
+{
+ return _PR_MD_DETACH_PROCESS(process);
+}
+
+PR_IMPLEMENT(PRStatus) PR_WaitProcess(PRProcess *process, PRInt32 *exitCode)
+{
+ return _PR_MD_WAIT_PROCESS(process, exitCode);
+} /* PR_WaitProcess */
+
+PR_IMPLEMENT(PRStatus) PR_KillProcess(PRProcess *process)
+{
+ return _PR_MD_KILL_PROCESS(process);
+}
+
+/*
+ ********************************************************************
+ *
+ * Module initialization
+ *
+ ********************************************************************
+ */
+
+static struct {
+ PRLock *ml;
+ PRCondVar *cv;
+} mod_init;
+
+static void _PR_InitCallOnce() {
+ mod_init.ml = PR_NewLock();
+ PR_ASSERT(NULL != mod_init.ml);
+ mod_init.cv = PR_NewCondVar(mod_init.ml);
+ PR_ASSERT(NULL != mod_init.cv);
+}
+
+
+PR_IMPLEMENT(PRStatus) PR_CallOnce(
+ PRCallOnceType *once,
+ PRCallOnceFN func)
+{
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ if (!once->initialized) {
+ if (PR_AtomicSet(&once->inProgress, 1) == 0) {
+ once->status = (*func)();
+ PR_Lock(mod_init.ml);
+ once->initialized = 1;
+ PR_NotifyAllCondVar(mod_init.cv);
+ PR_Unlock(mod_init.ml);
+ } else {
+ PR_Lock(mod_init.ml);
+ while (!once->initialized) {
+ PR_WaitCondVar(mod_init.cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(mod_init.ml);
+ }
+ }
+ return once->status;
+}
+
+PRBool _PR_Obsolete(const char *obsolete, const char *preferred)
+{
+#if defined(DEBUG)
+#ifndef XP_MAC
+ PR_fprintf(
+ PR_STDERR, "'%s' is obsolete. Use '%s' instead.\n",
+ obsolete, (NULL == preferred) ? "something else" : preferred);
+ return PR_FALSE;
+#else
+#pragma unused (obsolete, preferred)
+ return PR_FALSE;
+#endif
+#endif
+} /* _PR_Obsolete */
+
+/* prinit.c */
+
+
diff --git a/pr/src/misc/prinrval.c b/pr/src/misc/prinrval.c
new file mode 100644
index 00000000..2fe46a76
--- /dev/null
+++ b/pr/src/misc/prinrval.c
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * file: prinrval.c
+ * description: implementation for the kernel interval timing functions
+ */
+
+#include "primpl.h"
+
+/*
+ *-----------------------------------------------------------------------
+ *
+ * _PR_InitClock --
+ *
+ *
+ *-----------------------------------------------------------------------
+ */
+
+void _PR_InitClock(void)
+{
+ _PR_MD_INTERVAL_INIT();
+}
+
+/*
+ * This version of interval times is based on the time of day
+ * capability offered by system. This isn't valid for two reasons:
+ * 1) The time of day is neither linear nor montonically increasing
+ * 2) The units here are milliseconds. That's not appropriate for our use.
+ */
+
+PR_IMPLEMENT(PRIntervalTime) PR_IntervalNow()
+{
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+ return _PR_MD_GET_INTERVAL();
+} /* PR_IntervalNow */
+
+PR_EXTERN(PRUint32) PR_TicksPerSecond()
+{
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+ return _PR_MD_INTERVAL_PER_SEC();
+} /* PR_TicksPerSecond */
+
+PR_IMPLEMENT(PRIntervalTime) PR_SecondsToInterval(PRUint32 seconds)
+{
+ return seconds * PR_TicksPerSecond();
+} /* PR_SecondsToInterval */
+
+PR_IMPLEMENT(PRIntervalTime) PR_MillisecondsToInterval(PRUint32 milli)
+{
+ PRIntervalTime ticks;
+ PRUint64 tock, tps, msecPerSec, rounding;
+ LL_UI2L(tock, milli);
+ LL_I2L(msecPerSec, PR_MSEC_PER_SEC);
+ LL_I2L(rounding, (PR_MSEC_PER_SEC >> 1));
+ LL_I2L(tps, PR_TicksPerSecond());
+ LL_MUL(tock, tock, tps);
+ LL_ADD(tock, tock, rounding);
+ LL_DIV(tock, tock, msecPerSec);
+ LL_L2UI(ticks, tock);
+ return ticks;
+} /* PR_MillisecondsToInterval */
+
+PR_IMPLEMENT(PRIntervalTime) PR_MicrosecondsToInterval(PRUint32 micro)
+{
+ PRIntervalTime ticks;
+ PRUint64 tock, tps, usecPerSec, rounding;
+ LL_UI2L(tock, micro);
+ LL_I2L(usecPerSec, PR_USEC_PER_SEC);
+ LL_I2L(rounding, (PR_USEC_PER_SEC >> 1));
+ LL_I2L(tps, PR_TicksPerSecond());
+ LL_MUL(tock, tock, tps);
+ LL_ADD(tock, tock, rounding);
+ LL_DIV(tock, tock, usecPerSec);
+ LL_L2UI(ticks, tock);
+ return ticks;
+} /* PR_MicrosecondsToInterval */
+
+PR_IMPLEMENT(PRUint32) PR_IntervalToSeconds(PRIntervalTime ticks)
+{
+ return ticks / PR_TicksPerSecond();
+} /* PR_IntervalToSeconds */
+
+PR_IMPLEMENT(PRUint32) PR_IntervalToMilliseconds(PRIntervalTime ticks)
+{
+ PRUint32 milli;
+ PRUint64 tock, tps, msecPerSec, rounding;
+ LL_UI2L(tock, ticks);
+ LL_I2L(msecPerSec, PR_MSEC_PER_SEC);
+ LL_I2L(tps, PR_TicksPerSecond());
+ LL_USHR(rounding, tps, 1);
+ LL_MUL(tock, tock, msecPerSec);
+ LL_ADD(tock, tock, rounding);
+ LL_DIV(tock, tock, tps);
+ LL_L2UI(milli, tock);
+ return milli;
+} /* PR_IntervalToMilliseconds */
+
+PR_IMPLEMENT(PRUint32) PR_IntervalToMicroseconds(PRIntervalTime ticks)
+{
+ PRUint32 micro;
+ PRUint64 tock, tps, usecPerSec, rounding;
+ LL_UI2L(tock, ticks);
+ LL_I2L(usecPerSec, PR_USEC_PER_SEC);
+ LL_I2L(tps, PR_TicksPerSecond());
+ LL_USHR(rounding, tps, 1);
+ LL_MUL(tock, tock, usecPerSec);
+ LL_ADD(tock, tock, rounding);
+ LL_DIV(tock, tock, tps);
+ LL_L2UI(micro, tock);
+ return micro;
+} /* PR_IntervalToMicroseconds */
+
+/* prinrval.c */
+
diff --git a/pr/src/misc/prlog2.c b/pr/src/misc/prlog2.c
new file mode 100644
index 00000000..e733c7b3
--- /dev/null
+++ b/pr/src/misc/prlog2.c
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "prbit.h"
+
+/*
+** Compute the log of the least power of 2 greater than or equal to n
+*/
+PR_IMPLEMENT(PRIntn) PR_CeilingLog2(PRUint32 n)
+{
+ PRIntn log2 = 0;
+
+ if (n & (n-1))
+ log2++;
+ if (n >> 16)
+ log2 += 16, n >>= 16;
+ if (n >> 8)
+ log2 += 8, n >>= 8;
+ if (n >> 4)
+ log2 += 4, n >>= 4;
+ if (n >> 2)
+ log2 += 2, n >>= 2;
+ if (n >> 1)
+ log2++;
+ return log2;
+}
+
+/*
+** Compute the log of the greatest power of 2 less than or equal to n.
+** This really just finds the highest set bit in the word.
+*/
+PR_IMPLEMENT(PRIntn) PR_FloorLog2(PRUint32 n)
+{
+ PRIntn log2 = 0;
+
+ if (n >> 16)
+ log2 += 16, n >>= 16;
+ if (n >> 8)
+ log2 += 8, n >>= 8;
+ if (n >> 4)
+ log2 += 4, n >>= 4;
+ if (n >> 2)
+ log2 += 2, n >>= 2;
+ if (n >> 1)
+ log2++;
+ return log2;
+}
diff --git a/pr/src/misc/prlong.c b/pr/src/misc/prlong.c
new file mode 100644
index 00000000..85e200c1
--- /dev/null
+++ b/pr/src/misc/prlong.c
@@ -0,0 +1,259 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+static PRInt64 ll_zero = LL_INIT( 0x00000000,0x00000000 );
+static PRInt64 ll_maxint = LL_INIT( 0x7fffffff, 0xffffffff );
+static PRInt64 ll_minint = LL_INIT( 0x80000000, 0x00000000 );
+
+#if defined(HAVE_WATCOM_BUG_2)
+PRInt64 __pascal __loadds __export
+ LL_Zero(void) { return ll_zero; }
+PRInt64 __pascal __loadds __export
+ LL_MaxInt(void) { return ll_maxint; }
+PRInt64 __pascal __loadds __export
+ LL_MinInt(void) { return ll_minint; }
+#else
+PR_IMPLEMENT(PRInt64) LL_Zero(void) { return ll_zero; }
+PR_IMPLEMENT(PRInt64) LL_MaxInt(void) { return ll_maxint; }
+PR_IMPLEMENT(PRInt64) LL_MinInt(void) { return ll_minint; }
+#endif
+
+#ifndef HAVE_LONG_LONG
+/*
+** Divide 64-bit a by 32-bit b, which must be normalized so its high bit is 1.
+*/
+static void norm_udivmod32(PRUint32 *qp, PRUint32 *rp, PRUint64 a, PRUint32 b)
+{
+ PRUint32 d1, d0, q1, q0;
+ PRUint32 r1, r0, m;
+
+ d1 = _hi16(b);
+ d0 = _lo16(b);
+ r1 = a.hi % d1;
+ q1 = a.hi / d1;
+ m = q1 * d0;
+ r1 = (r1 << 16) | _hi16(a.lo);
+ if (r1 < m) {
+ q1--, r1 += b;
+ if (r1 >= b /* i.e., we didn't get a carry when adding to r1 */
+ && r1 < m) {
+ q1--, r1 += b;
+ }
+ }
+ r1 -= m;
+ r0 = r1 % d1;
+ q0 = r1 / d1;
+ m = q0 * d0;
+ r0 = (r0 << 16) | _lo16(a.lo);
+ if (r0 < m) {
+ q0--, r0 += b;
+ if (r0 >= b
+ && r0 < m) {
+ q0--, r0 += b;
+ }
+ }
+ *qp = (q1 << 16) | q0;
+ *rp = r0 - m;
+}
+
+static PRUint32 CountLeadingZeros(PRUint32 a)
+{
+ PRUint32 t;
+ PRUint32 r = 32;
+
+ if ((t = a >> 16) != 0)
+ r -= 16, a = t;
+ if ((t = a >> 8) != 0)
+ r -= 8, a = t;
+ if ((t = a >> 4) != 0)
+ r -= 4, a = t;
+ if ((t = a >> 2) != 0)
+ r -= 2, a = t;
+ if ((t = a >> 1) != 0)
+ r -= 1, a = t;
+ if (a & 1)
+ r--;
+ return r;
+}
+
+PR_IMPLEMENT(void) ll_udivmod(PRUint64 *qp, PRUint64 *rp, PRUint64 a, PRUint64 b)
+{
+ PRUint32 n0, n1, n2;
+ PRUint32 q0, q1;
+ PRUint32 rsh, lsh;
+
+ n0 = a.lo;
+ n1 = a.hi;
+
+ if (b.hi == 0) {
+ if (b.lo > n1) {
+ /* (0 q0) = (n1 n0) / (0 D0) */
+
+ lsh = CountLeadingZeros(b.lo);
+
+ if (lsh) {
+ /*
+ * Normalize, i.e. make the most significant bit of the
+ * denominator be set.
+ */
+ b.lo = b.lo << lsh;
+ n1 = (n1 << lsh) | (n0 >> (32 - lsh));
+ n0 = n0 << lsh;
+ }
+
+ a.lo = n0, a.hi = n1;
+ norm_udivmod32(&q0, &n0, a, b.lo);
+ q1 = 0;
+
+ /* remainder is in n0 >> lsh */
+ } else {
+ /* (q1 q0) = (n1 n0) / (0 d0) */
+
+ if (b.lo == 0) /* user wants to divide by zero! */
+ b.lo = 1 / b.lo; /* so go ahead and crash */
+
+ lsh = CountLeadingZeros(b.lo);
+
+ if (lsh == 0) {
+ /*
+ * From (n1 >= b.lo)
+ * && (the most significant bit of b.lo is set),
+ * conclude that
+ * (the most significant bit of n1 is set)
+ * && (the leading quotient digit q1 = 1).
+ *
+ * This special case is necessary, not an optimization
+ * (Shifts counts of 32 are undefined).
+ */
+ n1 -= b.lo;
+ q1 = 1;
+ } else {
+ /*
+ * Normalize.
+ */
+ rsh = 32 - lsh;
+
+ b.lo = b.lo << lsh;
+ n2 = n1 >> rsh;
+ n1 = (n1 << lsh) | (n0 >> rsh);
+ n0 = n0 << lsh;
+
+ a.lo = n1, a.hi = n2;
+ norm_udivmod32(&q1, &n1, a, b.lo);
+ }
+
+ /* n1 != b.lo... */
+
+ a.lo = n0, a.hi = n1;
+ norm_udivmod32(&q0, &n0, a, b.lo);
+
+ /* remainder in n0 >> lsh */
+ }
+
+ if (rp) {
+ rp->lo = n0 >> lsh;
+ rp->hi = 0;
+ }
+ } else {
+ if (b.hi > n1) {
+ /* (0 0) = (n1 n0) / (D1 d0) */
+
+ q0 = 0;
+ q1 = 0;
+
+ /* remainder in (n1 n0) */
+ if (rp) {
+ rp->lo = n0;
+ rp->hi = n1;
+ }
+ } else {
+ /* (0 q0) = (n1 n0) / (d1 d0) */
+
+ lsh = CountLeadingZeros(b.hi);
+ if (lsh == 0) {
+ /*
+ * From (n1 >= b.hi)
+ * && (the most significant bit of b.hi is set),
+ * conclude that
+ * (the most significant bit of n1 is set)
+ * && (the quotient digit q0 = 0 or 1).
+ *
+ * This special case is necessary, not an optimization.
+ */
+
+ /*
+ * The condition on the next line takes advantage of that
+ * n1 >= b.hi (true due to control flow).
+ */
+ if (n1 > b.hi || n0 >= b.lo) {
+ q0 = 1;
+ a.lo = n0, a.hi = n1;
+ LL_SUB(a, a, b);
+ } else {
+ q0 = 0;
+ }
+ q1 = 0;
+
+ if (rp) {
+ rp->lo = n0;
+ rp->hi = n1;
+ }
+ } else {
+ PRInt64 m;
+
+ /*
+ * Normalize.
+ */
+ rsh = 32 - lsh;
+
+ b.hi = (b.hi << lsh) | (b.lo >> rsh);
+ b.lo = b.lo << lsh;
+ n2 = n1 >> rsh;
+ n1 = (n1 << lsh) | (n0 >> rsh);
+ n0 = n0 << lsh;
+
+ a.lo = n1, a.hi = n2;
+ norm_udivmod32(&q0, &n1, a, b.hi);
+ LL_MUL32(m, q0, b.lo);
+
+ if ((m.hi > n1) || ((m.hi == n1) && (m.lo > n0))) {
+ q0--;
+ LL_SUB(m, m, b);
+ }
+
+ q1 = 0;
+
+ /* Remainder is ((n1 n0) - (m1 m0)) >> lsh */
+ if (rp) {
+ a.lo = n0, a.hi = n1;
+ LL_SUB(a, a, m);
+ rp->lo = (a.hi << rsh) | (a.lo >> lsh);
+ rp->hi = a.hi >> lsh;
+ }
+ }
+ }
+ }
+
+ if (qp) {
+ qp->lo = q0;
+ qp->hi = q1;
+ }
+}
+#endif /* !HAVE_LONG_LONG */
diff --git a/pr/src/misc/prnetdb.c b/pr/src/misc/prnetdb.c
new file mode 100644
index 00000000..c1728171
--- /dev/null
+++ b/pr/src/misc/prnetdb.c
@@ -0,0 +1,830 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+#include <string.h>
+
+/*
+ * On Unix, the error code for gethostbyname() and gethostbyaddr()
+ * is returned in the global variable h_errno, instead of the usual
+ * errno.
+ */
+#if defined(XP_UNIX)
+#if defined(_PR_NEED_H_ERRNO)
+extern int h_errno;
+#endif
+#define _MD_GETHOST_ERRNO() h_errno
+#else
+#define _MD_GETHOST_ERRNO() _MD_ERRNO()
+#endif
+
+#if defined(_PR_NO_PREEMPT)
+#define LOCK_DNS()
+#define UNLOCK_DNS()
+#else
+PRLock *_pr_dnsLock = NULL;
+#define LOCK_DNS() PR_Lock(_pr_dnsLock)
+#define UNLOCK_DNS() PR_Unlock(_pr_dnsLock)
+#endif /* defined(_PR_NO_PREEMPT) */
+
+#if defined(XP_UNIX)
+#include <signal.h>
+
+/*
+** Unix's, as a rule, have a bug in their select code: if a timer
+** interrupt occurs and you have SA_RESTART set on your signal, select
+** forgets how much time has elapsed and restarts the system call from
+** the beginning. This can cause a call to select to *never* time out.
+**
+** Because we aren't certain that select is wrapped properly in this code
+** we disable the clock while a dns operation is occuring. This sucks and
+** can be tossed when implement our own dns code that calls our own
+** PR_Poll.
+*/
+
+static sigset_t timer_set;
+#define DISABLECLOCK(_set) sigprocmask(SIG_BLOCK, &timer_set, _set)
+#define ENABLECLOCK(_set) sigprocmask(SIG_SETMASK, _set, 0)
+
+#endif /* XP_UNIX */
+
+/*
+ * Some platforms have the reentrant getprotobyname_r() and
+ * getprotobynumber_r(). However, they come in two flavors.
+ * Some return a pointer to struct protoent, others return
+ * an int.
+ */
+
+#if defined(SOLARIS) \
+ || (defined(LINUX2_0) && defined(_REENTRANT) \
+ && !(defined(__GLIBC__) && __GLIBC__ >= 2))
+#define _PR_HAVE_GETPROTO_R
+#define _PR_HAVE_GETPROTO_R_POINTER
+#endif
+
+#if defined(OSF1) || (defined(AIX) && defined(_THREAD_SAFE)) \
+ || (defined(HPUX10_10) && defined(_REENTRANT)) \
+ || (defined(HPUX10_20) && defined(_REENTRANT))
+#define _PR_HAVE_GETPROTO_R
+#define _PR_HAVE_GETPROTO_R_INT
+#endif
+
+#if (defined(LINUX2_0) && defined(__GLIBC__) && __GLIBC__ >= 2)
+#define _PR_HAVE_GETPROTO_R
+#define _PR_HAVE_5_ARG_GETPROTO_R
+#endif
+
+#if !defined(_PR_HAVE_GETPROTO_R)
+PRLock* _getproto_lock = NULL;
+#endif
+
+#if defined(_PR_INET6)
+PRBool _pr_ipv6_enabled = PR_FALSE;
+#if defined(AIX)
+const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
+const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
+#else
+extern const struct in6_addr in6addr_any;
+extern const struct in6_addr in6addr_loopback;
+#endif /* AIX */
+#endif /* _PR_INET6 */
+
+void _PR_InitNet(void)
+{
+#if defined(XP_UNIX)
+#ifdef HAVE_NETCONFIG
+ /*
+ * This one-liner prevents the endless re-open's and re-read's of
+ * /etc/netconfig on EACH and EVERY call to accept(), connect(), etc.
+ */
+ (void)setnetconfig();
+#endif
+ sigemptyset(&timer_set);
+ sigaddset(&timer_set, SIGALRM);
+#endif
+#if !defined(_PR_NO_PREEMPT)
+ _pr_dnsLock = PR_NewLock();
+#endif
+#if !defined(_PR_HAVE_GETPROTO_R)
+ _getproto_lock = PR_NewLock();
+#endif
+
+}
+
+PR_IMPLEMENT(PRStatus) PR_SetIPv6Enable(PRBool itIs)
+{
+#if defined(XP_MAC)
+#pragma unused (itIs)
+#endif
+
+#if defined(_PR_INET6)
+ _pr_ipv6_enabled = itIs;
+ return PR_SUCCESS;
+#else /* defined(_PR_INET6) */
+ PR_SetError(PR_PROTOCOL_NOT_SUPPORTED_ERROR, 0);
+ return PR_FAILURE;
+#endif /* defined(_PR_INET6) */
+} /* PR_SetIPv6Enable */
+
+PR_IMPLEMENT(PRStatus) PR_GetHostName(char *name, PRUint32 namelen)
+{
+#if defined(DEBUG)
+ static PRBool warn = PR_TRUE;
+ if (warn) warn = _PR_Obsolete("PR_GetHostName()", "PR_GetSystemInfo()");
+#endif
+ return PR_GetSystemInfo(PR_SI_HOSTNAME, name, namelen);
+}
+
+/*
+** Allocate space from the buffer, aligning it to "align" before doing
+** the allocation. "align" must be a power of 2.
+*/
+static char *Alloc(PRIntn amount, char **bufp, PRIntn *buflenp, PRIntn align)
+{
+ char *buf = *bufp;
+ PRIntn buflen = *buflenp;
+
+ if (align && ((long)buf & (align - 1))) {
+ PRIntn skip = align - ((ptrdiff_t)buf & (align - 1));
+ if (buflen < skip) {
+ return 0;
+ }
+ buf += skip;
+ buflen -= skip;
+ }
+ if (buflen < amount) {
+ return 0;
+ }
+ *bufp = buf + amount;
+ *buflenp = buflen - amount;
+ return buf;
+}
+
+/*
+** Copy a hostent, and all of the memory that it refers to into
+** (hopefully) stacked buffers.
+*/
+static PRStatus CopyHostent(
+ struct hostent *from, char *buf, PRIntn bufsize, PRHostEnt *to)
+{
+ PRIntn len, na;
+ char **ap;
+
+ /* Do the easy stuff */
+ to->h_addrtype = from->h_addrtype;
+ to->h_length = from->h_length;
+
+ /* Copy the official name */
+ if (!from->h_name) return PR_FAILURE;
+ len = strlen(from->h_name) + 1;
+ to->h_name = Alloc(len, &buf, &bufsize, 0);
+ if (!to->h_name) return PR_FAILURE;
+ memcpy(to->h_name, from->h_name, len);
+
+ /* Count the aliases, then allocate storage for the pointers */
+ for (na = 1, ap = from->h_aliases; *ap != 0; na++, ap++){;} /* nothing to execute */
+ to->h_aliases = (char**)Alloc(
+ na * sizeof(char*), &buf, &bufsize, sizeof(char**));
+ if (!to->h_aliases) return PR_FAILURE;
+
+ /* Copy the aliases, one at a time */
+ for (na = 0, ap = from->h_aliases; *ap != 0; na++, ap++) {
+ len = strlen(*ap) + 1;
+ to->h_aliases[na] = Alloc(len, &buf, &bufsize, 0);
+ if (!to->h_aliases[na]) return PR_FAILURE;
+ memcpy(to->h_aliases[na], *ap, len);
+ }
+ to->h_aliases[na] = 0;
+
+ /* Count the addresses, then allocate storage for the pointers */
+ for (na = 1, ap = from->h_addr_list; *ap != 0; na++, ap++){;} /* nothing to execute */
+ to->h_addr_list = (char**)Alloc(
+ na * sizeof(char*), &buf, &bufsize, sizeof(char**));
+ if (!to->h_addr_list) return PR_FAILURE;
+
+ /* Copy the addresses, one at a time */
+ for (na = 0, ap = from->h_addr_list; *ap != 0; na++, ap++) {
+ to->h_addr_list[na] = Alloc(to->h_length, &buf, &bufsize, 0);
+ if (!to->h_addr_list[na]) return PR_FAILURE;
+ memcpy(to->h_addr_list[na], *ap, to->h_length);
+ }
+ to->h_addr_list[na] = 0;
+ return PR_SUCCESS;
+}
+
+#if !defined(_PR_HAVE_GETPROTO_R)
+/*
+** Copy a protoent, and all of the memory that it refers to into
+** (hopefully) stacked buffers.
+*/
+static PRStatus CopyProtoent(
+ struct protoent *from, char *buf, PRIntn bufsize, PRProtoEnt *to)
+{
+ PRIntn len, na;
+ char **ap;
+
+ /* Do the easy stuff */
+ to->p_num = from->p_proto;
+
+ /* Copy the official name */
+ if (!from->p_name) return PR_FAILURE;
+ len = strlen(from->p_name) + 1;
+ to->p_name = Alloc(len, &buf, &bufsize, 0);
+ if (!to->p_name) return PR_FAILURE;
+ memcpy(to->p_name, from->p_name, len);
+
+ /* Count the aliases, then allocate storage for the pointers */
+ for (na = 1, ap = from->p_aliases; *ap != 0; na++, ap++){;} /* nothing to execute */
+ to->p_aliases = (char**)Alloc(
+ na * sizeof(char*), &buf, &bufsize, sizeof(char**));
+ if (!to->p_aliases) return PR_FAILURE;
+
+ /* Copy the aliases, one at a time */
+ for (na = 0, ap = from->p_aliases; *ap != 0; na++, ap++) {
+ len = strlen(*ap) + 1;
+ to->p_aliases[na] = Alloc(len, &buf, &bufsize, 0);
+ if (!to->p_aliases[na]) return PR_FAILURE;
+ memcpy(to->p_aliases[na], *ap, len);
+ }
+ to->p_aliases[na] = 0;
+
+ return PR_SUCCESS;
+}
+#endif /* !defined(_PR_HAVE_GETPROTO_R) */
+
+PR_IMPLEMENT(PRStatus) PR_GetHostByName(
+ const char *name, char *buf, PRIntn bufsize, PRHostEnt *hp)
+{
+ struct hostent *h;
+ PRStatus rv = PR_FAILURE;
+#ifdef XP_UNIX
+ sigset_t oldset;
+#endif
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+#ifdef XP_UNIX
+ DISABLECLOCK(&oldset);
+#endif
+ LOCK_DNS();
+
+#ifdef _PR_INET6
+ if (_pr_ipv6_enabled)
+ {
+ h = gethostbyname2(name, AF_INET6);
+ if (NULL == h)
+ {
+ h = gethostbyname2(name, AF_INET);
+ }
+ }
+ else
+ {
+ h = gethostbyname(name);
+ }
+#else
+ h = gethostbyname(name);
+#endif /* _PR_INET6 */
+
+ if (NULL == h)
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO());
+ else
+ {
+ rv = CopyHostent(h, buf, bufsize, hp);
+ if (PR_SUCCESS != rv)
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ }
+ UNLOCK_DNS();
+#ifdef XP_UNIX
+ ENABLECLOCK(&oldset);
+#endif
+ return rv;
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetHostByAddr(
+ const PRNetAddr *hostaddr, char *buf, PRIntn bufsize, PRHostEnt *hostentry)
+{
+ struct hostent *h;
+ PRStatus rv = PR_FAILURE;
+ const void *addr;
+ int addrlen;
+#ifdef XP_UNIX
+ sigset_t oldset;
+#endif
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+#ifdef XP_UNIX
+ DISABLECLOCK(&oldset);
+#endif
+ LOCK_DNS();
+#if defined(_PR_INET6)
+ if (hostaddr->raw.family == AF_INET6)
+ {
+ addr = &hostaddr->ipv6.ip;
+ addrlen = sizeof(hostaddr->ipv6.ip);
+ }
+ else
+#endif /* defined(_PR_INET6) */
+ {
+ PR_ASSERT(hostaddr->raw.family == AF_INET);
+ addr = &hostaddr->inet.ip;
+ addrlen = sizeof(hostaddr->inet.ip);
+ }
+
+ h = gethostbyaddr(addr, addrlen, hostaddr->raw.family);
+ if (NULL == h) PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO());
+ else
+ {
+ rv = CopyHostent(h, buf, bufsize, hostentry);
+ if (PR_SUCCESS != rv) {
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ }
+ }
+ UNLOCK_DNS();
+#ifdef XP_UNIX
+ ENABLECLOCK(&oldset);
+#endif
+ return rv;
+}
+
+/******************************************************************************/
+/*
+ * Some systems define a reentrant version of getprotobyname(). Too bad
+ * the signature isn't always the same. But hey, they tried. If there
+ * is such a definition, use it. Otherwise, grab a lock and do it here.
+ */
+/******************************************************************************/
+
+#if !defined(_PR_HAVE_GETPROTO_R)
+/*
+ * This may seem like a silly thing to do, but the compiler SHOULD
+ * complain if getprotobyname_r() is implemented on some system and
+ * we're not using it. For sure these signatures are different than
+ * any usable implementation.
+ */
+
+static struct protoent *getprotobyname_r(const char* name)
+{
+ return getprotobyname(name);
+} /* getprotobyname_r */
+
+static struct protoent *getprotobynumber_r(PRInt32 number)
+{
+ return getprotobynumber(number);
+} /* getprotobynumber_r */
+
+#endif /* !defined(_PR_HAVE_GETPROTO_R) */
+
+PR_IMPLEMENT(PRStatus) PR_GetProtoByName(
+ const char* name, char* buffer, PRInt32 buflen, PRProtoEnt* result)
+{
+ PRStatus rv = PR_SUCCESS;
+ struct protoent* res = (struct protoent*)result;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+#if defined(_PR_HAVE_GETPROTO_R_INT)
+ {
+ /*
+ ** The protoent_data has a pointer as the first field.
+ ** That implies the buffer better be aligned, and char*
+ ** doesn't promise much.
+ */
+ PRUptrdiff aligned = (PRUptrdiff)buffer;
+ if (0 != (aligned & (sizeof(struct protoent_data*) - 1)))
+ {
+ aligned += sizeof(struct protoent_data*) - 1;
+ aligned &= ~(sizeof(struct protoent_data*) - 1);
+ buflen -= (aligned - (PRUptrdiff)buffer);
+ buffer = (char*)aligned;
+ }
+ }
+#endif /* defined(_PR_HAVE_GETPROTO_R_INT) */
+
+ PR_ASSERT(PR_NETDB_BUF_SIZE <= buflen);
+ if (PR_NETDB_BUF_SIZE > buflen)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+#if defined(_PR_HAVE_GETPROTO_R_POINTER)
+ if (NULL == getprotobyname_r(name, res, buffer, buflen))
+ {
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO());
+ return PR_FAILURE;
+ }
+#elif defined(_PR_HAVE_GETPROTO_R_INT)
+ /*
+ ** The buffer needs to be zero'd, and it should be
+ ** at least the size of a struct protoent_data.
+ */
+ memset(buffer, 0, buflen);
+ if (-1 == getprotobyname_r(name, res, (struct protoent_data*)buffer))
+ {
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO());
+ return PR_FAILURE;
+ }
+#elif defined(_PR_HAVE_5_ARG_GETPROTO_R)
+ /* The 5th argument for getprotobyname_r() cannot be NULL */
+ if (-1 == getprotobyname_r(name, res, buffer, buflen, &res))
+ {
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO());
+ return PR_FAILURE;
+ }
+#else /* do it the hard way */
+ {
+ struct protoent *staticBuf;
+ PR_Lock(_getproto_lock);
+ staticBuf = getprotobyname_r(name);
+ if (NULL == staticBuf)
+ {
+ rv = PR_FAILURE;
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO());
+ }
+ else
+ {
+ rv = CopyProtoent(staticBuf, buffer, buflen, result);
+ if (PR_FAILURE == rv)
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ }
+ PR_Unlock(_getproto_lock);
+ }
+#endif /* all that */
+ return rv;
+}
+
+PR_IMPLEMENT(PRStatus) PR_GetProtoByNumber(
+ PRInt32 number, char* buffer, PRInt32 buflen, PRProtoEnt* result)
+{
+ PRStatus rv = PR_SUCCESS;
+ struct protoent* res = (struct protoent*)result;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+#if defined(_PR_HAVE_GETPROTO_R_INT)
+ {
+ /*
+ ** The protoent_data has a pointer as the first field.
+ ** That implies the buffer better be aligned, and char*
+ ** doesn't promise much.
+ */
+ PRUptrdiff aligned = (PRUptrdiff)buffer;
+ if (0 != (aligned & (sizeof(struct protoent_data*) - 1)))
+ {
+ aligned += sizeof(struct protoent_data*) - 1;
+ aligned &= ~(sizeof(struct protoent_data*) - 1);
+ buflen -= (aligned - (PRUptrdiff)buffer);
+ buffer = (char*)aligned;
+ }
+ }
+#endif /* defined(_PR_HAVE_GETPROTO_R_INT) */
+
+ PR_ASSERT(PR_NETDB_BUF_SIZE <= buflen);
+ if (PR_NETDB_BUF_SIZE > buflen)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+#if defined(_PR_HAVE_GETPROTO_R_POINTER)
+ if (NULL == getprotobynumber_r(number, res, buffer, buflen))
+ {
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO());
+ return PR_FAILURE;
+ }
+
+#elif defined(_PR_HAVE_GETPROTO_R_INT)
+ /*
+ ** The buffer needs to be zero'd for these OS's.
+ */
+ memset(buffer, 0, buflen);
+ if (-1 == getprotobynumber_r(number, res, (struct protoent_data*)buffer))
+ {
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO());
+ return PR_FAILURE;
+ }
+#elif defined(_PR_HAVE_5_ARG_GETPROTO_R)
+ /* The 5th argument for getprotobynumber_r() cannot be NULL */
+ if (-1 == getprotobynumber_r(number, res, buffer, buflen, &res))
+ {
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO());
+ return PR_FAILURE;
+ }
+#else /* do it the hard way */
+ {
+ struct protoent *staticBuf;
+ PR_Lock(_getproto_lock);
+ staticBuf = getprotobynumber_r(number);
+ if (NULL == staticBuf)
+ {
+ rv = PR_FAILURE;
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO());
+ }
+ else
+ {
+ rv = CopyProtoent(staticBuf, buffer, buflen, result);
+ if (PR_FAILURE == rv)
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ }
+ PR_Unlock(_getproto_lock);
+ }
+#endif /* all that crap */
+ return rv;
+
+}
+
+PR_IMPLEMENT(PRUintn) PR_NetAddrSize(const PRNetAddr* addr)
+{
+ PRUintn addrsize;
+
+#if defined(_PR_INET6)
+
+ if ((AF_INET == (0x00ff & addr->raw.family))
+ || (AF_INET == (0x00ff & ntohs(addr->raw.family))))
+ addrsize = sizeof(struct sockaddr_in);
+ else if ((AF_INET6 == (0x00ff & addr->raw.family))
+ || (AF_INET6 == (0x00ff & ntohs(addr->raw.family))))
+ addrsize = sizeof(struct sockaddr_in6);
+ else addrsize = 0;
+
+#else /* defined(_PR_INET6) */
+
+#if defined(XP_MAC)
+#pragma unused (addr)
+#endif
+
+ addrsize = sizeof(struct sockaddr_in);
+
+#endif /* defined(_PR_INET6) */
+
+ return addrsize;
+} /* PR_NetAddrSize */
+
+PR_IMPLEMENT(PRIntn) PR_EnumerateHostEnt(
+ PRIntn enumIndex, const PRHostEnt *hostEnt, PRUint16 port, PRNetAddr *address)
+{
+ void *addr = hostEnt->h_addr_list[enumIndex++];
+ memset(address, 0, sizeof(PRNetAddr));
+ if (NULL == addr) enumIndex = 0;
+ else
+ {
+#if defined(_PR_INET6)
+ if (_pr_ipv6_enabled)
+ {
+ address->ipv6.family = AF_INET6;
+ address->ipv6.port = htons(port);
+ if (AF_INET6 == hostEnt->h_addrtype)
+ memcpy(&address->ipv6.ip, addr, hostEnt->h_length);
+ else
+ {
+ unsigned char *start = (unsigned char *) &address->ipv6.ip;
+ PR_ASSERT(AF_INET == hostEnt->h_addrtype);
+ memset(start, 0, 10);
+ memset(start + 10, 0xff, 2);
+ memcpy(start + 12, addr, hostEnt->h_length);
+ PR_ASSERT(IN6_IS_ADDR_V4MAPPED(&address->ipv6.ip));
+ }
+ }
+ else
+#endif /* defined(_PR_INET6) */
+ {
+ PR_ASSERT(AF_INET == hostEnt->h_addrtype);
+ address->inet.family = hostEnt->h_addrtype;
+ address->inet.port = htons(port);
+ memcpy(&address->inet.ip, addr, hostEnt->h_length);
+ }
+ }
+ return enumIndex;
+} /* PR_EnumerateHostEnt */
+
+PR_IMPLEMENT(PRStatus) PR_InitializeNetAddr(
+ PRNetAddrValue val, PRUint16 port, PRNetAddr *addr)
+{
+ PRStatus rv = PR_SUCCESS;
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+#if defined(_PR_INET6)
+ if (_pr_ipv6_enabled)
+ {
+ addr->ipv6.family = AF_INET6;
+ addr->ipv6.port = htons(port);
+ switch (val)
+ {
+ case PR_IpAddrNull:
+ break; /* don't overwrite the address */
+ case PR_IpAddrAny:
+ addr->ipv6.ip = in6addr_any;
+ break;
+ case PR_IpAddrLoopback:
+ addr->ipv6.ip = in6addr_loopback;
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ rv = PR_FAILURE;
+ }
+ }
+ else
+#endif /* defined(_PR_INET6) */
+ {
+ addr->inet.family = AF_INET;
+ addr->inet.port = htons(port);
+ switch (val)
+ {
+ case PR_IpAddrNull:
+ break; /* don't overwrite the address */
+ case PR_IpAddrAny:
+ addr->inet.ip = htonl(INADDR_ANY);
+ break;
+ case PR_IpAddrLoopback:
+ addr->inet.ip = htonl(INADDR_LOOPBACK);
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ rv = PR_FAILURE;
+ }
+ }
+ return rv;
+} /* PR_InitializeNetAddr */
+
+PR_IMPLEMENT(PRNetAddr*) PR_CreateNetAddr(PRNetAddrValue val, PRUint16 port)
+{
+ PRNetAddr *addr = NULL;
+ if ((PR_IpAddrAny == val) || (PR_IpAddrLoopback == val))
+ {
+ addr = PR_NEWZAP(PRNetAddr);
+ if (NULL == addr)
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ else
+ if (PR_FAILURE == PR_InitializeNetAddr(val, port, addr))
+ PR_DELETE(addr); /* and that will make 'addr' == NULL */
+ }
+ else
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return addr;
+} /* PR_CreateNetAddr */
+
+PR_IMPLEMENT(PRStatus) PR_DestroyNetAddr(PRNetAddr *addr)
+{
+ PR_Free(addr);
+ return PR_SUCCESS;
+} /* PR_DestroyNetAddr */
+
+PR_IMPLEMENT(PRStatus) PR_StringToNetAddr(const char *string, PRNetAddr *addr)
+{
+ /*
+ ** If we're built to support IPv6 addressing AND it's currently enabled,
+ ** then all addresses are of the IPv6 addressing family. Both are required
+ ** before anything overt happens.
+ */
+ PRStatus status = PR_InitializeNetAddr(PR_IpAddrNull, 0, addr);
+
+ PR_ASSERT(PR_SUCCESS == status);
+ if (PR_SUCCESS != status) return status;
+
+#if defined(_PR_INET6)
+
+ if (_pr_ipv6_enabled)
+ {
+ /*
+ ** Okay, we're doing it.
+ */
+ PRIntn rv = inet_pton(AF_INET6, string, &addr->ipv6.ip);
+ if (1 != rv)
+ {
+ /*
+ * rv is 0 if the string argument is not a valid IPv4 or IPv6
+ * address string.
+ * rv is -1 with errno set to EADNOSUPPORT if the af argument is
+ * not a known address family.
+ */
+ PRIntn syserrno = (-1 == rv) ? errno : 0;
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, syserrno);
+ status = PR_FAILURE;
+ }
+ }
+ else
+#endif
+ {
+ PRUint32 *ip = (PRUint32*)&addr->inet.ip;
+
+ *ip = inet_addr(string);
+ if ((PRUint32) -1 == *ip)
+ {
+ /*
+ * Either the af argument is not AF_INET, or the string argument
+ * is a malformed address string.
+ */
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ status = PR_FAILURE;
+ }
+ }
+
+ return status;
+}
+
+PR_IMPLEMENT(PRStatus) PR_NetAddrToString(
+ const PRNetAddr *addr, char *string, PRUint32 size)
+{
+ PR_ASSERT(size >= 16);
+ if (size < 16) goto failed;
+
+#if defined(_PR_INET6)
+ if (_pr_ipv6_enabled)
+ {
+ PR_ASSERT(AF_INET6 == addr->ipv6.family);
+ if ((AF_INET6 != addr->ipv6.family)
+ || (NULL == inet_ntop(AF_INET6, &addr->ipv6.ip, string, size)))
+ goto failed;
+ }
+ else
+#endif /* defined(_PR_INET6) */
+ {
+ PR_ASSERT(AF_INET == addr->inet.family);
+ if (AF_INET != addr->inet.family) goto failed;
+ else
+ {
+ unsigned char *byte = (unsigned char*)&addr->inet.ip;
+ PR_snprintf(string, size, "%u.%u.%u.%u",
+ byte[0], byte[1], byte[2], byte[3]);
+ }
+ }
+
+ return PR_SUCCESS;
+
+failed:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+
+} /* PR_NetAddrToString */
+
+PR_IMPLEMENT(PRUint16) PR_ntohs(PRUint16 n) { return ntohs(n); }
+PR_IMPLEMENT(PRUint32) PR_ntohl(PRUint32 n) { return ntohl(n); }
+PR_IMPLEMENT(PRUint16) PR_htons(PRUint16 n) { return htons(n); }
+PR_IMPLEMENT(PRUint32) PR_htonl(PRUint32 n) { return htonl(n); }
+PR_IMPLEMENT(PRUint64) PR_ntohll(PRUint64 n)
+{
+ /*
+ ** There is currently no attempt to optomize out depending
+ ** on the host' byte order. That would be easy enough to
+ ** do.
+ */
+ PRUint64 tmp;
+ PRUint32 hi, lo;
+ LL_L2UI(lo, n);
+ LL_SHR(tmp, n, 32);
+ LL_L2UI(hi, tmp);
+ hi = PR_ntohl(hi);
+ lo = PR_ntohl(lo);
+ LL_UI2L(n, hi);
+ LL_SHL(n, n, 32);
+ LL_UI2L(tmp, lo);
+ LL_ADD(n, n, tmp);
+ return n;
+} /* ntohll */
+
+PR_IMPLEMENT(PRUint64) PR_htonll(PRUint64 n)
+{
+ /*
+ ** There is currently no attempt to optomize out depending
+ ** on the host' byte order. That would be easy enough to
+ ** do.
+ */
+ PRUint64 tmp;
+ PRUint32 hi, lo;
+ LL_L2UI(lo, n);
+ LL_SHR(tmp, n, 32);
+ LL_L2UI(hi, tmp);
+ hi = htonl(hi);
+ lo = htonl(lo);
+ LL_UI2L(n, hi);
+ LL_SHL(n, n, 32);
+ LL_UI2L(tmp, lo);
+ LL_ADD(n, n, tmp);
+ return n;
+} /* htonll */
+
+PR_IMPLEMENT(PRUint16) PR_FamilyInet(void)
+{
+#ifdef _PR_INET6
+ return (_pr_ipv6_enabled ? AF_INET6 : AF_INET);
+#else
+ return AF_INET;
+#endif
+}
diff --git a/pr/src/misc/prsystem.c b/pr/src/misc/prsystem.c
new file mode 100644
index 00000000..cf23e256
--- /dev/null
+++ b/pr/src/misc/prsystem.c
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+#include "prsystem.h"
+#include "prprf.h"
+
+#if defined(XP_UNIX)
+#include <unistd.h>
+#include <sys/utsname.h>
+#endif
+
+PR_IMPLEMENT(char) PR_GetDirectorySepartor()
+{
+ return PR_DIRECTORY_SEPARATOR;
+} /* PR_GetDirectorySepartor */
+
+PR_IMPLEMENT(PRStatus) PR_GetSystemInfo(PRSysInfo cmd, char *buf, PRUint32 buflen)
+{
+ PRUintn len = 0;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ switch(cmd)
+ {
+ case PR_SI_HOSTNAME:
+ if (PR_FAILURE == _PR_MD_GETHOSTNAME(buf, (PRUintn)buflen))
+ return PR_FAILURE;
+ /* Return the unqualified hostname */
+ while (buf[len] && (len < buflen)) {
+ if (buf[len] == '.') {
+ buf[len] = '\0';
+ break;
+ }
+ len += 1;
+ }
+ break;
+
+ case PR_SI_SYSNAME:
+ /* Return the operating system name */
+ (void)PR_snprintf(buf, buflen, _PR_SI_SYSNAME);
+ break;
+
+ case PR_SI_RELEASE:
+ /* Return the version of the operating system */
+#if defined(XP_UNIX)
+ {
+ struct utsname info;
+ uname(&info);
+ (void)PR_snprintf(buf, buflen, info.release);
+ }
+#endif
+ break;
+
+ case PR_SI_ARCHITECTURE:
+ /* Return the architecture of the machine (ie. x86, mips, alpha, ...)*/
+ (void)PR_snprintf(buf, buflen, _PR_SI_ARCHITECTURE);
+ break;
+ }
+ return PR_SUCCESS;
+}
diff --git a/pr/src/misc/prthinfo.c b/pr/src/misc/prthinfo.c
new file mode 100644
index 00000000..6f6ad1e7
--- /dev/null
+++ b/pr/src/misc/prthinfo.c
@@ -0,0 +1,229 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "prlog.h"
+#include "prthread.h"
+#ifdef XP_MAC
+#include "pprthred.h"
+#else
+#include "private/pprthred.h"
+#endif
+#include "primpl.h"
+
+PR_IMPLEMENT(PRWord *)
+PR_GetGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ return _MD_HomeGCRegisters(t, isCurrent, np);
+}
+
+PR_IMPLEMENT(PRStatus)
+PR_ThreadScanStackPointers(PRThread* t,
+ PRScanStackFun scanFun, void* scanClosure)
+{
+ PRThread* current = PR_GetCurrentThread();
+ PRWord *sp, *esp, *p0;
+ int n;
+ void **ptd;
+ PRStatus status;
+ PRUint32 index;
+ int stack_end;
+
+ /*
+ ** Store the thread's registers in the thread structure so the GC
+ ** can scan them. Then scan them.
+ */
+ p0 = _MD_HomeGCRegisters(t, t == current, &n);
+ status = scanFun(t, (void**)p0, n, scanClosure);
+ if (status != PR_SUCCESS)
+ return status;
+
+ /* Scan the C stack for pointers into the GC heap */
+#if defined(XP_PC) && defined(WIN16)
+ /*
+ ** Under WIN16, the stack of the current thread is always mapped into
+ ** the "task stack" (at SS:xxxx). So, if t is the current thread, scan
+ ** the "task stack". Otherwise, scan the "cached stack" of the inactive
+ ** thread...
+ */
+ if (t == current) {
+ sp = (PRWord*) &stack_end;
+ esp = (PRWord*) _pr_top_of_task_stack;
+
+ PR_ASSERT(sp <= esp);
+ } else {
+ sp = (PRWord*) PR_GetSP(t);
+ esp = (PRWord*) t->stack->stackTop;
+
+ PR_ASSERT((t->stack->stackSize == 0) ||
+ ((sp > (PRWord*)t->stack->stackBottom) &&
+ (sp <= (PRWord*)t->stack->stackTop)));
+ }
+#else /* ! WIN16 */
+#ifdef HAVE_STACK_GROWING_UP
+ if (t == current) {
+ esp = (PRWord*) &stack_end;
+ } else {
+ esp = (PRWord*) PR_GetSP(t);
+ }
+ sp = (PRWord*) t->stack->stackTop;
+ if (t->stack->stackSize) {
+ PR_ASSERT((esp > (PRWord*)t->stack->stackTop) &&
+ (esp < (PRWord*)t->stack->stackBottom));
+ }
+#else /* ! HAVE_STACK_GROWING_UP */
+ if (t == current) {
+ sp = (PRWord*) &stack_end;
+ } else {
+ sp = (PRWord*) PR_GetSP(t);
+ }
+ esp = (PRWord*) t->stack->stackTop;
+ if (t->stack->stackSize) {
+ PR_ASSERT((sp > (PRWord*)t->stack->stackBottom) &&
+ (sp < (PRWord*)t->stack->stackTop));
+ }
+#endif /* ! HAVE_STACK_GROWING_UP */
+#endif /* ! WIN16 */
+
+#if defined(WIN16)
+ {
+ prword_t scan;
+ prword_t limit;
+
+ scan = (prword_t) sp;
+ limit = (prword_t) esp;
+ while (scan < limit) {
+ prword_t *test;
+
+ test = *((prword_t **)scan);
+ status = scanFun(t, (void**)&test, 1, scanClosure);
+ if (status != PR_SUCCESS)
+ return status;
+ scan += sizeof(char);
+ }
+ }
+#else
+ if (sp < esp) {
+ status = scanFun(t, (void**)sp, esp - sp, scanClosure);
+ if (status != PR_SUCCESS)
+ return status;
+ }
+#endif
+
+ /*
+ ** Mark all of the per-thread-data items attached to this thread
+ */
+
+#if defined(_PR_PTHREADS)
+/* PR_ASSERT(!"I can't do this!"); */
+#else /* defined(_PR_PTHREADS) */
+
+ /* the execution environment better be accounted for otherwise it will be collected */
+ status = scanFun(t, (void**)&t->environment, 1, scanClosure);
+ if (status != PR_SUCCESS)
+ return status;
+
+ ptd = t->privateData;
+ for (index = 0; index < t->tpdLength; index++, ptd++) {
+ status = scanFun(t, (void**)ptd, 1, scanClosure);
+ if (status != PR_SUCCESS)
+ return status;
+ }
+#endif /* defined(_PR_PTHREADS) */
+
+ return PR_SUCCESS;
+}
+
+/* transducer for PR_EnumerateThreads */
+typedef struct PRScanStackData {
+ PRScanStackFun scanFun;
+ void* scanClosure;
+} PRScanStackData;
+
+static PRStatus PR_CALLBACK
+pr_ScanStack(PRThread* t, int i, void* arg)
+{
+#if defined(XP_MAC)
+#pragma unused (i)
+#endif
+ PRScanStackData* data = (PRScanStackData*)arg;
+ return PR_ThreadScanStackPointers(t, data->scanFun, data->scanClosure);
+}
+
+PR_IMPLEMENT(PRStatus)
+PR_ScanStackPointers(PRScanStackFun scanFun, void* scanClosure)
+{
+ PRScanStackData data;
+ data.scanFun = scanFun;
+ data.scanClosure = scanClosure;
+ return PR_EnumerateThreads(pr_ScanStack, &data);
+}
+
+PR_PUBLIC_API(PRUword)
+PR_GetStackSpaceLeft(PRThread* t)
+{
+ PRThread *current = PR_CurrentThread();
+ PRWord *sp, *esp;
+ int stack_end;
+
+#if defined(WIN16)
+ /*
+ ** Under WIN16, the stack of the current thread is always mapped into
+ ** the "task stack" (at SS:xxxx). So, if t is the current thread, scan
+ ** the "task stack". Otherwise, scan the "cached stack" of the inactive
+ ** thread...
+ */
+ if (t == current) {
+ sp = (PRWord*) &stack_end;
+ esp = (PRWord*) _pr_top_of_task_stack;
+
+ PR_ASSERT(sp <= esp);
+ } else {
+ sp = (PRWord*) PR_GetSP(t);
+ esp = (PRWord*) t->stack->stackTop;
+
+ PR_ASSERT((t->stack->stackSize == 0) ||
+ ((sp > (PRWord*)t->stack->stackBottom) &&
+ (sp <= (PRWord*)t->stack->stackTop)));
+ }
+#else /* ! WIN16 */
+#ifdef HAVE_STACK_GROWING_UP
+ if (t == current) {
+ esp = (PRWord*) &stack_end;
+ } else {
+ esp = (PRWord*) PR_GetSP(t);
+ }
+ sp = (PRWord*) t->stack->stackTop;
+ if (t->stack->stackSize) {
+ PR_ASSERT((esp > (PRWord*)t->stack->stackTop) &&
+ (esp < (PRWord*)t->stack->stackBottom));
+ }
+#else /* ! HAVE_STACK_GROWING_UP */
+ if (t == current) {
+ sp = (PRWord*) &stack_end;
+ } else {
+ sp = (PRWord*) PR_GetSP(t);
+ }
+ esp = (PRWord*) t->stack->stackTop;
+ if (t->stack->stackSize) {
+ PR_ASSERT((sp > (PRWord*)t->stack->stackBottom) &&
+ (sp < (PRWord*)t->stack->stackTop));
+ }
+#endif /* ! HAVE_STACK_GROWING_UP */
+#endif /* ! WIN16 */
+ return (PRUword)t->stack->stackSize - ((PRWord)esp - (PRWord)sp);
+}
diff --git a/pr/src/misc/prtime.c b/pr/src/misc/prtime.c
new file mode 100644
index 00000000..bfe36088
--- /dev/null
+++ b/pr/src/misc/prtime.c
@@ -0,0 +1,1930 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * prtime.c --
+ *
+ * NSPR date and time functions
+ *
+ */
+
+#include "prinit.h"
+#include "prtime.h"
+#include "prlock.h"
+#include "prprf.h"
+#include "prlog.h"
+
+#include <string.h>
+#include <ctype.h>
+
+#ifdef XP_MAC
+#include <time.h>
+#endif
+
+
+
+
+/*
+ * Static variables used by functions in this file
+ */
+
+/*
+ * The following array contains the day of year for the last day of
+ * each month, where index 1 is January, and day 0 is January 1.
+ */
+
+static int lastDayOfMonth[2][13] = {
+ {-1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364},
+ {-1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}
+};
+
+/*
+ * The number of days in a month
+ */
+
+static PRInt8 nDays[2][12] = {
+ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+ {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+};
+
+/*
+ * Declarations for internal functions defined later in this file.
+ */
+
+static void ComputeGMT(PRTime time, PRExplodedTime *gmt);
+static int IsLeapYear(PRInt16 year);
+static void ApplySecOffset(PRExplodedTime *time, PRInt32 secOffset);
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * ComputeGMT --
+ *
+ * Caveats:
+ * - we ignore leap seconds
+ * - our leap-year calculation is only correct for years 1901-2099
+ *
+ *------------------------------------------------------------------------
+ */
+
+static void
+ComputeGMT(PRTime time, PRExplodedTime *gmt)
+{
+ PRInt32 tmp, rem;
+ PRInt32 numDays;
+ PRInt64 numDays64, rem64;
+ int isLeap;
+ PRInt64 sec;
+ PRInt64 usec;
+ PRInt64 usecPerSec;
+ PRInt64 secPerDay;
+
+ /*
+ * We first do the usec, sec, min, hour thing so that we do not
+ * have to do LL arithmetic.
+ */
+
+ LL_I2L(usecPerSec, 1000000L);
+ LL_DIV(sec, time, usecPerSec);
+ LL_MOD(usec, time, usecPerSec);
+ LL_L2I(gmt->tm_usec, usec);
+ /* Correct for weird mod semantics so the remainder is always positive */
+ if (gmt->tm_usec < 0) {
+ PRInt64 one;
+
+ LL_I2L(one, 1L);
+ LL_SUB(sec, sec, one);
+ gmt->tm_usec += 1000000L;
+ }
+
+ LL_I2L(secPerDay, 86400L);
+ LL_DIV(numDays64, sec, secPerDay);
+ LL_MOD(rem64, sec, secPerDay);
+ /* We are sure both of these numbers can fit into PRInt32 */
+ LL_L2I(numDays, numDays64);
+ LL_L2I(rem, rem64);
+ if (rem < 0) {
+ numDays--;
+ rem += 86400L;
+ }
+
+ /* Compute day of week. Epoch started on a Thursday. */
+
+ gmt->tm_wday = (numDays + 4) % 7;
+ if (gmt->tm_wday < 0) {
+ gmt->tm_wday += 7;
+ }
+
+ /* Compute the time of day. */
+
+ gmt->tm_hour = rem / 3600;
+ rem %= 3600;
+ gmt->tm_min = rem / 60;
+ gmt->tm_sec = rem % 60;
+
+ /* Compute the four-year span containing the specified time */
+
+ tmp = numDays / (4 * 365 + 1);
+ rem = numDays % (4 * 365 + 1);
+
+ if (rem < 0) {
+ tmp--;
+ rem += (4 * 365 + 1);
+ }
+
+ /*
+ * Compute the year after 1900 by taking the four-year span and
+ * adjusting for the remainder. This works because 2000 is a
+ * leap year, and 1900 and 2100 are out of the range.
+ */
+
+ tmp = (tmp * 4) + 1970;
+ isLeap = 0;
+
+ /*
+ * 1970 has 365 days
+ * 1971 has 365 days
+ * 1972 has 366 days (leap year)
+ * 1973 has 365 days
+ */
+
+ if (rem >= 365) { /* 1971, etc. */
+ tmp++;
+ rem -= 365;
+ if (rem >= 365) { /* 1972, etc. */
+ tmp++;
+ rem -= 365;
+ if (rem >= 366) { /* 1973, etc. */
+ tmp++;
+ rem -= 366;
+ } else {
+ isLeap = 1;
+ }
+ }
+ }
+
+ gmt->tm_year = tmp;
+ gmt->tm_yday = rem;
+
+ /* Compute the month and day of month. */
+
+ for (tmp = 1; lastDayOfMonth[isLeap][tmp] < gmt->tm_yday; tmp++) {
+ }
+ gmt->tm_month = --tmp;
+ gmt->tm_mday = gmt->tm_yday - lastDayOfMonth[isLeap][tmp];
+
+ gmt->tm_params.tp_gmt_offset = 0;
+ gmt->tm_params.tp_dst_offset = 0;
+}
+
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * PR_ExplodeTime --
+ *
+ * Cf. struct tm *gmtime(const time_t *tp) and
+ * struct tm *localtime(const time_t *tp)
+ *
+ *------------------------------------------------------------------------
+ */
+
+PR_IMPLEMENT(void)
+PR_ExplodeTime(
+ PRTime usecs,
+ PRTimeParamFn params,
+ PRExplodedTime *exploded)
+{
+ ComputeGMT(usecs, exploded);
+ exploded->tm_params = params(exploded);
+ ApplySecOffset(exploded, exploded->tm_params.tp_gmt_offset
+ + exploded->tm_params.tp_dst_offset);
+}
+
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * PR_ImplodeTime --
+ *
+ * Cf. time_t mktime(struct tm *tp)
+ * Note that 1 year has < 2^25 seconds. So an PRInt32 is large enough.
+ *
+ *------------------------------------------------------------------------
+ */
+#if defined(HAVE_WATCOM_BUG_2)
+PRTime __pascal __export __loadds
+#else
+PR_IMPLEMENT(PRTime)
+#endif
+PR_ImplodeTime(const PRExplodedTime *exploded)
+{
+ PRExplodedTime copy;
+ PRTime retVal;
+ PRInt64 secPerDay, usecPerSec;
+ PRInt64 temp;
+ PRInt64 numSecs64;
+ PRInt32 fourYears;
+ PRInt32 remainder;
+ PRInt32 numDays;
+ PRInt32 numSecs;
+
+ /* Normalize first. Do this on our copy */
+ copy = *exploded;
+ PR_NormalizeTime(&copy, PR_GMTParameters);
+
+ fourYears = (copy.tm_year - 1970) / 4;
+ remainder = (copy.tm_year - 1970) % 4;
+ if (remainder < 0) {
+ remainder += 4;
+ fourYears--;
+ }
+ numDays = fourYears * (4 * 365 + 1);
+ switch (remainder) {
+ case 0:
+ break;
+ case 1: /* 1970 */
+ numDays += 365;
+ break;
+ case 2: /* 1970-1 */
+ numDays += 365 * 2;
+ break;
+ case 3: /* 1970-2 */
+ numDays += 365 * 3 + 1;
+ break;
+ }
+
+ numSecs = copy.tm_yday * 86400 + copy.tm_hour * 3600
+ + copy.tm_min * 60 + copy.tm_sec;
+
+ LL_I2L(temp, numDays);
+ LL_I2L(secPerDay, 86400);
+ LL_MUL(temp, temp, secPerDay);
+ LL_I2L(numSecs64, numSecs);
+ LL_ADD(numSecs64, numSecs64, temp);
+
+ /* apply the GMT and DST offsets */
+ LL_I2L(temp, copy.tm_params.tp_gmt_offset);
+ LL_SUB(numSecs64, numSecs64, temp);
+ LL_I2L(temp, copy.tm_params.tp_dst_offset);
+ LL_SUB(numSecs64, numSecs64, temp);
+
+ LL_I2L(usecPerSec, 1000000L);
+ LL_MUL(temp, numSecs64, usecPerSec);
+ LL_I2L(retVal, copy.tm_usec);
+ LL_ADD(retVal, retVal, temp);
+
+ return retVal;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * IsLeapYear --
+ *
+ * Returns 1 if the year is a leap year, 0 otherwise.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static int IsLeapYear(PRInt16 year)
+{
+ if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * 'secOffset' should be less than 86400 (i.e., a day).
+ * 'time' should point to a normalized PRExplodedTime.
+ */
+
+static void
+ApplySecOffset(PRExplodedTime *time, PRInt32 secOffset)
+{
+ time->tm_sec += secOffset;
+
+ /* Note that in this implementation we do not count leap seconds */
+ if (time->tm_sec < 0 || time->tm_sec >= 60) {
+ time->tm_min += time->tm_sec / 60;
+ time->tm_sec %= 60;
+ if (time->tm_sec < 0) {
+ time->tm_sec += 60;
+ time->tm_min--;
+ }
+ }
+
+ if (time->tm_min < 0 || time->tm_min >= 60) {
+ time->tm_hour += time->tm_min / 60;
+ time->tm_min %= 60;
+ if (time->tm_min < 0) {
+ time->tm_min += 60;
+ time->tm_hour--;
+ }
+ }
+
+ if (time->tm_hour < 0) {
+ /* Decrement mday, yday, and wday */
+ time->tm_hour += 24;
+ time->tm_mday--;
+ time->tm_yday--;
+ if (time->tm_mday < 1) {
+ time->tm_month--;
+ if (time->tm_month < 0) {
+ time->tm_month = 11;
+ time->tm_year--;
+ if (IsLeapYear(time->tm_year))
+ time->tm_yday = 365;
+ else
+ time->tm_yday = 364;
+ }
+ time->tm_mday = nDays[IsLeapYear(time->tm_year)][time->tm_month];
+ }
+ time->tm_wday--;
+ if (time->tm_wday < 0)
+ time->tm_wday = 6;
+ } else if (time->tm_hour > 23) {
+ /* Increment mday, yday, and wday */
+ time->tm_hour -= 24;
+ time->tm_mday++;
+ time->tm_yday++;
+ if (time->tm_mday >
+ nDays[IsLeapYear(time->tm_year)][time->tm_month]) {
+ time->tm_mday = 1;
+ time->tm_month++;
+ if (time->tm_month > 11) {
+ time->tm_month = 0;
+ time->tm_year++;
+ time->tm_yday = 0;
+ }
+ }
+ time->tm_wday++;
+ if (time->tm_wday > 6)
+ time->tm_wday = 0;
+ }
+}
+
+PR_IMPLEMENT(void)
+PR_NormalizeTime(PRExplodedTime *time, PRTimeParamFn params)
+{
+ int daysInMonth;
+ PRInt32 fourYears;
+ PRInt32 remainder;
+ PRInt32 numDays;
+
+ /* Get back to GMT */
+ time->tm_sec -= time->tm_params.tp_gmt_offset
+ + time->tm_params.tp_dst_offset;
+ time->tm_params.tp_gmt_offset = 0;
+ time->tm_params.tp_dst_offset = 0;
+
+ /* Now normalize GMT */
+
+ if (time->tm_usec < 0 || time->tm_usec >= 1000000) {
+ time->tm_sec += time->tm_usec / 1000000;
+ time->tm_usec %= 1000000;
+ if (time->tm_usec < 0) {
+ time->tm_usec += 1000000;
+ time->tm_sec--;
+ }
+ }
+
+ /* Note that we do not count leap seconds in this implementation */
+ if (time->tm_sec < 0 || time->tm_sec >= 60) {
+ time->tm_min += time->tm_sec / 60;
+ time->tm_sec %= 60;
+ if (time->tm_sec < 0) {
+ time->tm_sec += 60;
+ time->tm_min--;
+ }
+ }
+
+ if (time->tm_min < 0 || time->tm_min >= 60) {
+ time->tm_hour += time->tm_min / 60;
+ time->tm_min %= 60;
+ if (time->tm_min < 0) {
+ time->tm_min += 60;
+ time->tm_hour--;
+ }
+ }
+
+ if (time->tm_hour < 0 || time->tm_hour >= 24) {
+ time->tm_mday += time->tm_hour / 24;
+ time->tm_hour %= 24;
+ if (time->tm_hour < 0) {
+ time->tm_hour += 24;
+ time->tm_mday--;
+ }
+ }
+
+ /* Normalize month and year before mday */
+ if (time->tm_month < 0 || time->tm_month >= 12) {
+ time->tm_year += time->tm_month / 12;
+ time->tm_month %= 12;
+ if (time->tm_month < 0) {
+ time->tm_month += 12;
+ time->tm_year--;
+ }
+ }
+
+ /* Now that month and year are in proper range, normalize mday */
+
+ if (time->tm_mday < 1) {
+ /* mday too small */
+ do {
+ /* the previous month */
+ time->tm_month--;
+ if (time->tm_month < 0) {
+ time->tm_month = 11;
+ time->tm_year--;
+ }
+ time->tm_mday += nDays[IsLeapYear(time->tm_year)][time->tm_month];
+ } while (time->tm_mday < 1);
+ } else {
+ daysInMonth = nDays[IsLeapYear(time->tm_year)][time->tm_month];
+ while (time->tm_mday > daysInMonth) {
+ /* mday too large */
+ time->tm_mday -= daysInMonth;
+ time->tm_month++;
+ if (time->tm_month > 11) {
+ time->tm_month = 0;
+ time->tm_year++;
+ }
+ daysInMonth = nDays[IsLeapYear(time->tm_year)][time->tm_month];
+ }
+ }
+
+ /* Recompute yday and wday */
+ time->tm_yday = time->tm_mday +
+ lastDayOfMonth[IsLeapYear(time->tm_year)][time->tm_month];
+ fourYears = (time->tm_year - 1970) / 4;
+ remainder = (time->tm_year - 1970) % 4;
+ if (remainder < 0) {
+ remainder += 4;
+ fourYears--;
+ }
+ numDays = fourYears * (4 * 365 + 1);
+ switch (remainder) {
+ case 0:
+ break;
+ case 1:
+ numDays += 365; /* 1970 */
+ break;
+ case 2:
+ numDays += 365 + 365; /* 1970 and 1971 */
+ break;
+ case 3:
+ numDays += 365 + 365 + 366; /* 1970-2 */
+ }
+ numDays += time->tm_yday;
+ time->tm_wday = (numDays + 4) % 7;
+ if (time->tm_wday < 0) {
+ time->tm_wday += 7;
+ }
+
+ /* Recompute time parameters */
+
+ time->tm_params = params(time);
+
+ ApplySecOffset(time, time->tm_params.tp_gmt_offset
+ + time->tm_params.tp_dst_offset);
+}
+
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * PR_LocalTimeParameters --
+ *
+ * returns the time parameters for the local time zone
+ *
+ * The following uses localtime() from the standard C library.
+ * (time.h) This is our fallback implementation. Unix and PC
+ * use this version. Mac has its own machine-dependent
+ * implementation of this function.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include <time.h>
+
+#if (defined(OSF1) && !defined(OSF1V4)) || defined(HPUX10_10) \
+ || defined(HPUX10_20)
+
+#define MT_safe_localtime(timer, result) \
+ (localtime_r(timer, result) == 0 ? result : NULL)
+
+#elif defined(SOLARIS) || defined(IRIX) \
+ || (defined(AIX) && !defined(AIX4_1) && defined(_THREAD_SAFE)) \
+ || defined(OSF1V4) \
+ || defined(HPUX10_30) || defined(HPUX11)
+
+#define MT_safe_localtime localtime_r
+
+#else
+
+#if defined(XP_MAC)
+extern struct tm *Maclocaltime(const time_t * t);
+#endif
+
+static PRLock *monitor = NULL;
+
+static struct tm *MT_safe_localtime(const time_t *clock, struct tm *result)
+{
+ struct tm *tmPtr;
+ int needLock = PR_Initialized(); /* We need to use a lock to protect
+ * against NSPR threads only when the
+ * NSPR thread system is activated. */
+
+ if (needLock) {
+ if (monitor == NULL) {
+ monitor = PR_NewLock();
+ }
+ PR_Lock(monitor);
+ }
+
+ /*
+ * Microsoft (all flavors) localtime() returns a NULL pointer if 'clock'
+ * represents a time before midnight January 1, 1970. In
+ * that case, we also return a NULL pointer and the struct tm
+ * object pointed to by 'result' is not modified.
+ *
+ * Watcom C/C++ 11.0 localtime() treats time_t as unsigned long
+ * hence, does not recognize negative values of clock as pre-1/1/70.
+ * We have to manually check (WIN16 only) for negative value of
+ * clock and return NULL.
+ */
+
+#if defined(XP_MAC)
+ tmPtr = Maclocaltime(clock);
+#else
+ tmPtr = localtime(clock);
+#endif
+
+#if defined(WIN16)
+ if ( (PRInt32) *clock < 0 )
+ result = NULL;
+ else
+ *result = *tmPtr;
+#else
+ if (tmPtr) {
+ *result = *tmPtr;
+ } else {
+ result = NULL;
+ }
+#endif /* WIN16 */
+
+ if (needLock) PR_Unlock(monitor);
+
+ return result;
+}
+
+#endif /* definition of MT_safe_localtime() */
+
+#if defined(XP_UNIX) || defined(XP_PC)
+
+PR_IMPLEMENT(PRTimeParameters)
+PR_LocalTimeParameters(const PRExplodedTime *gmt)
+{
+
+ PRTimeParameters retVal;
+ struct tm localTime;
+ time_t secs;
+ PRTime secs64;
+ PRInt64 usecPerSec;
+ PRInt64 maxInt32;
+ PRInt32 dayOffset;
+ PRInt32 offset2Jan1970;
+ PRInt32 offsetNew;
+ int isdst2Jan1970;
+
+ /*
+ * Calculate the GMT offset. First, figure out what is
+ * 00:00:00 Jan. 2, 1970 GMT (which is exactly a day, or 86400
+ * seconds, since the epoch) in local time. Then we calculate
+ * the difference between local time and GMT in seconds:
+ * gmt_offset = local_time - GMT
+ *
+ * Caveat: the validity of this calculation depends on two
+ * assumptions:
+ * 1. Daylight saving time was not in effect on Jan. 2, 1970.
+ * 2. The time zone of the geographic location has not changed
+ * since Jan. 2, 1970.
+ */
+
+ secs = 86400L;
+ (void) MT_safe_localtime(&secs, &localTime);
+
+ /* GMT is 00:00:00, 2nd of Jan. */
+
+ offset2Jan1970 = (PRInt32)localTime.tm_sec
+ + 60L * (PRInt32)localTime.tm_min
+ + 3600L * (PRInt32)localTime.tm_hour
+ + 86400L * (PRInt32)((PRInt32)localTime.tm_mday - 2L);
+
+ isdst2Jan1970 = localTime.tm_isdst;
+
+ /*
+ * Now compute DST offset. We calculate the overall offset
+ * of local time from GMT, similar to above. The overall
+ * offset has two components: gmt offset and dst offset.
+ * We subtract gmt offset from the overall offset to get
+ * the dst offset.
+ * overall_offset = local_time - GMT
+ * overall_offset = gmt_offset + dst_offset
+ * ==> dst_offset = local_time - GMT - gmt_offset
+ */
+
+ secs64 = PR_ImplodeTime(gmt); /* This is still in microseconds */
+ LL_I2L(usecPerSec, PR_USEC_PER_SEC);
+ LL_DIV(secs64, secs64, usecPerSec); /* Convert to seconds */
+ LL_I2L(maxInt32, 0x7fffffff);
+ if (LL_CMP(secs64, >, maxInt32)) {
+ /* secs64 is too large for time_t (32-bit integer) */
+ retVal.tp_gmt_offset = offset2Jan1970;
+ retVal.tp_dst_offset = 0;
+ return retVal;
+ }
+ LL_L2I(secs, secs64);
+
+ /*
+ * On Windows, localtime() (and our MT_safe_localtime() too)
+ * returns a NULL pointer for time before midnight January 1,
+ * 1970 GMT. In that case, we just use the GMT offset for
+ * Jan 2, 1970 and assume that DST was not in effect.
+ */
+
+ if (MT_safe_localtime(&secs, &localTime) == NULL) {
+ retVal.tp_gmt_offset = offset2Jan1970;
+ retVal.tp_dst_offset = 0;
+ return retVal;
+ }
+
+ /*
+ * dayOffset is the offset between local time and GMT in
+ * the day component, which can only be -1, 0, or 1. We
+ * use the day of the week to compute dayOffset.
+ */
+
+ dayOffset = (PRInt32) localTime.tm_wday - gmt->tm_wday;
+
+ /*
+ * Need to adjust for wrapping around of day of the week from
+ * 6 back to 0.
+ */
+
+ if (dayOffset == -6) {
+ /* Local time is Sunday (0) and GMT is Saturday (6) */
+ dayOffset = 1;
+ } else if (dayOffset == 6) {
+ /* Local time is Saturday (6) and GMT is Sunday (0) */
+ dayOffset = -1;
+ }
+
+ offsetNew = (PRInt32)localTime.tm_sec - gmt->tm_sec
+ + 60L * ((PRInt32)localTime.tm_min - gmt->tm_min)
+ + 3600L * ((PRInt32)localTime.tm_hour - gmt->tm_hour)
+ + 86400L * (PRInt32)dayOffset;
+
+ if (localTime.tm_isdst <= 0) {
+ /* DST is not in effect */
+ retVal.tp_gmt_offset = offsetNew;
+ retVal.tp_dst_offset = 0;
+ } else {
+ /* DST is in effect */
+ if (isdst2Jan1970 <=0) {
+ /*
+ * DST was not in effect back in 2 Jan. 1970.
+ * Use the offset back then as the GMT offset,
+ * assuming the time zone has not changed since then.
+ */
+ retVal.tp_gmt_offset = offset2Jan1970;
+ retVal.tp_dst_offset = offsetNew - offset2Jan1970;
+ } else {
+ /*
+ * DST was also in effect back in 2 Jan. 1970.
+ * Then our clever trick (or rather, ugly hack) fails.
+ * We will just assume DST offset is an hour.
+ */
+ retVal.tp_gmt_offset = offsetNew - 3600;
+ retVal.tp_dst_offset = 3600;
+ }
+ }
+
+ return retVal;
+}
+
+#endif /* defined(XP_UNIX) !! defined(XP_PC) */
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * PR_USPacificTimeParameters --
+ *
+ * The time parameters function for the US Pacific Time Zone.
+ *
+ *------------------------------------------------------------------------
+ */
+
+PR_IMPLEMENT(PRTimeParameters)
+PR_USPacificTimeParameters(const PRExplodedTime *gmt)
+{
+ PRTimeParameters retVal;
+ PRExplodedTime std;
+
+ /*
+ * Based on geographic location and GMT, figure out offset of
+ * standard time from GMT. In this example implementation, we
+ * assume the local time zone is US Pacific Time.
+ */
+
+ retVal.tp_gmt_offset = -8L * 3600L;
+
+ /*
+ * Make a copy of GMT. Note that the tm_params field of this copy
+ * is ignored.
+ */
+
+ std.tm_usec = gmt->tm_usec;
+ std.tm_sec = gmt->tm_sec;
+ std.tm_min = gmt->tm_min;
+ std.tm_hour = gmt->tm_hour;
+ std.tm_mday = gmt->tm_mday;
+ std.tm_month = gmt->tm_month;
+ std.tm_year = gmt->tm_year;
+ std.tm_wday = gmt->tm_wday;
+ std.tm_yday = gmt->tm_yday;
+
+ /* Apply the offset to GMT to obtain the local standard time */
+ ApplySecOffset(&std, retVal.tp_gmt_offset);
+
+ /*
+ * Apply the rules on standard time or GMT to obtain daylight saving
+ * time offset. In this implementation, we use the US DST rule.
+ */
+ if (std.tm_month < 3) {
+ retVal.tp_dst_offset = 0L;
+ } else if (std.tm_month == 3) {
+ if (std.tm_wday == 0) {
+ /* A Sunday */
+ if (std.tm_mday <= 7) {
+ /* First Sunday */
+ /* 01:59:59 PST -> 03:00:00 PDT */
+ if (std.tm_hour < 2) {
+ retVal.tp_dst_offset = 0L;
+ } else {
+ retVal.tp_dst_offset = 3600L;
+ }
+ } else {
+ /* Not first Sunday */
+ retVal.tp_dst_offset = 3600L;
+ }
+ } else {
+ /* Not a Sunday. See if before first Sunday or after */
+ if (std.tm_wday + 1 <= std.tm_mday) {
+ /* After first Sunday */
+ retVal.tp_dst_offset = 3600L;
+ } else {
+ /* Before first Sunday */
+ retVal.tp_dst_offset = 0L;
+ }
+ }
+ } else if (std.tm_month < 9) {
+ retVal.tp_dst_offset = 3600L;
+ } else if (std.tm_month == 9) {
+ if (std.tm_wday == 0) {
+ if (31 - std.tm_mday < 7) {
+ /* Last Sunday */
+ /* 01:59:59 PDT -> 01:00:00 PST */
+ if (std.tm_hour < 1) {
+ retVal.tp_dst_offset = 3600L;
+ } else {
+ retVal.tp_dst_offset = 0L;
+ }
+ } else {
+ /* Not last Sunday */
+ retVal.tp_dst_offset = 3600L;
+ }
+ } else {
+ /* See if before or after last Sunday */
+ if (7 - std.tm_wday <= 31 - std.tm_mday) {
+ /* before last Sunday */
+ retVal.tp_dst_offset = 3600L;
+ } else {
+ retVal.tp_dst_offset = 0L;
+ }
+ }
+ } else {
+ retVal.tp_dst_offset = 0L;
+ }
+ return retVal;
+}
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * PR_GMTParameters --
+ *
+ * Returns the PRTimeParameters for Greenwich Mean Time.
+ * Trivially, both the tp_gmt_offset and tp_dst_offset fields are 0.
+ *
+ *------------------------------------------------------------------------
+ */
+
+PR_IMPLEMENT(PRTimeParameters)
+PR_GMTParameters(const PRExplodedTime *gmt)
+{
+#if defined(XP_MAC)
+#pragma unused (gmt)
+#endif
+
+ PRTimeParameters retVal = { 0, 0 };
+ return retVal;
+}
+
+/*
+ * The following code implements PR_ParseTimeString(). It is based on
+ * ns/lib/xp/xp_time.c, revision 1.25, by Jamie Zawinski <jwz@netscape.com>.
+ */
+
+/*
+ * We only recognize the abbreviations of a small subset of time zones
+ * in North America, Europe, and Japan.
+ *
+ * PST/PDT: Pacific Standard/Daylight Time
+ * MST/MDT: Mountain Standard/Daylight Time
+ * CST/CDT: Central Standard/Daylight Time
+ * EST/EDT: Eastern Standard/Daylight Time
+ * AST: Atlantic Standard Time
+ * NST: Newfoundland Standard Time
+ * GMT: Greenwich Mean Time
+ * BST: British Summer Time
+ * MET: Middle Europe Time
+ * EET: Eastern Europe Time
+ * JST: Japan Standard Time
+ */
+
+typedef enum
+{
+ TT_UNKNOWN,
+
+ TT_SUN, TT_MON, TT_TUE, TT_WED, TT_THU, TT_FRI, TT_SAT,
+
+ TT_JAN, TT_FEB, TT_MAR, TT_APR, TT_MAY, TT_JUN,
+ TT_JUL, TT_AUG, TT_SEP, TT_OCT, TT_NOV, TT_DEC,
+
+ TT_PST, TT_PDT, TT_MST, TT_MDT, TT_CST, TT_CDT, TT_EST, TT_EDT,
+ TT_AST, TT_NST, TT_GMT, TT_BST, TT_MET, TT_EET, TT_JST
+} TIME_TOKEN;
+
+/*
+ * This parses a time/date string into a PRTime
+ * (microseconds after "1-Jan-1970 00:00:00 GMT").
+ * It returns PR_SUCCESS on success, and PR_FAILURE
+ * if the time/date string can't be parsed.
+ *
+ * Many formats are handled, including:
+ *
+ * 14 Apr 89 03:20:12
+ * 14 Apr 89 03:20 GMT
+ * Fri, 17 Mar 89 4:01:33
+ * Fri, 17 Mar 89 4:01 GMT
+ * Mon Jan 16 16:12 PDT 1989
+ * Mon Jan 16 16:12 +0130 1989
+ * 6 May 1992 16:41-JST (Wednesday)
+ * 22-AUG-1993 10:59:12.82
+ * 22-AUG-1993 10:59pm
+ * 22-AUG-1993 12:59am
+ * 22-AUG-1993 12:59 PM
+ * Friday, August 04, 1995 3:54 PM
+ * 06/21/95 04:24:34 PM
+ * 20/06/95 21:07
+ * 95-06-08 19:32:48 EDT
+ *
+ * If the input string doesn't contain a description of the timezone,
+ * we consult the `default_to_gmt' to decide whether the string should
+ * be interpreted relative to the local time zone (PR_FALSE) or GMT (PR_TRUE).
+ * The correct value for this argument depends on what standard specified
+ * the time string which you are parsing.
+ */
+
+PR_IMPLEMENT(PRStatus)
+PR_ParseTimeString(
+ const char *string,
+ PRBool default_to_gmt,
+ PRTime *result)
+{
+ PRExplodedTime tm;
+ TIME_TOKEN dotw = TT_UNKNOWN;
+ TIME_TOKEN month = TT_UNKNOWN;
+ TIME_TOKEN zone = TT_UNKNOWN;
+ int zone_offset = -1;
+ int date = -1;
+ PRInt32 year = -1;
+ int hour = -1;
+ int min = -1;
+ int sec = -1;
+
+ const char *rest = string;
+
+#ifdef DEBUG
+ int iterations = 0;
+#endif
+
+ PR_ASSERT(string && result);
+ if (!string || !result) return PR_FAILURE;
+
+ while (*rest)
+ {
+
+#ifdef DEBUG
+ if (iterations++ > 1000)
+ {
+ PR_ASSERT(0);
+ return PR_FAILURE;
+ }
+#endif
+
+ switch (*rest)
+ {
+ case 'a': case 'A':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'p' || rest[1] == 'P') &&
+ (rest[2] == 'r' || rest[2] == 'R'))
+ month = TT_APR;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 's') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_AST;
+ else if (month == TT_UNKNOWN &&
+ (rest[1] == 'u' || rest[1] == 'U') &&
+ (rest[2] == 'g' || rest[2] == 'G'))
+ month = TT_AUG;
+ break;
+ case 'b': case 'B':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_BST;
+ break;
+ case 'c': case 'C':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 'd' || rest[1] == 'D') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_CDT;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_CST;
+ break;
+ case 'd': case 'D':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 'c' || rest[2] == 'C'))
+ month = TT_DEC;
+ break;
+ case 'e': case 'E':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 'd' || rest[1] == 'D') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_EDT;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_EET;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_EST;
+ break;
+ case 'f': case 'F':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 'b' || rest[2] == 'B'))
+ month = TT_FEB;
+ else if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'r' || rest[1] == 'R') &&
+ (rest[2] == 'i' || rest[2] == 'I'))
+ dotw = TT_FRI;
+ break;
+ case 'g': case 'G':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 'm' || rest[1] == 'M') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_GMT;
+ break;
+ case 'j': case 'J':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'a' || rest[1] == 'A') &&
+ (rest[2] == 'n' || rest[2] == 'N'))
+ month = TT_JAN;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_JST;
+ else if (month == TT_UNKNOWN &&
+ (rest[1] == 'u' || rest[1] == 'U') &&
+ (rest[2] == 'l' || rest[2] == 'L'))
+ month = TT_JUL;
+ else if (month == TT_UNKNOWN &&
+ (rest[1] == 'u' || rest[1] == 'U') &&
+ (rest[2] == 'n' || rest[2] == 'N'))
+ month = TT_JUN;
+ break;
+ case 'm': case 'M':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'a' || rest[1] == 'A') &&
+ (rest[2] == 'r' || rest[2] == 'R'))
+ month = TT_MAR;
+ else if (month == TT_UNKNOWN &&
+ (rest[1] == 'a' || rest[1] == 'A') &&
+ (rest[2] == 'y' || rest[2] == 'Y'))
+ month = TT_MAY;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 'd' || rest[1] == 'D') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_MDT;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_MET;
+ else if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'o' || rest[1] == 'O') &&
+ (rest[2] == 'n' || rest[2] == 'N'))
+ dotw = TT_MON;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_MST;
+ break;
+ case 'n': case 'N':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'o' || rest[1] == 'O') &&
+ (rest[2] == 'v' || rest[2] == 'V'))
+ month = TT_NOV;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_NST;
+ break;
+ case 'o': case 'O':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'c' || rest[1] == 'C') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ month = TT_OCT;
+ break;
+ case 'p': case 'P':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 'd' || rest[1] == 'D') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_PDT;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_PST;
+ break;
+ case 's': case 'S':
+ if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'a' || rest[1] == 'A') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ dotw = TT_SAT;
+ else if (month == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 'p' || rest[2] == 'P'))
+ month = TT_SEP;
+ else if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'u' || rest[1] == 'U') &&
+ (rest[2] == 'n' || rest[2] == 'N'))
+ dotw = TT_SUN;
+ break;
+ case 't': case 'T':
+ if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'h' || rest[1] == 'H') &&
+ (rest[2] == 'u' || rest[2] == 'U'))
+ dotw = TT_THU;
+ else if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'u' || rest[1] == 'U') &&
+ (rest[2] == 'e' || rest[2] == 'E'))
+ dotw = TT_TUE;
+ break;
+ case 'u': case 'U':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 't' || rest[1] == 'T') &&
+ !(rest[2] >= 'A' && rest[2] <= 'Z') &&
+ !(rest[2] >= 'a' && rest[2] <= 'z'))
+ /* UT is the same as GMT but UTx is not. */
+ zone = TT_GMT;
+ break;
+ case 'w': case 'W':
+ if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 'd' || rest[2] == 'D'))
+ dotw = TT_WED;
+ break;
+
+ case '+': case '-':
+ {
+ const char *end;
+ int sign;
+ if (zone_offset >= 0)
+ {
+ /* already got one... */
+ rest++;
+ break;
+ }
+ if (zone != TT_UNKNOWN && zone != TT_GMT)
+ {
+ /* GMT+0300 is legal, but PST+0300 is not. */
+ rest++;
+ break;
+ }
+
+ sign = ((*rest == '+') ? 1 : -1);
+ rest++; /* move over sign */
+ end = rest;
+ while (*end >= '0' && *end <= '9')
+ end++;
+ if (rest == end) /* no digits here */
+ break;
+
+ if ((end - rest) == 4)
+ /* offset in HHMM */
+ zone_offset = (((((rest[0]-'0')*10) + (rest[1]-'0')) * 60) +
+ (((rest[2]-'0')*10) + (rest[3]-'0')));
+ else if ((end - rest) == 2)
+ /* offset in hours */
+ zone_offset = (((rest[0]-'0')*10) + (rest[1]-'0')) * 60;
+ else if ((end - rest) == 1)
+ /* offset in hours */
+ zone_offset = (rest[0]-'0') * 60;
+ else
+ /* 3 or >4 */
+ break;
+
+ zone_offset *= sign;
+ zone = TT_GMT;
+ break;
+ }
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ int tmp_hour = -1;
+ int tmp_min = -1;
+ int tmp_sec = -1;
+ const char *end = rest + 1;
+ while (*end >= '0' && *end <= '9')
+ end++;
+
+ /* end is now the first character after a range of digits. */
+
+ if (*end == ':')
+ {
+ if (hour > 0 && min > 0) /* already got it */
+ break;
+
+ /* We have seen "[0-9]+:", so this is probably HH:MM[:SS] */
+ if ((end - rest) > 2)
+ /* it is [0-9][0-9][0-9]+: */
+ break;
+ else if (rest[1] != ':' &&
+ rest[2] != ':')
+ /* it is not [0-9]: or [0-9][0-9]: */
+ break;
+ else if ((end - rest) == 2)
+ tmp_hour = ((rest[0]-'0')*10 +
+ (rest[1]-'0'));
+ else
+ tmp_hour = (rest[0]-'0');
+
+ while (*rest && *rest != ':')
+ rest++;
+ rest++;
+
+ /* move over the colon, and parse minutes */
+
+ end = rest + 1;
+ while (*end >= '0' && *end <= '9')
+ end++;
+
+ if (end == rest)
+ /* no digits after first colon? */
+ break;
+ else if ((end - rest) > 2)
+ /* it is [0-9][0-9][0-9]+: */
+ break;
+ else if ((end - rest) == 2)
+ tmp_min = ((rest[0]-'0')*10 +
+ (rest[1]-'0'));
+ else
+ tmp_min = (rest[0]-'0');
+
+ /* now go for seconds */
+ rest = end;
+ if (*rest == ':')
+ rest++;
+ end = rest;
+ while (*end >= '0' && *end <= '9')
+ end++;
+
+ if (end == rest)
+ /* no digits after second colon - that's ok. */
+ ;
+ else if ((end - rest) > 2)
+ /* it is [0-9][0-9][0-9]+: */
+ break;
+ else if ((end - rest) == 2)
+ tmp_sec = ((rest[0]-'0')*10 +
+ (rest[1]-'0'));
+ else
+ tmp_sec = (rest[0]-'0');
+
+ /* If we made it here, we've parsed hour and min,
+ and possibly sec, so it worked as a unit. */
+
+ /* skip over whitespace and see if there's an AM or PM
+ directly following the time.
+ */
+ if (tmp_hour <= 12)
+ {
+ const char *s = end;
+ while (*s && (*s == ' ' || *s == '\t'))
+ s++;
+ if ((s[0] == 'p' || s[0] == 'P') &&
+ (s[1] == 'm' || s[1] == 'M'))
+ /* 10:05pm == 22:05, and 12:05pm == 12:05 */
+ tmp_hour = (tmp_hour == 12 ? 12 : tmp_hour + 12);
+ else if (tmp_hour == 12 &&
+ (s[0] == 'a' || s[0] == 'A') &&
+ (s[1] == 'm' || s[1] == 'M'))
+ /* 12:05am == 00:05 */
+ tmp_hour = 0;
+ }
+
+ hour = tmp_hour;
+ min = tmp_min;
+ sec = tmp_sec;
+ rest = end;
+ break;
+ }
+ else if ((*end == '/' || *end == '-') &&
+ end[1] >= '0' && end[1] <= '9')
+ {
+ /* Perhaps this is 6/16/95, 16/6/95, 6-16-95, or 16-6-95
+ or even 95-06-05...
+ #### But it doesn't handle 1995-06-22.
+ */
+ int n1, n2, n3;
+ const char *s;
+
+ if (month != TT_UNKNOWN)
+ /* if we saw a month name, this can't be. */
+ break;
+
+ s = rest;
+
+ n1 = (*s++ - '0'); /* first 1 or 2 digits */
+ if (*s >= '0' && *s <= '9')
+ n1 = n1*10 + (*s++ - '0');
+
+ if (*s != '/' && *s != '-') /* slash */
+ break;
+ s++;
+
+ if (*s < '0' || *s > '9') /* second 1 or 2 digits */
+ break;
+ n2 = (*s++ - '0');
+ if (*s >= '0' && *s <= '9')
+ n2 = n2*10 + (*s++ - '0');
+
+ if (*s != '/' && *s != '-') /* slash */
+ break;
+ s++;
+
+ if (*s < '0' || *s > '9') /* third 1, 2, or 4 digits */
+ break;
+ n3 = (*s++ - '0');
+ if (*s >= '0' && *s <= '9')
+ n3 = n3*10 + (*s++ - '0');
+
+ if (*s >= '0' && *s <= '9') /* optional digits 3 and 4 */
+ {
+ n3 = n3*10 + (*s++ - '0');
+ if (*s < '0' || *s > '9')
+ break;
+ n3 = n3*10 + (*s++ - '0');
+ }
+
+ if ((*s >= '0' && *s <= '9') || /* followed by non-alphanum */
+ (*s >= 'A' && *s <= 'Z') ||
+ (*s >= 'a' && *s <= 'z'))
+ break;
+
+ /* Ok, we parsed three 1-2 digit numbers, with / or -
+ between them. Now decide what the hell they are
+ (DD/MM/YY or MM/DD/YY or YY/MM/DD.)
+ */
+
+ if (n1 > 70) /* must be YY/MM/DD */
+ {
+ if (n2 > 12) break;
+ if (n3 > 31) break;
+ year = n1;
+ if (year < 1900) year += 1900;
+ month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1);
+ date = n3;
+ rest = s;
+ break;
+ }
+
+ if (n3 < 70 || /* before epoch - can't represent it. */
+ (n1 > 12 && n2 > 12)) /* illegal */
+ {
+ rest = s;
+ break;
+ }
+
+ if (n3 < 1900) n3 += 1900;
+
+ if (n1 > 12) /* must be DD/MM/YY */
+ {
+ date = n1;
+ month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1);
+ year = n3;
+ }
+ else /* assume MM/DD/YY */
+ {
+ /* #### In the ambiguous case, should we consult the
+ locale to find out the local default? */
+ month = (TIME_TOKEN)(n1 + ((int)TT_JAN) - 1);
+ date = n2;
+ year = n3;
+ }
+ rest = s;
+ }
+ else if ((*end >= 'A' && *end <= 'Z') ||
+ (*end >= 'a' && *end <= 'z'))
+ /* Digits followed by non-punctuation - what's that? */
+ ;
+ else if ((end - rest) == 4) /* four digits is a year */
+ year = (year < 0
+ ? ((rest[0]-'0')*1000L +
+ (rest[1]-'0')*100L +
+ (rest[2]-'0')*10L +
+ (rest[3]-'0'))
+ : year);
+ else if ((end - rest) == 2) /* two digits - date or year */
+ {
+ int n = ((rest[0]-'0')*10 +
+ (rest[1]-'0'));
+ /* If we don't have a date (day of the month) and we see a number
+ less than 32, then assume that is the date.
+
+ Otherwise, if we have a date and not a year, assume this is the
+ year. If it is less than 70, then assume it refers to the 21st
+ century. If it is two digits (>= 70), assume it refers to this
+ century. Otherwise, assume it refers to an unambiguous year.
+
+ The world will surely end soon.
+ */
+ if (date < 0 && n < 32)
+ date = n;
+ else if (year < 0)
+ {
+ if (n < 70)
+ year = 2000 + n;
+ else if (n < 100)
+ year = 1900 + n;
+ else
+ year = n;
+ }
+ /* else what the hell is this. */
+ }
+ else if ((end - rest) == 1) /* one digit - date */
+ date = (date < 0 ? (rest[0]-'0') : date);
+ /* else, three or more than four digits - what's that? */
+
+ break;
+ }
+ }
+
+ /* Skip to the end of this token, whether we parsed it or not.
+ Tokens are delimited by whitespace, or ,;-/
+ But explicitly not :+-.
+ */
+ while (*rest &&
+ *rest != ' ' && *rest != '\t' &&
+ *rest != ',' && *rest != ';' &&
+ *rest != '-' && *rest != '+' &&
+ *rest != '/' &&
+ *rest != '(' && *rest != ')' && *rest != '[' && *rest != ']')
+ rest++;
+ /* skip over uninteresting chars. */
+ SKIP_MORE:
+ while (*rest &&
+ (*rest == ' ' || *rest == '\t' ||
+ *rest == ',' || *rest == ';' || *rest == '/' ||
+ *rest == '(' || *rest == ')' || *rest == '[' || *rest == ']'))
+ rest++;
+
+ /* "-" is ignored at the beginning of a token if we have not yet
+ parsed a year (e.g., the second "-" in "30-AUG-1966"), or if
+ the character after the dash is not a digit. */
+ if (*rest == '-' && ((rest > string && isalpha(rest[-1]) && year < 0)
+ || rest[1] < '0' || rest[1] > '9'))
+ {
+ rest++;
+ goto SKIP_MORE;
+ }
+
+ }
+
+ if (zone != TT_UNKNOWN && zone_offset == -1)
+ {
+ switch (zone)
+ {
+ case TT_PST: zone_offset = -8 * 60; break;
+ case TT_PDT: zone_offset = -7 * 60; break;
+ case TT_MST: zone_offset = -7 * 60; break;
+ case TT_MDT: zone_offset = -6 * 60; break;
+ case TT_CST: zone_offset = -6 * 60; break;
+ case TT_CDT: zone_offset = -5 * 60; break;
+ case TT_EST: zone_offset = -5 * 60; break;
+ case TT_EDT: zone_offset = -4 * 60; break;
+ case TT_AST: zone_offset = -4 * 60; break;
+ case TT_NST: zone_offset = -3 * 60 - 30; break;
+ case TT_GMT: zone_offset = 0 * 60; break;
+ case TT_BST: zone_offset = 1 * 60; break;
+ case TT_MET: zone_offset = 1 * 60; break;
+ case TT_EET: zone_offset = 2 * 60; break;
+ case TT_JST: zone_offset = 9 * 60; break;
+ default:
+ PR_ASSERT (0);
+ break;
+ }
+ }
+
+ /* If we didn't find a year, month, or day-of-the-month, we can't
+ possibly parse this, and in fact, mktime() will do something random
+ (I'm seeing it return "Tue Feb 5 06:28:16 2036", which is no doubt
+ a numerologically significant date... */
+ if (month == TT_UNKNOWN || date == -1 || year == -1)
+ return PR_FAILURE;
+
+ memset(&tm, 0, sizeof(tm));
+ if (sec != -1)
+ tm.tm_sec = sec;
+ if (min != -1)
+ tm.tm_min = min;
+ if (hour != -1)
+ tm.tm_hour = hour;
+ if (date != -1)
+ tm.tm_mday = date;
+ if (month != TT_UNKNOWN)
+ tm.tm_month = (((int)month) - ((int)TT_JAN));
+ if (year != -1)
+ tm.tm_year = year;
+ if (dotw != TT_UNKNOWN)
+ tm.tm_wday = (((int)dotw) - ((int)TT_SUN));
+
+ if (zone == TT_UNKNOWN && default_to_gmt)
+ {
+ /* No zone was specified, so pretend the zone was GMT. */
+ zone = TT_GMT;
+ zone_offset = 0;
+ }
+
+ if (zone_offset == -1)
+ {
+ /* no zone was specified, and we're to assume that everything
+ is local. */
+ struct tm localTime;
+ time_t secs;
+
+ PR_ASSERT(tm.tm_month > -1
+ && tm.tm_mday > 0
+ && tm.tm_hour > -1
+ && tm.tm_min > -1
+ && tm.tm_sec > -1);
+
+ /*
+ * To obtain time_t from a tm structure representing the local
+ * time, we call mktime(). However, we need to see if we are
+ * on 1-Jan-1970 or before. If we are, we can't call mktime()
+ * because mktime() will crash on win16. In that case, we
+ * calculate zone_offset based on the zone offset at
+ * 00:00:00, 2 Jan 1970 GMT, and subtract zone_offset from the
+ * date we are parsing to transform the date to GMT. We also
+ * do so if mktime() returns (time_t) -1 (time out of range).
+ */
+
+ /* month, day, hours, mins and secs are always non-negative
+ so we dont need to worry about them. */
+ if(tm.tm_year >= 1970)
+ {
+ PRInt64 usec_per_sec;
+
+ localTime.tm_sec = tm.tm_sec;
+ localTime.tm_min = tm.tm_min;
+ localTime.tm_hour = tm.tm_hour;
+ localTime.tm_mday = tm.tm_mday;
+ localTime.tm_mon = tm.tm_month;
+ localTime.tm_year = tm.tm_year - 1900;
+ /* Set this to -1 to tell mktime "I don't care". If you set
+ it to 0 or 1, you are making assertions about whether the
+ date you are handing it is in daylight savings mode or not;
+ and if you're wrong, it will "fix" it for you. */
+ localTime.tm_isdst = -1;
+ secs = mktime(&localTime);
+ if (secs != (time_t) -1)
+ {
+ LL_I2L(*result, secs);
+ LL_I2L(usec_per_sec, PR_USEC_PER_SEC);
+ LL_MUL(*result, *result, usec_per_sec);
+ return PR_SUCCESS;
+ }
+ }
+
+ /* So mktime() can't handle this case. We assume the
+ zone_offset for the date we are parsing is the same as
+ the zone offset on 00:00:00 2 Jan 1970 GMT. */
+ secs = 86400;
+ (void) MT_safe_localtime(&secs, &localTime);
+ zone_offset = localTime.tm_min
+ + 60 * localTime.tm_hour
+ + 1440 * (localTime.tm_mday - 2);
+ }
+
+ /* Adjust the hours and minutes before handing them to
+ PR_ImplodeTime(). Note that it's ok for them to be <0 or >24/60
+
+ We adjust the time to GMT before going into PR_ImplodeTime().
+ The zone_offset represents the difference between the time
+ zone parsed and GMT
+ */
+ tm.tm_hour -= (zone_offset / 60);
+ tm.tm_min -= (zone_offset % 60);
+
+ *result = PR_ImplodeTime(&tm);
+
+ return PR_SUCCESS;
+}
+
+/*
+ *******************************************************************
+ *******************************************************************
+ **
+ ** OLD COMPATIBILITY FUNCTIONS
+ **
+ *******************************************************************
+ *******************************************************************
+ */
+
+
+/*
+ *-----------------------------------------------------------------------
+ *
+ * PR_FormatTime --
+ *
+ * Format a time value into a buffer. Same semantics as strftime().
+ *
+ *-----------------------------------------------------------------------
+ */
+
+PR_IMPLEMENT(PRUint32)
+PR_FormatTime(char *buf, int buflen, char *fmt, const PRExplodedTime *tm)
+{
+ struct tm a;
+ a.tm_sec = tm->tm_sec;
+ a.tm_min = tm->tm_min;
+ a.tm_hour = tm->tm_hour;
+ a.tm_mday = tm->tm_mday;
+ a.tm_mon = tm->tm_month;
+ a.tm_wday = tm->tm_wday;
+ a.tm_year = tm->tm_year - 1900;
+ a.tm_yday = tm->tm_yday;
+ a.tm_isdst = tm->tm_params.tp_dst_offset ? 1 : 0;
+
+/*
+ * On SunOS 4, struct tm has two additional fields: tm_zone
+ * and tm_gmtoff. The following code attempts to obtain values for
+ * these two fields.
+ */
+
+#if defined(SUNOS4) || defined(MACLINUX) || (__GLIBC__ >= 2)
+ if (mktime(&a) == -1) {
+ PR_snprintf(buf, buflen, "can't get timezone");
+ return 0;
+ }
+#endif
+
+ return strftime(buf, buflen, fmt, &a);
+}
+
+
+/*
+ * The following string arrays and macros are used by PR_FormatTimeUSEnglish().
+ */
+
+static const char* abbrevDays[] =
+{
+ "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
+};
+
+static const char* days[] =
+{
+ "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"
+};
+
+static const char* abbrevMonths[] =
+{
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+static const char* months[] =
+{
+ "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"
+};
+
+
+/*
+ * Add a single character to the given buffer, incrementing the buffer pointer
+ * and decrementing the buffer size. Return 0 on error.
+ */
+#define ADDCHAR( buf, bufSize, ch ) \
+do \
+{ \
+ if( bufSize < 1 ) \
+ { \
+ *(--buf) = '\0'; \
+ return 0; \
+ } \
+ *buf++ = ch; \
+ bufSize--; \
+} \
+while(0)
+
+
+/*
+ * Add a string to the given buffer, incrementing the buffer pointer
+ * and decrementing the buffer size appropriately. Return 0 on error.
+ */
+#define ADDSTR( buf, bufSize, str ) \
+do \
+{ \
+ PRUint32 strSize = strlen( str ); \
+ if( strSize > bufSize ) \
+ { \
+ if( bufSize==0 ) \
+ *(--buf) = '\0'; \
+ else \
+ *buf = '\0'; \
+ return 0; \
+ } \
+ memcpy(buf, str, strSize); \
+ buf += strSize; \
+ bufSize -= strSize; \
+} \
+while(0)
+
+/* Needed by PR_FormatTimeUSEnglish() */
+static unsigned int pr_WeekOfYear(const PRExplodedTime* time,
+ unsigned int firstDayOfWeek);
+
+
+/***********************************************************************************
+ *
+ * Description:
+ * This is a dumbed down version of strftime that will format the date in US
+ * English regardless of the setting of the global locale. This functionality is
+ * needed to write things like MIME headers which must always be in US English.
+ *
+ **********************************************************************************/
+
+PR_IMPLEMENT(PRUint32)
+PR_FormatTimeUSEnglish( char* buf, PRUint32 bufSize,
+ const char* format, const PRExplodedTime* time )
+{
+ char* bufPtr = buf;
+ const char* fmtPtr;
+ char tmpBuf[ 40 ];
+ const int tmpBufSize = sizeof( tmpBuf );
+
+
+ for( fmtPtr=format; *fmtPtr != '\0'; fmtPtr++ )
+ {
+ if( *fmtPtr != '%' )
+ {
+ ADDCHAR( bufPtr, bufSize, *fmtPtr );
+ }
+ else
+ {
+ switch( *(++fmtPtr) )
+ {
+ case '%':
+ /* escaped '%' character */
+ ADDCHAR( bufPtr, bufSize, '%' );
+ break;
+
+ case 'a':
+ /* abbreviated weekday name */
+ ADDSTR( bufPtr, bufSize, abbrevDays[ time->tm_wday ] );
+ break;
+
+ case 'A':
+ /* full weekday name */
+ ADDSTR( bufPtr, bufSize, days[ time->tm_wday ] );
+ break;
+
+ case 'b':
+ /* abbreviated month name */
+ ADDSTR( bufPtr, bufSize, abbrevMonths[ time->tm_month ] );
+ break;
+
+ case 'B':
+ /* full month name */
+ ADDSTR(bufPtr, bufSize, months[ time->tm_month ] );
+ break;
+
+ case 'c':
+ /* Date and time. */
+ PR_FormatTimeUSEnglish( tmpBuf, tmpBufSize, "%a %b %d %H:%M:%S %Y", time );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'd':
+ /* day of month ( 01 - 31 ) */
+ PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_mday );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'H':
+ /* hour ( 00 - 23 ) */
+ PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_hour );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'I':
+ /* hour ( 01 - 12 ) */
+ PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",
+ (time->tm_hour%12) ? time->tm_hour%12 : (PRInt32) 12 );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'j':
+ /* day number of year ( 001 - 366 ) */
+ PR_snprintf(tmpBuf,tmpBufSize,"%.3d",time->tm_yday + 1);
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'm':
+ /* month number ( 01 - 12 ) */
+ PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_month+1);
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'M':
+ /* minute ( 00 - 59 ) */
+ PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_min );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'p':
+ /* locale's equivalent of either AM or PM */
+ ADDSTR( bufPtr, bufSize, (time->tm_hour<12)?"AM":"PM" );
+ break;
+
+ case 'S':
+ /* seconds ( 00 - 61 ), allows for leap seconds */
+ PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_sec );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'U':
+ /* week number of year ( 00 - 53 ), Sunday is the first day of week 1 */
+ PR_snprintf(tmpBuf,tmpBufSize,"%.2d", pr_WeekOfYear( time, 0 ) );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'w':
+ /* weekday number ( 0 - 6 ), Sunday = 0 */
+ PR_snprintf(tmpBuf,tmpBufSize,"%d",time->tm_wday );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'W':
+ /* Week number of year ( 00 - 53 ), Monday is the first day of week 1 */
+ PR_snprintf(tmpBuf,tmpBufSize,"%.2d", pr_WeekOfYear( time, 1 ) );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'x':
+ /* Date representation */
+ PR_FormatTimeUSEnglish( tmpBuf, tmpBufSize, "%m/%d/%y", time );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'X':
+ /* Time representation. */
+ PR_FormatTimeUSEnglish( tmpBuf, tmpBufSize, "%H:%M:%S", time );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'y':
+ /* year within century ( 00 - 99 ) */
+ PR_snprintf(tmpBuf,tmpBufSize,"%.2d",time->tm_year % 100 );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'Y':
+ /* year as ccyy ( for example 1986 ) */
+ PR_snprintf(tmpBuf,tmpBufSize,"%.4d",time->tm_year );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ case 'Z':
+ /* Time zone name or no characters if no time zone exists.
+ * Since time zone name is supposed to be independant of locale, we
+ * defer to PR_FormatTime() for this option.
+ */
+ PR_FormatTime( tmpBuf, tmpBufSize, "%Z", time );
+ ADDSTR( bufPtr, bufSize, tmpBuf );
+ break;
+
+ default:
+ /* Unknown format. Simply copy format into output buffer. */
+ ADDCHAR( bufPtr, bufSize, '%' );
+ ADDCHAR( bufPtr, bufSize, *fmtPtr );
+ break;
+
+ }
+ }
+ }
+
+ ADDCHAR( bufPtr, bufSize, '\0' );
+ return (PRUint32)(bufPtr - buf - 1);
+}
+
+
+
+/***********************************************************************************
+ *
+ * Description:
+ * Returns the week number of the year (0-53) for the given time. firstDayOfWeek
+ * is the day on which the week is considered to start (0=Sun, 1=Mon, ...).
+ * Week 1 starts the first time firstDayOfWeek occurs in the year. In other words,
+ * a partial week at the start of the year is considered week 0.
+ *
+ **********************************************************************************/
+
+static unsigned int
+pr_WeekOfYear(const PRExplodedTime* time, unsigned int firstDayOfWeek)
+{
+ int dayOfWeek;
+ int dayOfYear;
+
+ /* Get the day of the year for the given time then adjust it to represent the
+ * first day of the week containing the given time.
+ */
+ dayOfWeek = time->tm_wday - firstDayOfWeek;
+ if (dayOfWeek < 0)
+ dayOfWeek += 7;
+
+ dayOfYear = time->tm_yday - dayOfWeek;
+
+
+ if( dayOfYear <= 0 )
+ {
+ /* If dayOfYear is <= 0, it is in the first partial week of the year. */
+ return 0;
+ }
+ else
+ {
+ /* Count the number of full weeks ( dayOfYear / 7 ) then add a week if there
+ * are any days left over ( dayOfYear % 7 ). Because we are only counting to
+ * the first day of the week containing the given time, rather than to the
+ * actual day representing the given time, any days in week 0 will be "absorbed"
+ * as extra days in the given week.
+ */
+ return (dayOfYear / 7) + ( (dayOfYear % 7) == 0 ? 0 : 1 );
+ }
+}
+
diff --git a/pr/src/nspr.rc b/pr/src/nspr.rc
new file mode 100644
index 00000000..1b9b146c
--- /dev/null
+++ b/pr/src/nspr.rc
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#ifndef WIN16
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+
+// end win16
+#endif
+
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Netscape Communications Corporation\0"
+ VALUE "FileDescription", "Netscape Portable Run Time\0"
+ VALUE "FileVersion", "1, 0, 0, 1\0"
+ VALUE "InternalName", "nspr20\0"
+ VALUE "LegalCopyright", "Copyright © 1996\0"
+ VALUE "OriginalFilename", "nspr20.dll\0"
+ VALUE "ProductName", "Netscape Communication Corporation NSPR20\0"
+ VALUE "ProductVersion", "1, 0, 0, 1\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // !_MAC
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/pr/src/pthreads/Makefile b/pr/src/pthreads/Makefile
new file mode 100644
index 00000000..1a21d806
--- /dev/null
+++ b/pr/src/pthreads/Makefile
@@ -0,0 +1,47 @@
+#
+# The contents of this file are subject to the Netscape Public License
+# Version 1.0 (the "NPL"); you may not use this file except in
+# compliance with the NPL. You may obtain a copy of the NPL at
+# http://www.mozilla.org/NPL/
+#
+# Software distributed under the NPL is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+# for the specific language governing rights and limitations under the
+# NPL.
+#
+# The Initial Developer of this code under the NPL is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1998 Netscape Communications Corporation. All Rights
+# Reserved.
+#
+
+#! gmake
+
+MOD_DEPTH = ../../..
+
+include $(MOD_DEPTH)/config/config.mk
+
+# Disable optimization of the nspr on SunOS4.1.3
+ifeq ($(OS_ARCH),SunOS)
+ifeq ($(OS_RELEASE),4.1.3_U1)
+OPTIMIZER =
+endif
+endif
+
+CSRCS = \
+ ptio.c \
+ ptsynch.c \
+ ptthread.c \
+ ptmisc.c \
+ $(NULL)
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(DIST)/include/private -I$(DIST)/include
+
+include $(MOD_DEPTH)/config/rules.mk
+
+export:: $(TARGETS)
+
+install:: export
+
diff --git a/pr/src/pthreads/ptio.c b/pr/src/pthreads/ptio.c
new file mode 100644
index 00000000..171877e7
--- /dev/null
+++ b/pr/src/pthreads/ptio.c
@@ -0,0 +1,3381 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+** File: ptio.c
+** Descritpion: Implemenation of I/O methods for pthreads
+** Exports: ptio.h
+*/
+
+#if defined(_PR_PTHREADS)
+
+#include <string.h> /* for memset() */
+#include <sys/types.h>
+#include <dirent.h>
+
+/*
+ * XXX: On Linux 2.0.27 sched.h uses this _P macro that seems to be undefined.
+ * I suspect that it is a typo (should be __P).
+ */
+#if defined(LINUX)
+#define _P(x) __P(x)
+#endif
+#include <pthread.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#ifdef SOLARIS
+#include <sys/filio.h> /* to pick up FIONREAD */
+#endif
+/* Linux (except glibc) and FreeBSD don't have poll */
+#if !(defined(LINUX) && !(defined(__GLIBC__) && __GLIBC__ >= 2)) \
+ && !defined(FREEBSD)
+#include <poll.h>
+#endif
+#ifdef AIX
+/* To pick up sysconf() */
+#include <unistd.h>
+#else
+/* To pick up getrlimit() etc. */
+#include <sys/time.h>
+#include <sys/resource.h>
+#endif
+
+#include "primpl.h"
+
+/* On Alpha Linux, these are already defined in sys/socket.h */
+#if !(defined(LINUX) && defined(__alpha))
+#include <netinet/tcp.h> /* TCP_NODELAY, TCP_MAXSEG */
+#endif
+
+#if defined(SOLARIS)
+#define _PRSockOptVal_t char *
+#elif defined(IRIX) || defined(OSF1) || defined(AIX) || defined(HPUX) \
+ || defined(LINUX) || defined(FREEBSD)
+#define _PRSockOptVal_t void *
+#else
+#error "Cannot determine architecture"
+#endif
+
+#if (defined(HPUX) && !defined(HPUX10_30) && !defined(HPUX11))
+#define _PRSelectFdSetArg_t int *
+#elif defined(AIX4_1)
+#define _PRSelectFdSetArg_t void *
+#elif defined(IRIX) || (defined(AIX) && !defined(AIX4_1)) \
+ || defined(OSF1) || defined(SOLARIS) \
+ || defined(HPUX10_30) || defined(HPUX11) || defined(LINUX) \
+ || defined(FREEBSD)
+#define _PRSelectFdSetArg_t fd_set *
+#else
+#error "Cannot determine architecture"
+#endif
+
+static PRStatus pt_InitIOContinuation(void);
+static PRFileDesc *pt_SetMethods(PRIntn osfd, PRDescType type);
+
+static pthread_condattr_t _pt_cvar_attr;
+static PRLock *_pr_flock_lock; /* For PR_LockFile() etc. */
+static PRLock *_pr_rename_lock; /* For PR_Rename() */
+
+extern struct _PT_Bookeeping pt_book; /* defined in ptthread.c */
+extern PRIntn pt_schedpriv; /* defined in ptthread.c */
+
+/*****************************************************************************/
+/*****************************************************************************/
+/************************** File descriptor caching **************************/
+/*****************************************************************************/
+/*****************************************************************************/
+
+typedef struct _PT_Fd_Cache
+{
+ PRLock *ml;
+ PRIntn count;
+ PRIntn limit;
+ PRFileDesc *fd;
+} _PT_Fd_Cache;
+static _PT_Fd_Cache pt_fd_cache;
+
+/*
+** Get a FileDescriptor from the cache if one exists. If not allocate
+** a new one from the heap.
+*/
+static PRFileDesc *pt_Getfd(void)
+{
+ PRFileDesc *fd;
+ do
+ {
+ fd = pt_fd_cache.fd; /* quick, unsafe check */
+ if (NULL == fd)
+ {
+ fd = PR_NEWZAP(PRFileDesc);
+ if (NULL == fd) goto finished;
+ fd->secret = PR_NEWZAP(PRFilePrivate);
+ if (NULL == fd->secret)
+ {
+ PR_DELETE(fd);
+ goto finished;
+ }
+ }
+ else
+ {
+ PRFilePrivate *secret;
+ PR_Lock(pt_fd_cache.ml);
+ fd = pt_fd_cache.fd; /* safer extraction */
+ if (NULL != fd)
+ {
+ pt_fd_cache.count -= 1;
+ pt_fd_cache.fd = fd->higher;
+ fd->higher = NULL;
+ }
+ PR_Unlock(pt_fd_cache.ml);
+ secret = fd->secret;
+ memset(fd, 0, sizeof(PRFileDesc));
+ memset(secret, 0, sizeof(PRFilePrivate));
+ fd->secret = secret;
+ }
+ } while (NULL == fd);
+finished:
+ return fd;
+} /* pt_Getfd */
+
+/*
+** Return a file descriptor to the cache unless there are too many in
+** there already. If put in cache, clear the fields first.
+*/
+static void pt_Putfd(PRFileDesc *fd)
+{
+ PR_ASSERT(_PR_FILEDESC_CLOSED == fd->secret->state);
+ PR_ASSERT(pt_fd_cache.count < pt_fd_cache.limit);
+
+ fd->secret->state = _PR_FILEDESC_FREED;
+ if (pt_fd_cache.count > pt_fd_cache.limit)
+ {
+ PR_DELETE(fd->secret);
+ PR_DELETE(fd);
+ }
+ else
+ {
+ PR_Lock(pt_fd_cache.ml);
+ pt_fd_cache.count += 1;
+ fd->higher = pt_fd_cache.fd;
+ pt_fd_cache.fd = fd;
+ PR_Unlock(pt_fd_cache.ml);
+ }
+} /* pt_Putfd */
+
+/*****************************************************************************/
+/************************* I/O Continuation machinery ************************/
+/*****************************************************************************/
+
+/*
+ * The polling interval defines the maximum amount of time that a thread
+ * might hang up before an interrupt is noticed.
+ */
+#define PT_DEFAULT_POLL_MSEC 100
+
+/*
+ * Latest POSIX defines this type as socklen_t. It may also be
+ * size_t or int.
+ */
+#if (defined(LINUX) && defined(__GLIBC__) && __GLIBC__ >= 2 \
+ && !defined(__alpha))
+typedef socklen_t pt_SockLen;
+#elif defined(AIX) || (defined(LINUX) && defined(__alpha))
+typedef PRSize pt_SockLen;
+#else
+typedef PRIntn pt_SockLen;
+#endif
+
+typedef struct pt_Continuation pt_Continuation;
+typedef PRBool (*ContinuationFn)(pt_Continuation *op, PRInt16 revents);
+
+typedef enum pr_ContuationStatus
+{
+ pt_continuation_sumbitted,
+ pt_continuation_inprogress,
+ pt_continuation_abort,
+ pt_continuation_done
+} pr_ContuationStatus;
+
+struct pt_Continuation
+{
+ /* These objects are linked in ascending timeout order */
+ pt_Continuation *next, *prev; /* self linked list of these things */
+
+ /* The building of the continuation operation */
+ ContinuationFn function; /* what function to continue */
+ union { PRIntn osfd; } arg1; /* #1 - the op's fd */
+ union { void* buffer; } arg2; /* #2 - primary transfer buffer */
+ union {
+ PRSize amount; /* #3 - size of 'buffer', or */
+ pt_SockLen *addr_len; /* - length of address */
+#ifdef HPUX11
+ /*
+ * For sendfile()
+ */
+ off_t offset; /* offset in file to send */
+#endif
+ } arg3;
+ union { PRIntn flags; } arg4; /* #4 - read/write flags */
+ union { PRNetAddr *addr; } arg5; /* #5 - send/recv address */
+
+#ifdef HPUX11
+ /*
+ * For sendfile()
+ */
+ int filedesc; /* descriptor of file to send */
+ int nbytes_to_send; /* size of header and file */
+#endif /* HPUX11 */
+
+ PRIntervalTime timeout; /* client (relative) timeout */
+ PRIntervalTime absolute; /* internal (absolute) timeout */
+
+ PRInt16 event; /* flags for poll()'s events */
+
+ /*
+ ** The representation and notification of the results of the operation.
+ ** These function can either return an int return code or a pointer to
+ ** some object.
+ */
+ union { PRSize code; void *object; } result;
+
+ PRIntn syserrno; /* in case it failed, why (errno) */
+ pr_ContuationStatus status; /* the status of the operation */
+ PRCondVar complete; /* to notify the initiating thread */
+};
+
+static struct pt_TimedQueue
+{
+ PRCallOnceType once; /* controls the initialization
+ * of this structure */
+ PRLock *ml; /* a little protection */
+ PRThread *thread; /* internal thread's identification */
+ PRCondVar *new_op; /* new operation supplied */
+ PRUintn op_count; /* number of operations in the list */
+ pt_Continuation *head, *tail; /* head/tail of list of operations */
+
+ pt_Continuation *op; /* timed operation furthest in future */
+ PRBool exitFlag; /* a Boolean flag for signaling the
+ * continuation thread to exit */
+} pt_tq;
+
+#if defined(DEBUG)
+static struct pt_debug_s
+{
+ PRUintn predictionsFoiled;
+ PRUintn pollingListMax;
+ PRUintn continuationsServed;
+} pt_debug;
+#endif /* DEBUG */
+
+/*
+ * The following two functions, pt_InsertTimedInternal and
+ * pt_FinishTimedInternal, are always called with the pt_tq.ml
+ * lock held. The "internal" in the functions' names come from
+ * the Mesa programming language. Internal functions are always
+ * called from inside a monitor.
+ */
+
+static void pt_InsertTimedInternal(pt_Continuation *op)
+{
+ pt_Continuation *t_op = NULL;
+ PRIntervalTime now = PR_IntervalNow();
+
+ /*
+ * If this element operation isn't timed, it gets queued at the
+ * end of the list (just after pt_tq.tail) and we're
+ * finishd early.
+ */
+ if (PR_INTERVAL_NO_TIMEOUT == op->timeout)
+ {
+ t_op = pt_tq.tail; /* put it at the end */
+ goto done;
+ }
+
+ /*
+ * The portion of this routine deals with timed ops.
+ */
+ op->absolute = now + op->timeout; /* absolute ticks */
+ if (NULL == pt_tq.op) pt_tq.op = op;
+ else
+ {
+ /*
+ * To find where in the list to put the new operation, based
+ * on the absolute time the operation in question will expire.
+ *
+ * The new operation ('op') will expire at now() + op->timeout.
+ *
+ * This should be easy!
+ */
+
+ for (t_op = pt_tq.op; NULL != t_op; t_op = t_op->prev)
+ {
+ /*
+ * If 'op' expires later than t_op, then insert 'op' just
+ * ahead of t_op. Otherwise, compute when operation[n-1]
+ * expires and try again.
+ *
+ * The actual different between the expiriation of 'op'
+ * and the current operation what becomes the new operaton's
+ * timeout interval. That interval is also subtracted from
+ * the interval of the operation immediately following where
+ * we stick 'op' (unless the next one isn't timed). The new
+ * timeout assigned to 'op' takes into account the values of
+ * now() and when the previous intervals were computed.
+ */
+ if ((PRInt32)(op->absolute - t_op->absolute) >= 0)
+ {
+ if (t_op == pt_tq.op) pt_tq.op = op;
+ break;
+ }
+ }
+ }
+
+done:
+
+ /*
+ * Insert 'op' into the queue just after t_op or if t_op is null,
+ * at the head of the list.
+ *
+ * We need to set up the 'next' and 'prev' pointers of 'op'
+ * correctly before inserting 'op' into the queue. Also, we insert
+ * 'op' by updating pt_tq.head or op->prev->next first, and then
+ * updating op->next->prev. We want to make sure that the 'next'
+ * pointers are linked up correctly at all times so that we can
+ * traverse the queue by starting with pt_tq.head and following
+ * the 'next' pointers, without having to acquire the pt_tq.ml lock.
+ * (We do that in ContinuationThread.) We traverse the 'prev'
+ * pointers only in this function, which is called with the lock held.
+ *
+ * Similar care is taken in pt_FinishTimedInternal where we remove
+ * an op from the queue.
+ */
+ if (NULL == t_op)
+ {
+ op->prev = NULL;
+ op->next = pt_tq.head;
+ pt_tq.head = op;
+ if (NULL == pt_tq.tail) pt_tq.tail = op;
+ else op->next->prev = op;
+ }
+ else
+ {
+ op->prev = t_op;
+ op->next = t_op->next;
+ if (NULL != op->prev)
+ op->prev->next = op;
+ if (NULL != op->next)
+ op->next->prev = op;
+ if (t_op == pt_tq.tail)
+ pt_tq.tail = op;
+ }
+
+ pt_tq.op_count += 1;
+
+} /* pt_InsertTimedInternal */
+
+/*
+ * function: pt_FinishTimedInternal
+ *
+ * Takes the finished operation out of the timed queue. It
+ * notifies the initiating thread that the opertions is
+ * complete and returns to the caller the value of the next
+ * operation in the list (or NULL).
+ */
+static pt_Continuation *pt_FinishTimedInternal(pt_Continuation *op)
+{
+ pt_Continuation *next;
+
+ /* remove this one from the list */
+ if (NULL == op->prev) pt_tq.head = op->next;
+ else op->prev->next = op->next;
+ if (NULL == op->next) pt_tq.tail = op->prev;
+ else op->next->prev = op->prev;
+
+ /* did we happen to hit the timed op? */
+ if (op == pt_tq.op) pt_tq.op = op->prev;
+
+ next = op->next;
+ op->next = op->prev = NULL;
+ op->status = pt_continuation_done;
+
+ pt_tq.op_count -= 1;
+
+#if defined(DEBUG)
+ pt_debug.continuationsServed += 1;
+#endif
+ PR_NotifyCondVar(&op->complete);
+
+ return next;
+} /* pt_FinishTimedInternal */
+
+static void ContinuationThread(void *arg)
+{
+ /* initialization */
+ PRInt32 msecs, mx_poll_ticks;
+ struct pollfd *pollingList = 0; /* list built for polling */
+ PRIntn pollingListUsed; /* # entries used in the list */
+ PRIntn pollingListNeeded; /* # entries needed this time */
+ PRIntn pollingSlotsAllocated = 0; /* # entries available in list */
+
+ mx_poll_ticks = (PRInt32)PR_MillisecondsToInterval(PT_DEFAULT_POLL_MSEC);
+
+ /* do some real work */
+ while (PR_TRUE)
+ {
+ PRIntn rv;
+ PRInt32 timeout;
+ PRIntn pollIndex;
+ PRIntervalTime now;
+ pt_Continuation *op;
+
+ PR_Lock(pt_tq.ml);
+ while (!pt_tq.exitFlag && (NULL == pt_tq.head))
+ PR_WaitCondVar(pt_tq.new_op, PR_INTERVAL_NO_TIMEOUT);
+ pollingListNeeded = pt_tq.op_count;
+ PR_Unlock(pt_tq.ml);
+
+ /* Okay. We're history */
+ if (pt_tq.exitFlag) break;
+
+ /*
+ * We are not holding the pt_tq.ml lock now, so more items may
+ * get added to pt_tq during this window of time. We hope
+ * that 10 more spaces in the polling list should be enough.
+ */
+ pollingListNeeded += 10;
+ if (pollingListNeeded > pollingSlotsAllocated)
+ {
+ if (NULL != pollingList) PR_DELETE(pollingList);
+ pollingList = (struct pollfd*)PR_Malloc(
+ pollingListNeeded * sizeof(struct pollfd));
+ PR_ASSERT(NULL != pollingList);
+ pollingSlotsAllocated = pollingListNeeded;
+ }
+
+#if defined(DEBUG)
+ if (pollingListNeeded > pt_debug.pollingListMax)
+ pt_debug.pollingListMax = pollingListNeeded;
+#endif
+
+ /*
+ * Build up a polling list.
+ * This list is sorted on time. Operations that have been
+ * interrupted are completed and not included in the list.
+ * There is an assertion that the operation is in progress.
+ */
+ pollingListUsed = 0;
+ PR_Lock(pt_tq.ml);
+
+ for (op = pt_tq.head; NULL != op;)
+ {
+ if (pt_continuation_abort == op->status)
+ {
+ op->result.code = -1;
+ op->syserrno = EINTR;
+ op = pt_FinishTimedInternal(op);
+ }
+ else
+ {
+ if (pollingListUsed == pollingSlotsAllocated) break;
+ PR_ASSERT(pt_continuation_done != op->status);
+ op->status = pt_continuation_inprogress;
+ pollingList[pollingListUsed].revents = 0;
+ pollingList[pollingListUsed].fd = op->arg1.osfd;
+ pollingList[pollingListUsed].events = op->event;
+ pollingListUsed += 1;
+ op = op->next;
+ }
+ }
+
+ PR_Unlock(pt_tq.ml);
+
+ /*
+ * If 'op' isn't NULL at this point, then we didn't get to
+ * the end of the list. That means that more items got added
+ * to the list than we anticipated. So, forget this iteration,
+ * go around the horn again.
+ * One would hope this doesn't happen all that often.
+ */
+ if (NULL != op)
+ {
+#if defined(DEBUG)
+ pt_debug.predictionsFoiled += 1; /* keep track */
+#endif
+ continue; /* make it rethink things */
+ }
+
+ if (NULL == pt_tq.head) continue; /* did list evaporate? */
+
+ /*
+ * We don't want to wait forever on this poll. So keep
+ * the interval down. The operations, if they are timed,
+ * still have to timeout, while those that are not timed
+ * should persist forever. But they may be aborted. That's
+ * what this anxiety is all about.
+ */
+ if (PR_INTERVAL_NO_TIMEOUT == pt_tq.head->timeout)
+ msecs = PT_DEFAULT_POLL_MSEC;
+ else
+ {
+ timeout = pt_tq.head->absolute - PR_IntervalNow();
+ if (timeout <= 0) msecs = 0; /* already timed out */
+ else if (timeout >= mx_poll_ticks) msecs = PT_DEFAULT_POLL_MSEC;
+ else msecs = (PRInt32)PR_IntervalToMilliseconds(timeout);
+ }
+
+ rv = poll(pollingList, pollingListUsed, msecs);
+
+ if ((-1 == rv) && ((errno == EINTR) || (errno == EAGAIN)))
+ continue; /* go around the loop again */
+
+ if (rv > 0)
+ {
+ /*
+ * poll() says that something in our list is ready for some more
+ * action. Find it, load up the operation and see what happens.
+ */
+
+ /*
+ * This may work out okay. The rule is that only this thread,
+ * the continuation thread, can remove elements from the list.
+ * Therefore, the list is at worst, longer than when we built
+ * the polling list.
+ */
+
+ op = pt_tq.head;
+ for (pollIndex = 0; pollIndex < pollingListUsed; ++pollIndex)
+ {
+ PR_ASSERT(NULL != op);
+ if (0 != pollingList[pollIndex].revents)
+ {
+ /*
+ * This one wants attention. Redo the operation.
+ * We know that there can only be more elements
+ * in the op list than we knew about when we created
+ * the poll list. Therefore, we might have to skip
+ * a few ops to find the right one to operate on.
+ */
+ while ((pollingList[pollIndex].fd != op->arg1.osfd)
+ || (pollingList[pollIndex].events != op->event))
+ {
+ PR_ASSERT(NULL != op->next); /* it has to be in there */
+ op = op->next; /* keep advancing down the list */
+ }
+
+ /*
+ * Skip over all those not in progress. They'll be
+ * pruned next time we build a polling list. Call
+ * the continuation function. If it reports completion,
+ * finish off the operation.
+ */
+ if ((pt_continuation_inprogress == op->status)
+ && (op->function(op, pollingList[pollIndex].revents)))
+ {
+ PR_Lock(pt_tq.ml);
+ op = pt_FinishTimedInternal(op);
+ PR_Unlock(pt_tq.ml);
+ }
+ continue;
+ }
+ op = op->next; /* progress to next operation */
+ }
+ }
+
+ /*
+ * This is timeout processing. It is done after checking
+ * for good completions. Those that just made it under the
+ * wire are lucky, but none the less, valid.
+ */
+ if ((NULL != pt_tq.head)
+ && (PR_INTERVAL_NO_TIMEOUT != pt_tq.head->timeout))
+ {
+ now = PR_IntervalNow();
+ while ((PRInt32)(pt_tq.head->absolute - now) <= 0)
+ {
+ /*
+ * The leading element of the timed queue has timed
+ * out. Get rid of it. In any case go around the
+ * loop again, computing the polling list, checking
+ * for interrupted operations.
+ */
+
+ PR_Lock(pt_tq.ml);
+ pt_tq.head->result.code = -1;
+ pt_tq.head->syserrno = ETIMEDOUT;
+ (void)pt_FinishTimedInternal(pt_tq.head);
+ PR_Unlock(pt_tq.ml);
+ if ((NULL == pt_tq.head)
+ || (PR_INTERVAL_NO_TIMEOUT == pt_tq.head->timeout))
+ break;
+ }
+ }
+ }
+ if (NULL != pollingList) PR_DELETE(pollingList);
+} /* ContinuationThread */
+
+static PRIntn pt_Continue(pt_Continuation *op)
+{
+ PRIntn rc;
+ PRStatus rv;
+#ifdef DEBUG
+ PRBool waitcv_interrupted = PR_FALSE;
+#endif /* DEBUG */
+
+ PR_CallOnce(&pt_tq.once, pt_InitIOContinuation);
+
+ /* Finish filling in the blank slots */
+ /* op->complete = PR_NewCondVar(pt_tq.ml); */
+ op->complete.lock = pt_tq.ml;
+ rc = PTHREAD_COND_INIT(op->complete.cv, _pt_cvar_attr); PR_ASSERT(0 == rc);
+ op->status = pt_continuation_sumbitted;
+ PR_Lock(pt_tq.ml); /* we provide the locking */
+
+ pt_InsertTimedInternal(op); /* insert in the structure */
+
+ PR_NotifyCondVar(pt_tq.new_op); /* notify the continuation thread */
+
+ while (pt_continuation_done != op->status) /* wait for completion */
+ {
+ rv = PR_WaitCondVar(&op->complete, PR_INTERVAL_NO_TIMEOUT);
+ /*
+ * If we get interrupted, we set state the continuation thread will
+ * see and allow it to finish the I/O operation w/ error. That way
+ * the rule that only the continuation thread is removing elements
+ * from the list is still valid.
+ *
+ * Don't call interrupt on the continuation thread. That'll just
+ * irritate him. He's cycling around at least every mx_poll_ticks
+ * anyhow and should notice the request in there.
+ */
+ if ((PR_FAILURE == rv)
+ && (PR_PENDING_INTERRUPT_ERROR == PR_GetError()))
+ {
+#ifdef DEBUG
+ waitcv_interrupted = PR_TRUE;
+#endif /* DEBUG */
+ if (pt_continuation_done != op->status)
+ {
+ /* tell the continuation thread to abort the operation */
+ op->status = pt_continuation_abort;
+ }
+ else
+ {
+ op->result.code = -1;
+ op->syserrno = EINTR;
+ }
+ }
+ }
+
+ PR_Unlock(pt_tq.ml); /* we provide the locking */
+ rc = pthread_cond_destroy(&op->complete.cv); PR_ASSERT(0 == rc);
+
+ /* make sure that the continuation thread did abort the operation */
+#ifdef DEBUG
+ if (PR_TRUE == waitcv_interrupted)
+ {
+ PR_ASSERT(-1 == op->result.code);
+ PR_ASSERT(EINTR == op->syserrno);
+ }
+#endif /* DEBUG */
+ return op->result.code; /* and the primary answer */
+} /* pt_Continue */
+
+/*****************************************************************************/
+/*********************** specific continuation functions *********************/
+/*****************************************************************************/
+static PRBool pt_connect_cont(pt_Continuation *op, PRInt16 revents)
+{
+ op->syserrno = _MD_unix_get_nonblocking_connect_error(op->arg1.osfd);
+ if (op->syserrno != 0) {
+ op->result.code = -1;
+ } else {
+ op->result.code = 0;
+ }
+ return PR_TRUE; /* this one is cooked */
+} /* pt_connect_cont */
+
+static PRBool pt_accept_cont(pt_Continuation *op, PRInt16 revents)
+{
+ op->syserrno = 0;
+ op->result.code = accept(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.addr_len);
+ if (-1 == op->result.code)
+ {
+ op->syserrno = errno;
+ if (EWOULDBLOCK == errno || EAGAIN == errno) /* the only thing we allow */
+ return PR_FALSE; /* do nothing - this one ain't finished */
+ }
+ return PR_TRUE;
+} /* pt_accept_cont */
+
+static PRBool pt_read_cont(pt_Continuation *op, PRInt16 revents)
+{
+ /*
+ * Any number of bytes will complete the operation. It need
+ * not (and probably will not) satisfy the request. The only
+ * error we continue is EWOULDBLOCK|EAGAIN.
+ */
+ op->result.code = read(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.amount);
+ op->syserrno = errno;
+ return ((-1 == op->result.code) &&
+ (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ?
+ PR_FALSE : PR_TRUE;
+} /* pt_read_cont */
+
+static PRBool pt_recv_cont(pt_Continuation *op, PRInt16 revents)
+{
+ /*
+ * Any number of bytes will complete the operation. It need
+ * not (and probably will not) satisfy the request. The only
+ * error we continue is EWOULDBLOCK|EAGAIN.
+ */
+ op->result.code = recv(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags);
+ op->syserrno = errno;
+ return ((-1 == op->result.code) &&
+ (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ?
+ PR_FALSE : PR_TRUE;
+} /* pt_recv_cont */
+
+static PRBool pt_send_cont(pt_Continuation *op, PRInt16 revents)
+{
+ /*
+ * We want to write the entire amount out, no matter how many
+ * tries it takes. Keep advancing the buffer and the decrementing
+ * the amount until the amount goes away. Return the total bytes
+ * (which should be the original amount) when finished (or an
+ * error).
+ */
+ PRIntn bytes = send(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags);
+ op->syserrno = errno;
+ if (bytes > 0) /* this is progress */
+ {
+ char *bp = (char*)op->arg2.buffer;
+ bp += bytes; /* adjust the buffer pointer */
+ op->arg2.buffer = bp;
+ op->result.code += bytes; /* accumulate the number sent */
+ op->arg3.amount -= bytes; /* and reduce the required count */
+ return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE;
+ }
+ else return ((-1 == bytes) &&
+ (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ?
+ PR_FALSE : PR_TRUE;
+} /* pt_send_cont */
+
+static PRBool pt_write_cont(pt_Continuation *op, PRInt16 revents)
+{
+ /*
+ * We want to write the entire amount out, no matter how many
+ * tries it takes. Keep advancing the buffer and the decrementing
+ * the amount until the amount goes away. Return the total bytes
+ * (which should be the original amount) when finished (or an
+ * error).
+ */
+ PRIntn bytes = write(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.amount);
+ op->syserrno = errno;
+ if (bytes > 0) /* this is progress */
+ {
+ char *bp = (char*)op->arg2.buffer;
+ bp += bytes; /* adjust the buffer pointer */
+ op->arg2.buffer = bp;
+ op->result.code += bytes; /* accumulate the number sent */
+ op->arg3.amount -= bytes; /* and reduce the required count */
+ return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE;
+ }
+ else return ((-1 == bytes) &&
+ (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ?
+ PR_FALSE : PR_TRUE;
+} /* pt_write_cont */
+
+static PRBool pt_writev_cont(pt_Continuation *op, PRInt16 revents)
+{
+ /*
+ * Same rules as write, but continuing seems to be a bit more
+ * complicated. As the number of bytes sent grows, we have to
+ * redefine the vector we're pointing at. We might have to
+ * modify an individual vector parms or we might have to eliminate
+ * a pair altogether.
+ */
+ PRIntn bytes = writev(
+ op->arg1.osfd, (struct iovec*)op->arg2.buffer, op->arg3.amount);
+ op->syserrno = errno;
+ if (bytes > 0) /* this is progress */
+ {
+ PRIntn iov_index;
+ struct iovec *iov = (struct iovec*)op->arg2.buffer;
+ op->result.code += bytes; /* accumulate the number sent */
+ for (iov_index = 0; iov_index < op->arg3.amount; ++iov_index)
+ {
+ /* how much progress did we make in the i/o vector? */
+ if (bytes < iov[iov_index].iov_len)
+ {
+ /* this element's not done yet */
+ char **bp = (char**)&(iov[iov_index].iov_base);
+ iov[iov_index].iov_len -= bytes; /* there's that much left */
+ *bp += bytes; /* starting there */
+ break; /* go off and do that */
+ }
+ bytes -= iov[iov_index].iov_len; /* that element's consumed */
+ }
+ op->arg2.buffer = &iov[iov_index]; /* new start of array */
+ op->arg3.amount -= iov_index; /* and array length */
+ return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE;
+ }
+ else return ((-1 == bytes) &&
+ (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ?
+ PR_FALSE : PR_TRUE;
+} /* pt_writev_cont */
+
+static PRBool pt_sendto_cont(pt_Continuation *op, PRInt16 revents)
+{
+ PRIntn bytes = sendto(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags,
+ (struct sockaddr*)op->arg5.addr, PR_NETADDR_SIZE(op->arg5.addr));
+ op->syserrno = errno;
+ if (bytes > 0) /* this is progress */
+ {
+ char *bp = (char*)op->arg2.buffer;
+ bp += bytes; /* adjust the buffer pointer */
+ op->arg2.buffer = bp;
+ op->result.code += bytes; /* accumulate the number sent */
+ op->arg3.amount -= bytes; /* and reduce the required count */
+ return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE;
+ }
+ else return ((-1 == bytes) &&
+ (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ?
+ PR_FALSE : PR_TRUE;
+} /* pt_sendto_cont */
+
+static PRBool pt_recvfrom_cont(pt_Continuation *op, PRInt16 revents)
+{
+ pt_SockLen addr_len = sizeof(PRNetAddr);
+ op->result.code = recvfrom(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.amount,
+ op->arg4.flags, (struct sockaddr*)op->arg5.addr, &addr_len);
+ op->syserrno = errno;
+ return ((-1 == op->result.code) &&
+ (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ?
+ PR_FALSE : PR_TRUE;
+} /* pt_recvfrom_cont */
+
+#ifdef HPUX11
+static PRBool pt_hpux_transmitfile_cont(pt_Continuation *op, PRInt16 revents)
+{
+ struct iovec *hdtrl = (struct iovec *) op->arg2.buffer;
+ int count;
+
+ count = sendfile(op->arg1.osfd, op->filedesc, op->arg3.offset, 0,
+ hdtrl, op->arg4.flags);
+ PR_ASSERT(count <= op->nbytes_to_send);
+
+ if (count != -1) {
+ op->result.code += count;
+ } else if (errno != EWOULDBLOCK && errno != EAGAIN) {
+ op->result.code = -1;
+ } else {
+ return PR_FALSE;
+ }
+
+ if (count != -1 && count < op->nbytes_to_send) {
+ if (hdtrl[0].iov_len == 0) {
+ PR_ASSERT(hdtrl[0].iov_base == NULL);
+ op->arg3.offset += count;
+ } else if (count < hdtrl[0].iov_len) {
+ PR_ASSERT(op->arg3.offset == 0);
+ hdtrl[0].iov_base = (char *) hdtrl[0].iov_base + count;
+ hdtrl[0].iov_len -= count;
+ } else {
+ op->arg3.offset = count - hdtrl[0].iov_len;
+ hdtrl[0].iov_base = NULL;
+ hdtrl[0].iov_len = 0;
+ }
+ op->nbytes_to_send -= count;
+ return PR_FALSE;
+ }
+
+ return PR_TRUE;
+}
+#endif /* HPUX11 */
+
+#if !defined(PT_NO_ATFORK)
+
+static void pt_BeforeFork()
+{
+ PRStatus rv;
+ PRThread *thred = pt_tq.thread;
+
+ /*
+ * We shut down the continuation thread cleanly only if there are
+ * no other threads in the process when fork() is called.
+ * If there are other threads, they won't be duplicated in the child
+ * process. Then the child process may already be unclean, so there
+ * is no point for us to try to be clean.
+ */
+
+ if ((NULL != thred) && (2 == pt_book.user + pt_book.system))
+ {
+ /* pt_tq should be empty */
+ PR_ASSERT((0 == pt_tq.op_count)
+ && (NULL == pt_tq.head)
+ && (NULL == pt_tq.tail)
+ && (NULL == pt_tq.op));
+ pt_tq.exitFlag = PR_TRUE;
+ rv = PR_Interrupt(thred);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(thred);
+ PR_ASSERT(PR_SUCCESS == rv);
+ pt_tq.exitFlag = PR_FALSE;
+ /* Indicates that the continuation thread is shut down cleanly */
+ pt_tq.thread = NULL;
+ memset(&pt_tq.once, 0, sizeof(pt_tq.once));
+ PR_ASSERT(1 == pt_book.user + pt_book.system);
+ }
+} /* pt_BeforeFork */
+
+static void pt_AfterForkParent(void)
+{
+} /* pt_AfterForkParent */
+
+static void pt_AfterForkChild(void)
+{
+ /*
+ * pt_tq may be in a corrupted state if the continuation thread
+ * existed and was not terminated cleanly before fork. In this
+ * case, we expect the child process to call exec immediately.
+ */
+} /* pt_AfterForkChild */
+
+#endif /* PT_NO_ATFORK */
+
+void _PR_InitIO()
+{
+ _pr_stdin = pt_SetMethods(0, PR_DESC_FILE);
+ _pr_stdout = pt_SetMethods(1, PR_DESC_FILE);
+ _pr_stderr = pt_SetMethods(2, PR_DESC_FILE);
+
+ PR_ASSERT(_pr_stdin && _pr_stdout && _pr_stderr);
+
+ _pr_flock_lock = PR_NewLock();
+ PR_ASSERT(NULL != _pr_flock_lock);
+ _pr_rename_lock = PR_NewLock();
+ PR_ASSERT(NULL != _pr_rename_lock);
+
+ pt_fd_cache.ml = PR_NewLock();
+ PR_ASSERT(NULL != pt_fd_cache.ml);
+ pt_fd_cache.limit = FD_SETSIZE;
+} /* _PR_InitIO */
+
+static PRStatus pt_InitIOContinuation()
+{
+ PRIntn rv;
+
+ PR_ASSERT((0 == pt_tq.op_count)
+ && (NULL == pt_tq.head)
+ && (NULL == pt_tq.tail)
+ && (NULL == pt_tq.op)
+ && (PR_FALSE == pt_tq.exitFlag));
+
+ if (NULL == pt_tq.ml)
+ {
+ /* The very first time */
+ pt_tq.ml = PR_NewLock();
+ PR_ASSERT(NULL != pt_tq.ml);
+ pt_tq.new_op = PR_NewCondVar(pt_tq.ml);
+ PR_ASSERT(NULL != pt_tq.new_op);
+ rv = PTHREAD_CONDATTR_INIT(&_pt_cvar_attr);
+ PR_ASSERT(0 == rv);
+
+#if !defined(PT_NO_ATFORK)
+ rv = pthread_atfork(pt_BeforeFork,
+ pt_AfterForkParent, pt_AfterForkChild);
+ PR_ASSERT(0 == rv);
+#endif
+ }
+
+ pt_tq.thread = PR_CreateThread(
+ PR_SYSTEM_THREAD, ContinuationThread, NULL,
+ PR_PRIORITY_URGENT, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ PR_ASSERT(NULL != pt_tq.thread);
+
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRFileDesc*) PR_GetSpecialFD(PRSpecialFD osfd)
+{
+ PRFileDesc *result = NULL;
+ PR_ASSERT(osfd >= PR_StandardInput && osfd <= PR_StandardError);
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ switch (osfd)
+ {
+ case PR_StandardInput: result = _pr_stdin; break;
+ case PR_StandardOutput: result = _pr_stdout; break;
+ case PR_StandardError: result = _pr_stderr; break;
+ default:
+ (void)PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ }
+ return result;
+} /* PR_GetSpecialFD */
+
+/*****************************************************************************/
+/***************************** I/O private methods ***************************/
+/*****************************************************************************/
+
+static PRBool pt_TestAbort(void)
+{
+ PRThread *me = PR_CurrentThread();
+ if(me->state & PT_THREAD_ABORTED)
+ {
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ me->state &= ~PT_THREAD_ABORTED;
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+} /* pt_TestAbort */
+
+static void pt_MapError(void (*mapper)(PRIntn), PRIntn syserrno)
+{
+ switch (syserrno)
+ {
+ case EINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); break;
+ case ETIMEDOUT:
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0); break;
+ default:
+ mapper(syserrno);
+ }
+} /* pt_MapError */
+
+static PRStatus pt_Close(PRFileDesc *fd)
+{
+ PRIntn syserrno, rv = 0;
+ if ((NULL == fd) || (NULL == fd->secret))
+ {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+ return PR_FAILURE;
+ }
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ if (_PR_FILEDESC_OPEN == fd->secret->state)
+ {
+ fd->secret->state = _PR_FILEDESC_CLOSED;
+ rv = close(fd->secret->md.osfd);
+ syserrno = errno;
+ }
+
+ pt_Putfd(fd);
+ if (-1 == rv)
+ {
+ pt_MapError(_PR_MD_MAP_CLOSE_ERROR, syserrno);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+} /* pt_Close */
+
+static PRInt32 pt_Read(PRFileDesc *fd, void *buf, PRInt32 amount)
+{
+ PRInt32 syserrno, bytes = -1;
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ bytes = read(fd->secret->md.osfd, buf, amount);
+ syserrno = errno;
+
+ if ((bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN)
+ && (!fd->secret->nonblocking))
+ {
+ pt_Continuation op;
+ op.arg1.osfd = fd->secret->md.osfd;
+ op.arg2.buffer = buf;
+ op.arg3.amount = amount;
+ op.timeout = PR_INTERVAL_NO_TIMEOUT;
+ op.function = pt_read_cont;
+ op.event = POLLIN | POLLPRI;
+ bytes = pt_Continue(&op);
+ syserrno = op.syserrno;
+ }
+ if (bytes < 0)
+ pt_MapError(_PR_MD_MAP_READ_ERROR, syserrno);
+ return bytes;
+} /* pt_Read */
+
+static PRInt32 pt_Write(PRFileDesc *fd, const void *buf, PRInt32 amount)
+{
+ PRInt32 syserrno, bytes = -1;
+ PRBool fNeedContinue = PR_FALSE;
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ bytes = write(fd->secret->md.osfd, buf, amount);
+ syserrno = errno;
+
+ if ( (bytes >= 0) && (bytes < amount) && (!fd->secret->nonblocking) )
+ {
+ buf = (char *) buf + bytes;
+ amount -= bytes;
+ fNeedContinue = PR_TRUE;
+ }
+ if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN)
+ && (!fd->secret->nonblocking) )
+ {
+ bytes = 0;
+ fNeedContinue = PR_TRUE;
+ }
+
+ if (fNeedContinue == PR_TRUE)
+ {
+ pt_Continuation op;
+ op.arg1.osfd = fd->secret->md.osfd;
+ op.arg2.buffer = (void*)buf;
+ op.arg3.amount = amount;
+ op.timeout = PR_INTERVAL_NO_TIMEOUT;
+ op.result.code = bytes; /* initialize the number sent */
+ op.function = pt_write_cont;
+ op.event = POLLOUT | POLLPRI;
+ bytes = pt_Continue(&op);
+ syserrno = op.syserrno;
+ }
+ if (bytes == -1)
+ pt_MapError(_PR_MD_MAP_WRITE_ERROR, syserrno);
+ return bytes;
+} /* pt_Write */
+
+static PRInt32 pt_Writev(
+ PRFileDesc *fd, PRIOVec *iov, PRInt32 iov_len, PRIntervalTime timeout)
+{
+ PRIntn iov_index = 0;
+ PRBool fNeedContinue = PR_FALSE;
+ PRInt32 syserrno, bytes = -1, rv = -1;
+
+ if (pt_TestAbort()) return rv;
+
+ /*
+ * The first shot at this can use the client's iov directly.
+ * Only if we have to continue the operation do we have to
+ * make a copy that we can modify.
+ */
+ rv = bytes = writev(fd->secret->md.osfd, (struct iovec*)iov, iov_len);
+ syserrno = errno;
+
+ /*
+ * If we moved some bytes (ie., bytes > 0) how does that implicate
+ * the i/o vector list. In other words, exactly where are we within
+ * that array? What are the parameters for resumption? Maybe we're
+ * done!
+ */
+ if ((bytes > 0) && (!fd->secret->nonblocking))
+ {
+ for (iov_index = 0; iov_index < iov_len; ++iov_index)
+ {
+ if (bytes < iov[iov_index].iov_len) break; /* continue w/ what's left */
+ bytes -= iov[iov_index].iov_len; /* this one's done cooked */
+ }
+ }
+
+ if ((bytes >= 0) && (iov_index < iov_len) && (!fd->secret->nonblocking))
+ {
+ if (PR_INTERVAL_NO_WAIT == timeout)
+ {
+ rv = -1;
+ syserrno = ETIMEDOUT;
+ }
+ else fNeedContinue = PR_TRUE;
+ }
+ else if ((bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN)
+ && (!fd->secret->nonblocking))
+ {
+ if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT;
+ else
+ {
+ rv = bytes = 0;
+ fNeedContinue = PR_TRUE;
+ }
+ }
+
+ if (fNeedContinue == PR_TRUE)
+ {
+ pt_Continuation op;
+ /*
+ * Okay. Now we need a modifiable copy of the array.
+ * Allocate some storage and copy the (already) modified
+ * bits into the new vector. The is copying only the
+ * part of the array that's still valid. The array may
+ * have a new length and the first element of the array may
+ * have different values.
+ */
+ struct iovec *osiov = NULL, *tiov;
+ PRIntn osiov_len = iov_len - iov_index; /* recompute */
+ osiov = (struct iovec*)PR_Malloc(osiov_len * sizeof(struct iovec));
+ PR_ASSERT(NULL != osiov);
+ for (tiov = osiov; iov_index < iov_len; ++iov_index)
+ {
+ tiov->iov_base = iov[iov_index].iov_base;
+ tiov->iov_len = iov[iov_index].iov_len;
+ tiov += 1;
+ }
+ osiov->iov_len -= bytes; /* that may be partially done */
+ /* so advance the description */
+ osiov->iov_base = (char*)osiov->iov_base + bytes;
+
+ op.arg1.osfd = fd->secret->md.osfd;
+ op.arg2.buffer = (void*)osiov;
+ op.arg3.amount = osiov_len;
+ op.timeout = timeout;
+ op.result.code = rv;
+ op.function = pt_writev_cont;
+ op.event = POLLOUT | POLLPRI;
+ rv = pt_Continue(&op);
+ syserrno = op.syserrno;
+ PR_DELETE(osiov);
+ }
+ if (rv == -1) pt_MapError(_PR_MD_MAP_WRITEV_ERROR, syserrno);
+ return rv;
+} /* pt_Writev */
+
+static PRInt32 pt_Seek(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence)
+{
+ PRIntn how;
+ off_t pos = -1;
+
+ if (pt_TestAbort()) return pos;
+
+ switch (whence)
+ {
+ case PR_SEEK_SET: how = SEEK_SET; break;
+ case PR_SEEK_CUR: how = SEEK_CUR; break;
+ case PR_SEEK_END: how = SEEK_END; break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return pos;
+ }
+ pos = lseek(fd->secret->md.osfd, offset, how);
+ if (pos == -1)
+ pt_MapError(_PR_MD_MAP_LSEEK_ERROR, errno);
+ return pos;
+} /* pt_Seek */
+
+static PRInt64 pt_Seek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence)
+{
+ PRInt64 on;
+ PRInt32 off, position = -1;
+ LL_L2I(off, offset); /* possible loss of bits */
+ LL_I2L(on, off); /* get back original or notice we didn't */
+ if (LL_EQ(on, offset)) position = pt_Seek(fd, off, whence);
+ LL_I2L(on, position); /* might not have worked */
+ return on;
+} /* pt_Seek64 */
+
+static PRInt32 pt_Available(PRFileDesc *fd)
+{
+ PRInt32 rv, bytes = -1;
+ if (pt_TestAbort()) return bytes;
+
+ rv = ioctl(fd->secret->md.osfd, FIONREAD, &bytes);
+
+ if (rv == -1)
+ pt_MapError(_PR_MD_MAP_SOCKETAVAILABLE_ERROR, errno);
+ return bytes;
+} /* pt_Available */
+
+static PRInt64 pt_Available64(PRFileDesc *fd)
+{
+ PRInt64 rv;
+ PRInt32 avail = pt_Available(fd);
+ LL_I2L(rv, avail);
+ return rv;
+} /* pt_Available64 */
+
+static PRStatus pt_Synch(PRFileDesc *fd)
+{
+ return (NULL == fd) ? PR_FAILURE : PR_SUCCESS;
+} /* pt_Synch */
+
+static PRStatus pt_FileInfo(PRFileDesc *fd, PRFileInfo *info)
+{
+ PRInt32 rv;
+ struct stat sb;
+ PRInt64 s, s2us;
+
+ if ((rv = fstat(fd->secret->md.osfd, &sb)) == 0 )
+ {
+ if (info)
+ {
+ if (S_IFREG & sb.st_mode)
+ info->type = PR_FILE_FILE ;
+ else if (S_IFDIR & sb.st_mode)
+ info->type = PR_FILE_DIRECTORY;
+ else
+ info->type = PR_FILE_OTHER;
+ info->size = sb.st_size;
+#if defined(IRIX) && defined(HAVE_LONG_LONG)
+ info->modifyTime = (PR_USEC_PER_SEC * (PRInt64)sb.st_mtim.tv_sec);
+ info->creationTime = (PR_USEC_PER_SEC * (PRInt64)sb.st_ctim.tv_sec);
+ info->modifyTime = (PR_USEC_PER_SEC * (PRInt64)sb.st_mtime);
+ info->creationTime = (PR_USEC_PER_SEC * (PRInt64)sb.st_ctime);
+#else
+ LL_I2L(s, sb.st_mtime);
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_MUL(s, s, s2us);
+ info->modifyTime = s;
+ LL_I2L(s, sb.st_ctime);
+ LL_MUL(s, s, s2us);
+ info->creationTime = s;
+#endif
+ }
+ }
+ return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
+} /* pt_FileInfo */
+
+static PRStatus pt_FileInfo64(PRFileDesc *fd, PRFileInfo64 *info)
+{
+ PRFileInfo info32;
+ PRStatus rv = pt_FileInfo(fd, &info32);
+ if (PR_SUCCESS == rv)
+ {
+ info->type = info32.type;
+ info->creationTime = info32.creationTime;
+ info->modifyTime = info32.modifyTime;
+ LL_I2L(info->size, info32.size);
+ }
+ return rv;
+} /* pt_FileInfo64 */
+
+static PRStatus pt_Fsync(PRFileDesc *fd)
+{
+ PRIntn rv = -1;
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ rv = fsync(fd->secret->md.osfd);
+ if (rv < 0) {
+ pt_MapError(_PR_MD_MAP_FSYNC_ERROR, errno);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+} /* pt_Fsync */
+
+static PRStatus pt_Connect(
+ PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PRIntn rv = -1, syserrno;
+ PRSize addr_len = PR_NETADDR_SIZE(addr);
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ rv = connect(fd->secret->md.osfd, (struct sockaddr*)addr, addr_len);
+ syserrno = errno;
+ if ((-1 == rv) && (EINPROGRESS == syserrno) && (!fd->secret->nonblocking))
+ {
+ if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT;
+ else
+ {
+ pt_Continuation op;
+ op.arg1.osfd = fd->secret->md.osfd;
+ op.arg2.buffer = (void*)addr;
+ op.arg3.amount = addr_len;
+ op.timeout = timeout;
+ op.function = pt_connect_cont;
+ op.event = POLLOUT | POLLPRI;
+ rv = pt_Continue(&op);
+ syserrno = op.syserrno;
+ }
+ }
+ if (-1 == rv) {
+ pt_MapError(_PR_MD_MAP_CONNECT_ERROR, syserrno);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+} /* pt_Connect */
+
+PR_IMPLEMENT(PRStatus) PR_GetConnectStatus(const PRPollDesc *pd)
+{
+ PRInt32 osfd;
+ PRFileDesc *bottom = pd->fd;
+ int err;
+
+ if (pd->out_flags & PR_POLL_NVAL) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+ return PR_FAILURE;
+ }
+ if ((pd->out_flags & (PR_POLL_WRITE | PR_POLL_EXCEPT | PR_POLL_ERR)) == 0) {
+ PR_ASSERT(pd->out_flags == 0);
+ PR_SetError(PR_IN_PROGRESS_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ osfd = bottom->secret->md.osfd;
+
+ err = _MD_unix_get_nonblocking_connect_error(osfd);
+ if (err != 0) {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+static PRFileDesc* pt_Accept(
+ PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PRFileDesc *newfd = NULL;
+ PRIntn syserrno, osfd = -1;
+ pt_SockLen addr_len = sizeof(PRNetAddr);
+
+ if (pt_TestAbort()) return newfd;
+
+ osfd = accept(fd->secret->md.osfd, (struct sockaddr*)addr, &addr_len);
+ syserrno = errno;
+
+ if (osfd == -1)
+ {
+ if (fd->secret->nonblocking) goto failed;
+
+ if (EWOULDBLOCK != syserrno && EAGAIN != syserrno) goto failed;
+ else
+ {
+ if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT;
+ else
+ {
+ pt_Continuation op;
+ op.arg1.osfd = fd->secret->md.osfd;
+ op.arg2.buffer = addr;
+ op.arg3.addr_len = &addr_len;
+ op.timeout = timeout;
+ op.function = pt_accept_cont;
+ op.event = POLLIN | POLLPRI;
+ osfd = pt_Continue(&op);
+ syserrno = op.syserrno;
+ }
+ if (osfd < 0) goto failed;
+ }
+ }
+#ifdef AIX
+ /* mask off the first byte of struct sockaddr (the length field) */
+ if (addr)
+ addr->inet.family &= 0x00ff;
+#endif
+ newfd = pt_SetMethods(osfd, PR_DESC_SOCKET_TCP);
+ if (newfd == NULL) close(osfd); /* $$$ whoops! this doesn't work $$$ */
+ else
+ {
+ PR_ASSERT((NULL == addr) || (PR_NETADDR_SIZE(addr) == addr_len));
+#if defined(_PR_INET6)
+ PR_ASSERT((NULL == addr) || (addr->raw.family == AF_INET)
+ || (addr->raw.family == AF_INET6));
+#else
+ PR_ASSERT((NULL == addr) || (addr->raw.family == AF_INET));
+#endif
+ }
+ return newfd;
+
+failed:
+ pt_MapError(_PR_MD_MAP_ACCEPT_ERROR, syserrno);
+ return NULL;
+} /* pt_Accept */
+
+static PRStatus pt_Bind(PRFileDesc *fd, const PRNetAddr *addr)
+{
+ PRIntn rv;
+ PRInt32 one = 1;
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+#if defined(_PR_INET6)
+ PR_ASSERT(addr->raw.family == AF_INET || addr->raw.family == AF_INET6);
+#else
+ PR_ASSERT(addr->raw.family == AF_INET);
+#endif
+
+ rv = setsockopt(
+ fd->secret->md.osfd, SOL_SOCKET, SO_REUSEADDR,
+ (_PRSockOptVal_t) &one, sizeof(one));
+ if (rv == -1) {
+ pt_MapError(_PR_MD_MAP_SETSOCKOPT_ERROR, errno);
+ return PR_FAILURE;
+ }
+
+ rv = bind(fd->secret->md.osfd, (struct sockaddr*)addr, PR_NETADDR_SIZE(addr));
+
+ if (rv == -1) {
+ pt_MapError(_PR_MD_MAP_BIND_ERROR, errno);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+} /* pt_Bind */
+
+static PRStatus pt_Listen(PRFileDesc *fd, PRIntn backlog)
+{
+ PRIntn rv;
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ rv = listen(fd->secret->md.osfd, backlog);
+ if (rv == -1) {
+ pt_MapError(_PR_MD_MAP_LISTEN_ERROR, errno);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+} /* pt_Listen */
+
+static PRStatus pt_Shutdown(PRFileDesc *fd, PRIntn how)
+{
+ PRIntn rv = -1;
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ rv = shutdown(fd->secret->md.osfd, how);
+
+ if (rv == -1) {
+ pt_MapError(_PR_MD_MAP_SHUTDOWN_ERROR, errno);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+} /* pt_Shutdown */
+
+static PRInt32 pt_Recv(
+ PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ PRInt32 syserrno, bytes = -1;
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ bytes = recv(fd->secret->md.osfd, buf, amount, flags);
+ syserrno = errno;
+
+ if ((bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN)
+ && (!fd->secret->nonblocking))
+ {
+ if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT;
+ else
+ {
+ pt_Continuation op;
+ op.arg1.osfd = fd->secret->md.osfd;
+ op.arg2.buffer = buf;
+ op.arg3.amount = amount;
+ op.arg4.flags = flags;
+ op.timeout = timeout;
+ op.function = pt_recv_cont;
+ op.event = POLLIN | POLLPRI;
+ bytes = pt_Continue(&op);
+ syserrno = op.syserrno;
+ }
+ }
+ if (bytes < 0)
+ pt_MapError(_PR_MD_MAP_RECV_ERROR, syserrno);
+ return bytes;
+} /* pt_Recv */
+
+static PRInt32 pt_Send(
+ PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ PRInt32 syserrno, bytes = -1;
+ PRBool fNeedContinue = PR_FALSE;
+
+ /*
+ * Under HP-UX DCE threads, pthread.h includes dce/cma_ux.h,
+ * which has the following:
+ * # define send cma_send
+ * extern int cma_send (int , void *, int, int );
+ * So we need to cast away the 'const' of argument #2 for send().
+ */
+#if defined (HPUX) && defined(_PR_DCETHREADS)
+#define PT_SENDBUF_CAST (void *)
+#else
+#define PT_SENDBUF_CAST
+#endif
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ bytes = send(fd->secret->md.osfd, PT_SENDBUF_CAST buf, amount, flags);
+ syserrno = errno;
+
+ if ( (bytes >= 0) && (bytes < amount) && (!fd->secret->nonblocking) )
+ {
+ if (PR_INTERVAL_NO_WAIT == timeout)
+ {
+ bytes = -1;
+ syserrno = ETIMEDOUT;
+ }
+ else
+ {
+ buf = (char *) buf + bytes;
+ amount -= bytes;
+ fNeedContinue = PR_TRUE;
+ }
+ }
+ if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN)
+ && (!fd->secret->nonblocking) )
+ {
+ if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT;
+ else
+ {
+ bytes = 0;
+ fNeedContinue = PR_TRUE;
+ }
+ }
+
+ if (fNeedContinue == PR_TRUE)
+ {
+ pt_Continuation op;
+ op.arg1.osfd = fd->secret->md.osfd;
+ op.arg2.buffer = (void*)buf;
+ op.arg3.amount = amount;
+ op.arg4.flags = flags;
+ op.timeout = timeout;
+ op.result.code = bytes; /* initialize the number sent */
+ op.function = pt_send_cont;
+ op.event = POLLOUT | POLLPRI;
+ bytes = pt_Continue(&op);
+ syserrno = op.syserrno;
+ }
+ if (bytes == -1)
+ pt_MapError(_PR_MD_MAP_SEND_ERROR, syserrno);
+ return bytes;
+} /* pt_Send */
+
+static PRInt32 pt_SendTo(
+ PRFileDesc *fd, const void *buf,
+ PRInt32 amount, PRIntn flags, const PRNetAddr *addr,
+ PRIntervalTime timeout)
+{
+ PRInt32 syserrno, bytes = -1;
+ PRBool fNeedContinue = PR_FALSE;
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+#if defined(_PR_INET6)
+ PR_ASSERT(addr->raw.family == AF_INET || addr->raw.family == AF_INET6);
+#else
+ PR_ASSERT(addr->raw.family == AF_INET);
+#endif
+ bytes = sendto(
+ fd->secret->md.osfd, buf, amount, flags,
+ (struct sockaddr*)addr, PR_NETADDR_SIZE(addr));
+ syserrno = errno;
+ if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN)
+ && (!fd->secret->nonblocking) )
+ {
+ if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT;
+ else fNeedContinue = PR_TRUE;
+ }
+ if (fNeedContinue == PR_TRUE)
+ {
+ pt_Continuation op;
+ op.arg1.osfd = fd->secret->md.osfd;
+ op.arg2.buffer = (void*)buf;
+ op.arg3.amount = amount;
+ op.arg4.flags = flags;
+ op.arg5.addr = (PRNetAddr*)addr;
+ op.timeout = timeout;
+ op.result.code = 0; /* initialize the number sent */
+ op.function = pt_sendto_cont;
+ op.event = POLLOUT | POLLPRI;
+ bytes = pt_Continue(&op);
+ syserrno = op.syserrno;
+ }
+ if (bytes < 0)
+ pt_MapError(_PR_MD_MAP_SENDTO_ERROR, syserrno);
+ return bytes;
+} /* pt_SendTo */
+
+static PRInt32 pt_RecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout)
+{
+ PRBool fNeedContinue = PR_FALSE;
+ PRInt32 syserrno, bytes = -1;
+ pt_SockLen addr_len = sizeof(PRNetAddr);
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ bytes = recvfrom(
+ fd->secret->md.osfd, buf, amount, flags,
+ (struct sockaddr*)addr, &addr_len);
+ syserrno = errno;
+
+ if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN)
+ && (!fd->secret->nonblocking) )
+ {
+ if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT;
+ else fNeedContinue = PR_TRUE;
+ }
+
+ if (fNeedContinue == PR_TRUE)
+ {
+ pt_Continuation op;
+ op.arg1.osfd = fd->secret->md.osfd;
+ op.arg2.buffer = buf;
+ op.arg3.amount = amount;
+ op.arg4.flags = flags;
+ op.arg5.addr = addr;
+ op.timeout = timeout;
+ op.function = pt_recvfrom_cont;
+ op.event = POLLIN | POLLPRI;
+ bytes = pt_Continue(&op);
+ syserrno = op.syserrno;
+ }
+#ifdef AIX
+ /* mask off the first byte of struct sockaddr (the length field) */
+ if (addr) addr->inet.family &= 0x00ff;
+#endif
+ if (bytes < 0)
+ pt_MapError(_PR_MD_MAP_RECVFROM_ERROR, syserrno);
+ return bytes;
+} /* pt_RecvFrom */
+
+#ifdef HPUX11
+/*
+ * pt_HPUXTransmitFile
+ *
+ * Send file fd across socket sd. If headers is non-NULL, 'hlen'
+ * bytes of headers is sent before sending the file.
+ *
+ * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file
+ *
+ * return number of bytes sent or -1 on error
+ *
+ * This implementation takes advantage of the sendfile() system
+ * call available in HP-UX B.11.00.
+ */
+
+static PRInt32 pt_HPUXTransmitFile(PRFileDesc *sd, PRFileDesc *fd,
+ const void *headers, PRInt32 hlen, PRTransmitFileFlags flags,
+ PRIntervalTime timeout)
+{
+ struct stat statbuf;
+ size_t nbytes_to_send;
+ struct iovec hdtrl[2]; /* optional header and trailer buffers */
+ int send_flags;
+ PRInt32 count;
+ int syserrno;
+
+ /* Get file size */
+ if (fstat(fd->secret->md.osfd, &statbuf) == -1) {
+ _PR_MD_MAP_FSTAT_ERROR(errno);
+ return -1;
+ }
+ nbytes_to_send = hlen + statbuf.st_size;
+
+ hdtrl[0].iov_base = (void *) headers; /* cast away the 'const' */
+ hdtrl[0].iov_len = hlen;
+ hdtrl[1].iov_base = NULL;
+ hdtrl[1].iov_base = 0;
+ /*
+ * SF_DISCONNECT seems to close the socket even if sendfile()
+ * only does a partial send on a nonblocking socket. This
+ * would prevent the subsequent sendfile() calls on that socket
+ * from working. So we don't use the SD_DISCONNECT flag.
+ */
+ send_flags = 0;
+
+ do {
+ count = sendfile(sd->secret->md.osfd, fd->secret->md.osfd,
+ 0, 0, hdtrl, send_flags);
+ PR_ASSERT(count <= nbytes_to_send);
+ } while (count == -1 && (syserrno = errno) == EINTR);
+
+ if (count == -1 && (syserrno == EAGAIN || syserrno == EWOULDBLOCK)) {
+ count = 0;
+ }
+ if (count != -1 && count < nbytes_to_send) {
+ pt_Continuation op;
+
+ if (count < hlen) {
+ hdtrl[0].iov_base = ((char *) headers) + count;
+ hdtrl[0].iov_len = hlen - count;
+ op.arg3.offset = 0;
+ } else {
+ hdtrl[0].iov_base = NULL;
+ hdtrl[0].iov_len = 0;
+ op.arg3.offset = count - hlen;
+ }
+
+ op.arg1.osfd = sd->secret->md.osfd;
+ op.filedesc = fd->secret->md.osfd;
+ op.arg2.buffer = hdtrl;
+ op.arg4.flags = send_flags;
+ op.nbytes_to_send = nbytes_to_send - count;
+ op.result.code = count;
+ op.timeout = timeout;
+ op.function = pt_hpux_transmitfile_cont;
+ op.event = POLLOUT | POLLPRI;
+ count = pt_Continue(&op);
+ syserrno = op.syserrno;
+ }
+
+ if (count == -1) {
+ _MD_hpux_map_sendfile_error(syserrno);
+ return -1;
+ }
+ if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) {
+ PR_Close(sd);
+ }
+ return count;
+}
+#endif /* HPUX11 */
+
+static PRInt32 pt_TransmitFile(
+ PRFileDesc *sd, PRFileDesc *fd, const void *headers,
+ PRInt32 hlen, PRTransmitFileFlags flags, PRIntervalTime timeout)
+{
+ if (pt_TestAbort()) return -1;
+
+#ifdef HPUX11
+ return pt_HPUXTransmitFile(sd, fd, headers, hlen, flags, timeout);
+#else
+ return _PR_UnixTransmitFile(sd, fd, headers, hlen, flags, timeout);
+#endif
+} /* pt_TransmitFile */
+
+/*
+ * XXX: When IPv6 is running, we need to see if this code works
+ * with a PRNetAddr structure that supports both IPv4 and IPv6.
+ */
+static PRInt32 pt_AcceptRead(
+ PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr,
+ void *buf, PRInt32 amount, PRIntervalTime timeout)
+{
+ PRInt32 rv = -1;
+ PRNetAddr remote;
+ PRFileDesc *accepted = NULL;
+ PRIntervalTime start, elapsed;
+
+ if (pt_TestAbort()) return rv;
+
+ if (PR_INTERVAL_NO_TIMEOUT != timeout) start = PR_IntervalNow();
+ if ((accepted = PR_Accept(sd, &remote, timeout)) == NULL) return rv;
+
+ if (PR_INTERVAL_NO_TIMEOUT != timeout)
+ {
+ elapsed = (PRIntervalTime) (PR_IntervalNow() - start);
+ if (elapsed > timeout)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ goto failed;
+ }
+ else timeout = timeout - elapsed;
+ }
+
+ rv = PR_Recv(accepted, buf, amount, 0, timeout);
+ if (rv >= 0)
+ {
+ /* copy the new info out where caller can see it */
+ *nd = accepted;
+ *raddr = (PRNetAddr *)((char*)buf + amount);
+ memcpy(*raddr, &remote, PR_NETADDR_SIZE(&remote));
+ return rv;
+ }
+
+failed:
+ PR_Close(accepted);
+ return rv;
+} /* pt_AcceptRead */
+
+static PRStatus pt_GetSockName(PRFileDesc *fd, PRNetAddr *addr)
+{
+ PRIntn rv = -1;
+ pt_SockLen addr_len = sizeof(PRNetAddr);
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ rv = getsockname(
+ fd->secret->md.osfd, (struct sockaddr*)addr, &addr_len);
+#ifdef AIX
+ /* mask off the first byte of struct sockaddr (the length field) */
+ if (addr) addr->inet.family &= 0x00ff;
+#endif
+ if (rv == -1) {
+ pt_MapError(_PR_MD_MAP_GETSOCKNAME_ERROR, errno);
+ return PR_FAILURE;
+ } else {
+ PR_ASSERT(addr_len == PR_NETADDR_SIZE(addr));
+#if defined(_PR_INET6)
+ PR_ASSERT(addr->raw.family == AF_INET || addr->raw.family == AF_INET6);
+#else
+ PR_ASSERT(addr->raw.family == AF_INET);
+#endif
+ return PR_SUCCESS;
+ }
+} /* pt_GetSockName */
+
+static PRStatus pt_GetPeerName(PRFileDesc *fd, PRNetAddr *addr)
+{
+ PRIntn rv = -1;
+ pt_SockLen addr_len = sizeof(PRNetAddr);
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ rv = getpeername(
+ fd->secret->md.osfd, (struct sockaddr*)addr, &addr_len);
+
+#ifdef AIX
+ /* mask off the first byte of struct sockaddr (the length field) */
+ if (addr) addr->inet.family &= 0x00ff;
+#endif
+ if (rv == -1) {
+ pt_MapError(_PR_MD_MAP_GETPEERNAME_ERROR, errno);
+ return PR_FAILURE;
+ } else {
+ PR_ASSERT(addr_len == PR_NETADDR_SIZE(addr));
+#if defined(_PR_INET6)
+ PR_ASSERT(addr->raw.family == AF_INET || addr->raw.family == AF_INET6);
+#else
+ PR_ASSERT(addr->raw.family == AF_INET);
+#endif
+ return PR_SUCCESS;
+ }
+} /* pt_GetPeerName */
+
+static PRStatus pt_GetSockOpt(
+ PRFileDesc *fd, PRSockOption optname, void* optval, PRInt32* optlen)
+{
+ PRIntn rv = -1;
+ PRInt32 level, name;
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ /*
+ * PR_SockOpt_Nonblocking is a special case that does not
+ * translate to a getsockopt() call.
+ */
+ if (PR_SockOpt_Nonblocking == optname)
+ {
+ PR_ASSERT(sizeof(PRIntn) <= *optlen);
+ *((PRIntn *) optval) = (PRIntn) fd->secret->nonblocking;
+ *optlen = sizeof(PRIntn);
+ return PR_SUCCESS;
+ }
+
+ rv = _PR_MapOptionName(optname, &level, &name);
+ if (0 == rv)
+ {
+ if (PR_SockOpt_Linger == optname)
+ {
+ struct linger linger;
+ pt_SockLen len = sizeof(linger);
+ rv = getsockopt(fd->secret->md.osfd, level, name,
+ (char *) &linger, &len);
+ if (0 == rv)
+ {
+ ((PRLinger*)(optval))->polarity = linger.l_onoff
+ ? PR_TRUE : PR_FALSE;
+ ((PRLinger*)(optval))->linger = PR_SecondsToInterval(
+ linger.l_linger);
+ *optlen = sizeof(PRLinger);
+ }
+ }
+ else
+ {
+ /* Make sure the pointer type cast below is safe */
+ PR_ASSERT(sizeof(PRInt32) == sizeof(PRIntn));
+ rv = getsockopt(fd->secret->md.osfd, level, name,
+ optval, (pt_SockLen*)optlen);
+ }
+ }
+
+ if (rv == -1) {
+ pt_MapError(_PR_MD_MAP_GETSOCKOPT_ERROR, errno);
+ return PR_FAILURE;
+ } else {
+ return PR_SUCCESS;
+ }
+} /* pt_GetSockOpt */
+
+static PRStatus pt_SetSockOpt(
+ PRFileDesc *fd, PRSockOption optname, const void* optval, PRInt32 optlen)
+{
+ PRIntn rv = -1;
+ PRInt32 level, name;
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ /*
+ * PR_SockOpt_Nonblocking is a special case that does not
+ * translate to a setsockopt() call.
+ */
+ if (PR_SockOpt_Nonblocking == optname)
+ {
+ PR_ASSERT(sizeof(PRIntn) == optlen);
+ fd->secret->nonblocking = *((PRIntn *) optval) ? PR_TRUE : PR_FALSE;
+ return PR_SUCCESS;
+ }
+
+ rv = _PR_MapOptionName(optname, &level, &name);
+ if (0 == rv)
+ {
+ if (PR_SockOpt_Linger == optname)
+ {
+ struct linger linger;
+ linger.l_onoff = ((PRLinger*)(optval))->polarity;
+ linger.l_linger = PR_IntervalToSeconds(
+ ((PRLinger*)(optval))->linger);
+ rv = setsockopt(fd->secret->md.osfd, level, name,
+ (char *) &linger, sizeof(linger));
+ }
+ else
+ {
+ rv = setsockopt(fd->secret->md.osfd, level, name,
+ optval, optlen);
+ }
+ }
+
+ if (rv == -1) {
+ pt_MapError(_PR_MD_MAP_SETSOCKOPT_ERROR, errno);
+ return PR_FAILURE;
+ } else {
+ return PR_SUCCESS;
+ }
+} /* pt_SetSockOpt */
+
+static PRStatus pt_GetSocketOption(PRFileDesc *fd, PRSocketOptionData *data)
+{
+ PRIntn rv;
+ pt_SockLen length;
+ PRInt32 level, name;
+
+ /*
+ * PR_SockOpt_Nonblocking is a special case that does not
+ * translate to a getsockopt() call
+ */
+ if (PR_SockOpt_Nonblocking == data->option)
+ {
+ data->value.non_blocking = fd->secret->nonblocking;
+ return PR_SUCCESS;
+ }
+
+ rv = _PR_MapOptionName(data->option, &level, &name);
+ if (PR_SUCCESS == rv)
+ {
+ switch (data->option)
+ {
+ case PR_SockOpt_Linger:
+ {
+ struct linger linger;
+ length = sizeof(linger);
+ rv = getsockopt(
+ fd->secret->md.osfd, level, name, (char *) &linger, &length);
+ PR_ASSERT((-1 == rv) || (sizeof(linger) == length));
+ data->value.linger.polarity =
+ (linger.l_onoff) ? PR_TRUE : PR_FALSE;
+ data->value.linger.linger =
+ PR_SecondsToInterval(linger.l_linger);
+ break;
+ }
+ case PR_SockOpt_Reuseaddr:
+ case PR_SockOpt_Keepalive:
+ case PR_SockOpt_NoDelay:
+ {
+ PRIntn value;
+ length = sizeof(PRIntn);
+ rv = getsockopt(
+ fd->secret->md.osfd, level, name, (char*)&value, &length);
+ PR_ASSERT((-1 == rv) || (sizeof(PRIntn) == length));
+ data->value.reuse_addr = (0 == value) ? PR_FALSE : PR_TRUE;
+ break;
+ }
+ case PR_SockOpt_McastLoopback:
+ {
+ PRUint8 boolean;
+ length = sizeof(boolean);
+ rv = getsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&boolean, &length);
+ PR_ASSERT((-1 == rv) || (sizeof(boolean) == length));
+ data->value.mcast_loopback = (0 == boolean) ? PR_FALSE : PR_TRUE;
+ break;
+ }
+ case PR_SockOpt_RecvBufferSize:
+ case PR_SockOpt_SendBufferSize:
+ case PR_SockOpt_MaxSegment:
+ {
+ PRIntn value;
+ length = sizeof(PRIntn);
+ rv = getsockopt(
+ fd->secret->md.osfd, level, name, (char*)&value, &length);
+ PR_ASSERT((-1 == rv) || (sizeof(PRIntn) == length));
+ data->value.recv_buffer_size = value;
+ break;
+ }
+ case PR_SockOpt_IpTimeToLive:
+ case PR_SockOpt_IpTypeOfService:
+ {
+ length = sizeof(PRUintn);
+ rv = getsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&data->value.ip_ttl, &length);
+ PR_ASSERT((-1 == rv) || (sizeof(PRIntn) == length));
+ break;
+ }
+ case PR_SockOpt_McastTimeToLive:
+ {
+ PRUint8 ttl;
+ length = sizeof(ttl);
+ rv = getsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&ttl, &length);
+ PR_ASSERT((-1 == rv) || (sizeof(ttl) == length));
+ data->value.mcast_ttl = ttl;
+ break;
+ }
+ case PR_SockOpt_AddMember:
+ case PR_SockOpt_DropMember:
+ {
+ struct ip_mreq mreq;
+ length = sizeof(mreq);
+ rv = getsockopt(
+ fd->secret->md.osfd, level, name, (char*)&mreq, &length);
+ PR_ASSERT((-1 == rv) || (sizeof(mreq) == length));
+ data->value.add_member.mcaddr.inet.ip =
+ mreq.imr_multiaddr.s_addr;
+ data->value.add_member.ifaddr.inet.ip =
+ mreq.imr_interface.s_addr;
+ break;
+ }
+ case PR_SockOpt_McastInterface:
+ {
+ length = sizeof(data->value.mcast_if.inet.ip);
+ rv = getsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&data->value.mcast_if.inet.ip, &length);
+ PR_ASSERT((-1 == rv)
+ || (sizeof(data->value.mcast_if.inet.ip) == length));
+ break;
+ }
+ default:
+ PR_NOT_REACHED("Unknown socket option");
+ break;
+ }
+ if (-1 == rv) _PR_MD_MAP_GETSOCKOPT_ERROR(errno);
+ }
+ return (-1 == rv) ? PR_FAILURE : PR_SUCCESS;
+} /* pt_GetSocketOption */
+
+static PRStatus pt_SetSocketOption(PRFileDesc *fd, const PRSocketOptionData *data)
+{
+ PRIntn rv;
+ PRInt32 level, name;
+
+ /*
+ * PR_SockOpt_Nonblocking is a special case that does not
+ * translate to a setsockopt call.
+ */
+ if (PR_SockOpt_Nonblocking == data->option)
+ {
+ fd->secret->nonblocking = data->value.non_blocking;
+ return PR_SUCCESS;
+ }
+
+ rv = _PR_MapOptionName(data->option, &level, &name);
+ if (PR_SUCCESS == rv)
+ {
+ switch (data->option)
+ {
+ case PR_SockOpt_Linger:
+ {
+ struct linger linger;
+ linger.l_onoff = data->value.linger.polarity;
+ linger.l_linger = PR_IntervalToSeconds(data->value.linger.linger);
+ rv = setsockopt(
+ fd->secret->md.osfd, level, name, (char*)&linger, sizeof(linger));
+ break;
+ }
+ case PR_SockOpt_Reuseaddr:
+ case PR_SockOpt_Keepalive:
+ case PR_SockOpt_NoDelay:
+ {
+ PRIntn value = (data->value.reuse_addr) ? 1 : 0;
+ rv = setsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&value, sizeof(PRIntn));
+ break;
+ }
+ case PR_SockOpt_McastLoopback:
+ {
+ PRUint8 boolean = data->value.mcast_loopback ? 1 : 0;
+ rv = setsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&boolean, sizeof(boolean));
+ break;
+ }
+ case PR_SockOpt_RecvBufferSize:
+ case PR_SockOpt_SendBufferSize:
+ case PR_SockOpt_MaxSegment:
+ {
+ PRIntn value = data->value.recv_buffer_size;
+ rv = setsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&value, sizeof(PRIntn));
+ break;
+ }
+ case PR_SockOpt_IpTimeToLive:
+ case PR_SockOpt_IpTypeOfService:
+ {
+ rv = setsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&data->value.ip_ttl, sizeof(PRUintn));
+ break;
+ }
+ case PR_SockOpt_McastTimeToLive:
+ {
+ PRUint8 ttl = data->value.mcast_ttl;
+ rv = setsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&ttl, sizeof(ttl));
+ break;
+ }
+ case PR_SockOpt_AddMember:
+ case PR_SockOpt_DropMember:
+ {
+ struct ip_mreq mreq;
+ mreq.imr_multiaddr.s_addr =
+ data->value.add_member.mcaddr.inet.ip;
+ mreq.imr_interface.s_addr =
+ data->value.add_member.ifaddr.inet.ip;
+ rv = setsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&mreq, sizeof(mreq));
+ break;
+ }
+ case PR_SockOpt_McastInterface:
+ {
+ rv = setsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&data->value.mcast_if.inet.ip,
+ sizeof(data->value.mcast_if.inet.ip));
+ break;
+ }
+ default:
+ PR_NOT_REACHED("Unknown socket option");
+ break;
+ }
+ if (-1 == rv) _PR_MD_MAP_SETSOCKOPT_ERROR(errno);
+ }
+ return (-1 == rv) ? PR_FAILURE : PR_SUCCESS;
+} /* pt_SetSocketOption */
+
+/*****************************************************************************/
+/****************************** I/O method objects ***************************/
+/*****************************************************************************/
+
+static PRIOMethods _pr_file_methods = {
+ PR_DESC_FILE,
+ pt_Close,
+ pt_Read,
+ pt_Write,
+ pt_Available,
+ pt_Available64,
+ pt_Fsync,
+ pt_Seek,
+ pt_Seek64,
+ pt_FileInfo,
+ pt_FileInfo64,
+ (PRWritevFN)_PR_InvalidInt,
+ (PRConnectFN)_PR_InvalidStatus,
+ (PRAcceptFN)_PR_InvalidDesc,
+ (PRBindFN)_PR_InvalidStatus,
+ (PRListenFN)_PR_InvalidStatus,
+ (PRShutdownFN)_PR_InvalidStatus,
+ (PRRecvFN)_PR_InvalidInt,
+ (PRSendFN)_PR_InvalidInt,
+ (PRRecvfromFN)_PR_InvalidInt,
+ (PRSendtoFN)_PR_InvalidInt,
+ (PRPollFN)0,
+ (PRAcceptreadFN)_PR_InvalidInt,
+ (PRTransmitfileFN)_PR_InvalidInt,
+ (PRGetsocknameFN)_PR_InvalidStatus,
+ (PRGetpeernameFN)_PR_InvalidStatus,
+ (PRGetsockoptFN)_PR_InvalidStatus,
+ (PRSetsockoptFN)_PR_InvalidStatus,
+};
+
+static PRIOMethods _pr_tcp_methods = {
+ PR_DESC_SOCKET_TCP,
+ pt_Close,
+ pt_Read,
+ pt_Write,
+ pt_Available,
+ pt_Available64,
+ pt_Synch,
+ (PRSeekFN)_PR_InvalidInt,
+ (PRSeek64FN)_PR_InvalidInt64,
+ (PRFileInfoFN)_PR_InvalidStatus,
+ (PRFileInfo64FN)_PR_InvalidStatus,
+ pt_Writev,
+ pt_Connect,
+ pt_Accept,
+ pt_Bind,
+ pt_Listen,
+ pt_Shutdown,
+ pt_Recv,
+ pt_Send,
+ (PRRecvfromFN)_PR_InvalidInt,
+ (PRSendtoFN)_PR_InvalidInt,
+ (PRPollFN)0,
+ pt_AcceptRead,
+ pt_TransmitFile,
+ pt_GetSockName,
+ pt_GetPeerName,
+ pt_GetSockOpt,
+ pt_SetSockOpt,
+ pt_GetSocketOption,
+ pt_SetSocketOption
+};
+
+static PRIOMethods _pr_udp_methods = {
+ PR_DESC_SOCKET_UDP,
+ pt_Close,
+ pt_Read,
+ pt_Write,
+ pt_Available,
+ pt_Available64,
+ pt_Synch,
+ (PRSeekFN)_PR_InvalidInt,
+ (PRSeek64FN)_PR_InvalidInt64,
+ (PRFileInfoFN)_PR_InvalidStatus,
+ (PRFileInfo64FN)_PR_InvalidStatus,
+ pt_Writev,
+ pt_Connect,
+ (PRAcceptFN)_PR_InvalidDesc,
+ pt_Bind,
+ pt_Listen,
+ pt_Shutdown,
+ pt_Recv,
+ pt_Send,
+ pt_RecvFrom,
+ pt_SendTo,
+ (PRPollFN)0,
+ (PRAcceptreadFN)_PR_InvalidInt,
+ (PRTransmitfileFN)_PR_InvalidInt,
+ pt_GetSockName,
+ pt_GetPeerName,
+ pt_GetSockOpt,
+ pt_SetSockOpt,
+ pt_GetSocketOption,
+ pt_SetSocketOption
+};
+
+#if defined(_PR_FCNTL_FLAGS)
+#undef _PR_FCNTL_FLAGS
+#endif
+
+#if defined(HPUX) || defined(OSF1) || defined(SOLARIS) || defined (IRIX) \
+ || defined(AIX) || defined(LINUX) || defined(FREEBSD)
+#define _PR_FCNTL_FLAGS O_NONBLOCK
+#else
+#error "Can't determine architecture"
+#endif
+
+static PRFileDesc *pt_SetMethods(PRIntn osfd, PRDescType type)
+{
+ PRInt32 flags, one = 1;
+ PRFileDesc *fd = pt_Getfd();
+
+ if (fd == NULL) PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ else
+ {
+ fd->secret->md.osfd = osfd;
+ fd->secret->state = _PR_FILEDESC_OPEN;
+ switch (type)
+ {
+ case PR_DESC_FILE:
+ fd->methods = PR_GetFileMethods();
+ break;
+ case PR_DESC_SOCKET_TCP:
+ fd->methods = PR_GetTCPMethods();
+ flags = fcntl(osfd, F_GETFL, 0);
+ flags |= _PR_FCNTL_FLAGS;
+ (void)fcntl(osfd, F_SETFL, flags);
+ (void)setsockopt(osfd, SOL_SOCKET, SO_KEEPALIVE,
+ (_PRSockOptVal_t) &one, sizeof(one));
+ break;
+ case PR_DESC_SOCKET_UDP:
+ fd->methods = PR_GetUDPMethods();
+ flags = fcntl(osfd, F_GETFL, 0);
+ flags |= _PR_FCNTL_FLAGS;
+ (void)fcntl(osfd, F_SETFL, flags);
+ break;
+ default:
+ break;
+ }
+ }
+ return fd;
+} /* pt_SetMethods */
+
+PR_IMPLEMENT(PRIOMethods*) PR_GetFileMethods()
+{
+ return &_pr_file_methods;
+} /* PR_GetFileMethods */
+
+PR_IMPLEMENT(PRIOMethods*) PR_GetTCPMethods()
+{
+ return &_pr_tcp_methods;
+} /* PR_GetTCPMethods */
+
+PR_IMPLEMENT(PRIOMethods*) PR_GetUDPMethods()
+{
+ return &_pr_udp_methods;
+} /* PR_GetUDPMethods */
+
+PR_IMPLEMENT(PRFileDesc*) PR_AllocFileDesc(PRInt32 osfd, PRIOMethods *methods)
+{
+ PRFileDesc *fd = pt_Getfd();
+
+ /*
+ * Assert that the file descriptor is small enough to fit in the
+ * fd_set passed to select
+ */
+ PR_ASSERT(osfd < FD_SETSIZE);
+ if (NULL == fd) goto failed;
+
+ fd->methods = methods;
+ fd->secret->md.osfd = osfd;
+ /* Make fd non-blocking */
+ if (osfd > 2)
+ {
+ /* Don't mess around with stdin, stdout or stderr */
+ PRIntn flags;
+ flags = fcntl(osfd, F_GETFL, 0);
+ fcntl(osfd, F_SETFL, flags | _PR_FCNTL_FLAGS);
+ }
+ fd->secret->state = _PR_FILEDESC_OPEN;
+ return fd;
+
+failed:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return fd;
+} /* PR_AllocFileDesc */
+
+PR_IMPLEMENT(PRFileDesc*) PR_Socket(PRInt32 domain, PRInt32 type, PRInt32 proto)
+{
+ PRIntn osfd;
+ PRDescType ftype;
+ PRFileDesc *fd = NULL;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ if (pt_TestAbort()) return NULL;
+
+ if (PF_INET != domain
+#if defined(_PR_INET6)
+ && PF_INET6 != domain
+#endif
+ )
+ {
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0);
+ return fd;
+ }
+ if (type == SOCK_STREAM) ftype = PR_DESC_SOCKET_TCP;
+ else if (type == SOCK_DGRAM) ftype = PR_DESC_SOCKET_UDP;
+ else
+ {
+ (void)PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0);
+ return fd;
+ }
+
+ osfd = socket(domain, type, proto);
+ if (osfd == -1) pt_MapError(_PR_MD_MAP_SOCKET_ERROR, errno);
+ else
+ {
+ fd = pt_SetMethods(osfd, ftype);
+ if (fd == NULL) close(osfd);
+ }
+ return fd;
+} /* PR_Socket */
+
+/*****************************************************************************/
+/****************************** I/O public methods ***************************/
+/*****************************************************************************/
+
+PR_IMPLEMENT(PRFileDesc*) PR_Open(const char *name, PRIntn flags, PRIntn mode)
+{
+ PRFileDesc *fd = NULL;
+ PRIntn syserrno, osfd = -1, osflags = 0;;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ if (pt_TestAbort()) return NULL;
+
+ if (flags & PR_RDONLY) osflags |= O_RDONLY;
+ if (flags & PR_WRONLY) osflags |= O_WRONLY;
+ if (flags & PR_RDWR) osflags |= O_RDWR;
+ if (flags & PR_APPEND) osflags |= O_APPEND;
+ if (flags & PR_TRUNCATE) osflags |= O_TRUNC;
+
+ /*
+ ** We have to hold the lock across the creation in order to
+ ** enforce the sematics of PR_Rename(). (see the latter for
+ ** more details)
+ */
+ if (flags & PR_CREATE_FILE)
+ {
+ osflags |= O_CREAT;
+ if (NULL !=_pr_rename_lock)
+ PR_Lock(_pr_rename_lock);
+ }
+
+ osfd = open(name, osflags, mode);
+ syserrno = errno;
+
+ if ((flags & PR_CREATE_FILE) && (NULL !=_pr_rename_lock))
+ PR_Unlock(_pr_rename_lock);
+
+ if (osfd == -1)
+ pt_MapError(_PR_MD_MAP_OPEN_ERROR, syserrno);
+ else
+ {
+ fd = pt_SetMethods(osfd, PR_DESC_FILE);
+ if (fd == NULL) close(osfd); /* $$$ whoops! this is bad $$$ */
+ }
+ return fd;
+} /* PR_Open */
+
+PR_IMPLEMENT(PRStatus) PR_Delete(const char *name)
+{
+ PRIntn rv = -1;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ rv = unlink(name);
+
+ if (rv == -1) {
+ pt_MapError(_PR_MD_MAP_UNLINK_ERROR, errno);
+ return PR_FAILURE;
+ } else
+ return PR_SUCCESS;
+} /* PR_Delete */
+
+PR_IMPLEMENT(PRStatus) PR_Access(const char *name, PRAccessHow how)
+{
+ PRIntn rv;
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ switch (how)
+ {
+ case PR_ACCESS_READ_OK:
+ rv = access(name, R_OK);
+ break;
+ case PR_ACCESS_WRITE_OK:
+ rv = access(name, W_OK);
+ break;
+ case PR_ACCESS_EXISTS:
+ default:
+ rv = access(name, F_OK);
+ }
+ if (0 == rv) return PR_SUCCESS;
+ pt_MapError(_PR_MD_MAP_ACCESS_ERROR, errno);
+ return PR_FAILURE;
+
+} /* PR_Access */
+
+PR_IMPLEMENT(PRStatus) PR_GetFileInfo(const char *fn, PRFileInfo *info)
+{
+ PRInt32 rv;
+ struct stat sb;
+ PRInt64 s, s2us;
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ if ((rv = stat(fn, &sb)) == 0 )
+ {
+ if (info)
+ {
+ if (S_IFREG & sb.st_mode)
+ info->type = PR_FILE_FILE ;
+ else if (S_IFDIR & sb.st_mode)
+ info->type = PR_FILE_DIRECTORY;
+ else
+ info->type = PR_FILE_OTHER;
+ info->size = sb.st_size;
+#if defined(IRIX) && defined(HAVE_LONG_LONG)
+ info->modifyTime = (PR_USEC_PER_SEC * (PRInt64)sb.st_mtim.tv_sec);
+ info->creationTime = (PR_USEC_PER_SEC * (PRInt64)sb.st_ctim.tv_sec);
+#else
+ LL_I2L(s, sb.st_mtime);
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_MUL(s, s, s2us);
+ info->modifyTime = s;
+ LL_I2L(s, sb.st_ctime);
+ LL_MUL(s, s, s2us);
+ info->creationTime = s;
+#endif
+ }
+ }
+ return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
+} /* PR_GetFileInfo */
+
+PR_IMPLEMENT(PRStatus) PR_Rename(const char *from, const char *to)
+{
+ PRIntn rv = -1;
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ /*
+ ** We have to acquire a lock here to stiffle anybody trying to create
+ ** a new file at the same time. And we have to hold that lock while we
+ ** test to see if the file exists and do the rename. The other place
+ ** where the lock is held is in PR_Open() when possibly creating a
+ ** new file.
+ */
+
+ PR_Lock(_pr_rename_lock);
+ rv = access(to, F_OK);
+ if (0 == rv)
+ {
+ PR_SetError(PR_FILE_EXISTS_ERROR, 0);
+ rv = -1;
+ }
+ else
+ {
+ rv = rename(from, to);
+ if (rv == -1)
+ pt_MapError(_PR_MD_MAP_RENAME_ERROR, errno);
+ }
+ PR_Unlock(_pr_rename_lock);
+ return (-1 == rv) ? PR_FAILURE : PR_SUCCESS;
+} /* PR_Rename */
+
+PR_IMPLEMENT(PRStatus) PR_CloseDir(PRDir *dir)
+{
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ if (NULL != dir->md.d)
+ {
+ closedir(dir->md.d);
+ dir->md.d = NULL;
+ }
+ return PR_SUCCESS;
+} /* PR_CloseDir */
+
+PR_IMPLEMENT(PRStatus) PR_MkDir(const char *name, PRIntn mode)
+{
+ PRInt32 rv = -1;
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ /*
+ ** This lock is used to enforce rename semantics as described
+ ** in PR_Rename.
+ */
+ if (NULL !=_pr_rename_lock)
+ PR_Lock(_pr_rename_lock);
+ rv = mkdir(name, mode);
+ if (-1 == rv)
+ pt_MapError(_PR_MD_MAP_MKDIR_ERROR, errno);
+ if (NULL !=_pr_rename_lock)
+ PR_Unlock(_pr_rename_lock);
+
+ return (-1 == rv) ? PR_FAILURE : PR_SUCCESS;
+} /* PR_Mkdir */
+
+PR_IMPLEMENT(PRStatus) PR_RmDir(const char *name)
+{
+ PRInt32 rv;
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ rv = rmdir(name);
+ if (0 == rv) {
+ return PR_SUCCESS;
+ } else {
+ pt_MapError(_PR_MD_MAP_RMDIR_ERROR, errno);
+ return PR_FAILURE;
+ }
+} /* PR_Rmdir */
+
+
+PR_IMPLEMENT(PRDir*) PR_OpenDir(const char *name)
+{
+ DIR *osdir;
+ PRDir *dir = NULL;
+
+ if (pt_TestAbort()) return dir;
+
+ osdir = opendir(name);
+ if (osdir == NULL)
+ pt_MapError(_PR_MD_MAP_OPENDIR_ERROR, errno);
+ else
+ {
+ dir = PR_NEWZAP(PRDir);
+ dir->md.d = osdir;
+ }
+ return dir;
+} /* PR_OpenDir */
+
+PR_IMPLEMENT(PRInt32) PR_Poll(
+ PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
+{
+ PRInt32 ready = 0;
+ /*
+ * For restarting poll() if it is interrupted by a signal.
+ * We use these variables to figure out how much time has
+ * elapsed and how much of the timeout still remains.
+ */
+ PRIntervalTime start, elapsed, remaining;
+
+ if (0 == npds) PR_Sleep(timeout);
+ else
+ {
+ PRIntn index, msecs;
+ struct pollfd *syspoll = NULL;
+ syspoll = (struct pollfd*)PR_Malloc(npds * sizeof(struct pollfd));
+ for (index = 0; index < npds; ++index)
+ {
+ PRFileDesc *bottom = pds[index].fd;
+
+ if (bottom == NULL)
+ {
+ /* make poll() ignore this entry */
+ syspoll[index].fd = -1;
+ continue;
+ }
+
+ while (bottom->lower != NULL) bottom = bottom->lower;
+ syspoll[index].fd = bottom->secret->md.osfd;
+
+ syspoll[index].events = 0;
+ if (pds[index].in_flags & PR_POLL_READ)
+ syspoll[index].events |= POLLIN;
+ if (pds[index].in_flags & PR_POLL_WRITE)
+ syspoll[index].events |= POLLOUT;
+ if (pds[index].in_flags & PR_POLL_EXCEPT)
+ syspoll[index].events |= POLLPRI;
+ if (_PR_FILEDESC_OPEN == bottom->secret->state)
+ pds[index].out_flags = 0; /* init the result */
+ else
+ {
+ ready += 1; /* this will cause an abrupt return */
+ pds[index].out_flags = POLLNVAL; /* bogii */
+ }
+ }
+ if (0 == ready)
+ {
+ switch (timeout)
+ {
+ case PR_INTERVAL_NO_WAIT: msecs = 0; break;
+ case PR_INTERVAL_NO_TIMEOUT: msecs = -1; break;
+ default:
+ msecs = PR_IntervalToMilliseconds(timeout);
+ start = PR_IntervalNow();
+ }
+
+retry:
+ ready = poll(syspoll, npds, msecs);
+ if (-1 == ready)
+ {
+ PRIntn oserror = errno;
+ PRErrorCode prerror;
+
+ switch (oserror) {
+ case EAGAIN:
+ prerror = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+ case EINTR:
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ goto retry;
+ else if (timeout == PR_INTERVAL_NO_WAIT)
+ ready = 0; /* don't retry, just time out */
+ {
+ elapsed = (PRIntervalTime) (PR_IntervalNow()
+ - start);
+ if (elapsed > timeout)
+ ready = 0; /* timed out */
+ else
+ {
+ remaining = timeout - elapsed;
+ msecs = PR_IntervalToMilliseconds(remaining);
+ goto retry;
+ }
+ }
+ break;
+ case EINVAL:
+ prerror = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ case EFAULT:
+ prerror = PR_ACCESS_FAULT_ERROR;
+ break;
+ default:
+ prerror = PR_UNKNOWN_ERROR;
+ break;
+ }
+ PR_SetError(prerror, oserror);
+ }
+ else if (ready > 0)
+ {
+ for (index = 0; index < npds; ++index)
+ {
+ if (pds[index].fd == NULL) continue;
+ PR_ASSERT(0 == pds[index].out_flags);
+ if (0 != syspoll[index].revents)
+ {
+ if (syspoll[index].revents & POLLIN)
+ pds[index].out_flags |= PR_POLL_READ;
+ if (syspoll[index].revents & POLLOUT)
+ pds[index].out_flags |= PR_POLL_WRITE;
+ if (syspoll[index].revents & POLLPRI)
+ pds[index].out_flags |= PR_POLL_EXCEPT;
+ if (syspoll[index].revents & POLLERR)
+ pds[index].out_flags |= PR_POLL_ERR;
+ if (syspoll[index].revents & POLLNVAL)
+ pds[index].out_flags |= PR_POLL_NVAL;
+ }
+ }
+ }
+ }
+
+ PR_DELETE(syspoll);
+
+ }
+ return ready;
+
+} /* PR_Poll */
+
+PR_IMPLEMENT(PRDirEntry*) PR_ReadDir(PRDir *dir, PRDirFlags flags)
+{
+ struct dirent *dp;
+
+ if (pt_TestAbort()) return NULL;
+
+ for (;;)
+ {
+ dp = readdir(dir->md.d);
+ if (NULL == dp) return NULL;
+ if ((flags & PR_SKIP_DOT)
+ && ('.' == dp->d_name[0])
+ && (0 == dp->d_name[1])) continue;
+ if ((flags & PR_SKIP_DOT_DOT)
+ && ('.' == dp->d_name[0])
+ && ('.' == dp->d_name[1])
+ && (0 == dp->d_name[2])) continue;
+ if ((flags & PR_SKIP_HIDDEN) && ('.' == dp->d_name[0]))
+ continue;
+ break;
+ }
+ dir->d.name = dp->d_name;
+ return &dir->d;
+} /* PR_ReadDir */
+
+PR_IMPLEMENT(PRFileDesc*) PR_NewUDPSocket()
+{
+ PRFileDesc *fd = NULL;
+ PRIntn osfd = -1, syserrno;
+ PRIntn domain = PF_INET;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ if (pt_TestAbort()) return NULL;
+
+#if defined(_PR_INET6)
+ if (_pr_ipv6_enabled)
+ domain = PF_INET6;
+#endif
+ osfd = socket(domain, SOCK_DGRAM, 0);
+ syserrno = errno;
+
+ if (osfd == -1)
+ pt_MapError(_PR_MD_MAP_SOCKET_ERROR, syserrno);
+ else
+ {
+ fd = pt_SetMethods(osfd, PR_DESC_SOCKET_UDP);
+ if (fd == NULL) close(osfd);
+ }
+ return fd;
+} /* PR_NewUDPSocket */
+
+PR_IMPLEMENT(PRFileDesc*) PR_NewTCPSocket()
+{
+ PRIntn osfd = -1;
+ PRFileDesc *fd = NULL;
+ PRIntn domain = PF_INET;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ if (pt_TestAbort()) return NULL;
+
+#if defined(_PR_INET6)
+ if (_pr_ipv6_enabled)
+ domain = PF_INET6;
+#endif
+ osfd = socket(domain, SOCK_STREAM, 0);
+
+ if (osfd == -1)
+ pt_MapError(_PR_MD_MAP_SOCKET_ERROR, errno);
+ else
+ {
+ fd = pt_SetMethods(osfd, PR_DESC_SOCKET_TCP);
+ if (fd == NULL) close(osfd);
+ }
+ return fd;
+} /* PR_NewTCPSocket */
+
+PR_IMPLEMENT(PRStatus) PR_NewTCPSocketPair(PRFileDesc *fds[2])
+{
+ PRInt32 osfd[2];
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, osfd) == -1) {
+ pt_MapError(_PR_MD_MAP_SOCKETPAIR_ERROR, errno);
+ return PR_FAILURE;
+ }
+
+ fds[0] = pt_SetMethods(osfd[0], PR_DESC_SOCKET_TCP);
+ if (fds[0] == NULL) {
+ close(osfd[0]);
+ close(osfd[1]);
+ return PR_FAILURE;
+ }
+ fds[1] = pt_SetMethods(osfd[1], PR_DESC_SOCKET_TCP);
+ if (fds[1] == NULL) {
+ PR_Close(fds[0]);
+ close(osfd[1]);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+} /* PR_NewTCPSocketPair */
+
+PR_IMPLEMENT(PRStatus) PR_CreatePipe(
+ PRFileDesc **readPipe,
+ PRFileDesc **writePipe
+)
+{
+ int pipefd[2];
+ int flags;
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ if (pipe(pipefd) == -1)
+ {
+ /* XXX map pipe error */
+ PR_SetError(PR_UNKNOWN_ERROR, errno);
+ return PR_FAILURE;
+ }
+ *readPipe = pt_SetMethods(pipefd[0], PR_DESC_FILE);
+ if (NULL == *readPipe)
+ {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return PR_FAILURE;
+ }
+ flags = fcntl(pipefd[0], F_GETFL, 0);
+ flags |= _PR_FCNTL_FLAGS;
+ (void)fcntl(pipefd[0], F_SETFL, flags);
+ *writePipe = pt_SetMethods(pipefd[1], PR_DESC_FILE);
+ if (NULL == *writePipe)
+ {
+ PR_Close(*readPipe);
+ close(pipefd[1]);
+ return PR_FAILURE;
+ }
+ flags = fcntl(pipefd[1], F_GETFL, 0);
+ flags |= _PR_FCNTL_FLAGS;
+ (void)fcntl(pipefd[1], F_SETFL, flags);
+ return PR_SUCCESS;
+}
+
+/*****************************************************************************/
+/***************************** I/O friends methods ***************************/
+/*****************************************************************************/
+
+PR_IMPLEMENT(PRFileDesc*) PR_ImportFile(PRInt32 osfd)
+{
+ PRFileDesc *fd = pt_SetMethods(osfd, PR_DESC_FILE);
+ if (NULL == fd) close(osfd);
+ return fd;
+} /* PR_ImportFile */
+
+PR_IMPLEMENT(PRFileDesc*) PR_ImportTCPSocket(PRInt32 osfd)
+{
+ PRFileDesc *fd = pt_SetMethods(osfd, PR_DESC_SOCKET_TCP);
+ if (NULL == fd) close(osfd);
+ return fd;
+} /* PR_ImportTCPSocket */
+
+PR_IMPLEMENT(PRFileDesc*) PR_ImportUDPSocket(PRInt32 osfd)
+{
+ PRFileDesc *fd = pt_SetMethods(osfd, PR_DESC_SOCKET_UDP);
+ if (NULL != fd) close(osfd);
+ return fd;
+} /* PR_ImportUDPSocket */
+
+PR_IMPLEMENT(PRInt32) PR_FileDesc2NativeHandle(PRFileDesc *fd)
+{
+ if (fd)
+ {
+ /*
+ * The fd may be layered. Chase the links to the
+ * bottom layer to get the osfd.
+ */
+ PRFileDesc *bottom = fd;
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ return bottom->secret->md.osfd;
+ }
+ else
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+} /* PR_FileDesc2NativeHandle */
+
+PR_IMPLEMENT(void) PR_ChangeFileDescNativeHandle(PRFileDesc *fd,
+ PRInt32 handle)
+{
+ if (fd) fd->secret->md.osfd = handle;
+} /* PR_ChangeFileDescNativeHandle*/
+
+PR_IMPLEMENT(PRStatus) PR_LockFile(PRFileDesc *fd)
+{
+ PRStatus status = PR_SUCCESS;
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ PR_Lock(_pr_flock_lock);
+ if (0 == fd->secret->lockCount)
+ {
+ status = _PR_MD_LOCKFILE(fd->secret->md.osfd);
+ if (PR_SUCCESS == status) fd->secret->lockCount = 1;
+ }
+ else fd->secret->lockCount += 1;
+ PR_Unlock(_pr_flock_lock);
+
+ return status;
+} /* PR_LockFile */
+
+PR_IMPLEMENT(PRStatus) PR_TLockFile(PRFileDesc *fd)
+{
+ PRStatus status = PR_SUCCESS;
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ PR_Lock(_pr_flock_lock);
+ if (0 == fd->secret->lockCount)
+ {
+ status = _PR_MD_TLOCKFILE(fd->secret->md.osfd);
+ if (PR_SUCCESS == status) fd->secret->lockCount = 1;
+ }
+ else fd->secret->lockCount += 1;
+ PR_Unlock(_pr_flock_lock);
+
+ return status;
+} /* PR_TLockFile */
+
+PR_IMPLEMENT(PRStatus) PR_UnlockFile(PRFileDesc *fd)
+{
+ PRStatus status = PR_SUCCESS;
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ PR_Lock(_pr_flock_lock);
+ if (fd->secret->lockCount == 1)
+ {
+ status = _PR_MD_UNLOCKFILE(fd->secret->md.osfd);
+ if (PR_SUCCESS == status) fd->secret->lockCount = 0;
+ }
+ else fd->secret->lockCount -= 1;
+ PR_Unlock(_pr_flock_lock);
+
+ return status;
+}
+
+/*
+ * The next two entry points should not be in the API, but they are
+ * defined here for historical (or hysterical) reasons.
+ */
+
+PRInt32 PR_GetSysfdTableMax(void)
+{
+#if defined(XP_UNIX) && !defined(AIX)
+ struct rlimit rlim;
+
+ if ( getrlimit(RLIMIT_NOFILE, &rlim) < 0)
+ return -1;
+
+ return rlim.rlim_max;
+#elif defined(AIX)
+ return sysconf(_SC_OPEN_MAX);
+#endif
+}
+
+PRInt32 PR_SetSysfdTableSize(PRIntn table_size)
+{
+#if defined(XP_UNIX) && !defined(AIX)
+ struct rlimit rlim;
+ PRInt32 tableMax = PR_GetSysfdTableMax();
+
+ if (tableMax < 0) return -1;
+ rlim.rlim_max = tableMax;
+
+ /* Grow as much as we can; even if too big */
+ if ( rlim.rlim_max < table_size )
+ rlim.rlim_cur = rlim.rlim_max;
+ else
+ rlim.rlim_cur = table_size;
+
+ if ( setrlimit(RLIMIT_NOFILE, &rlim) < 0)
+ return -1;
+
+ return rlim.rlim_cur;
+#elif defined(AIX)
+ return -1;
+#endif
+}
+
+/*
+ * PR_Stat is supported for backward compatibility; some existing Java
+ * code uses it. New code should use PR_GetFileInfo.
+ */
+
+#ifndef NO_NSPR_10_SUPPORT
+PR_IMPLEMENT(PRInt32) PR_Stat(const char *name, struct stat *buf)
+{
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) unwarned = _PR_Obsolete("PR_Stat", "PR_GetFileInfo");
+
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ if (-1 == stat(name, buf)) {
+ pt_MapError(_PR_MD_MAP_STAT_ERROR, errno);
+ return PR_FAILURE;
+ } else {
+ return PR_SUCCESS;
+ }
+}
+#endif /* ! NO_NSPR_10_SUPPORT */
+
+
+PR_IMPLEMENT(void) PR_FD_ZERO(PR_fd_set *set)
+{
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) unwarned = _PR_Obsolete("PR_FD_ZERO (PR_Select)", "PR_Poll");
+ memset(set, 0, sizeof(PR_fd_set));
+}
+
+PR_IMPLEMENT(void) PR_FD_SET(PRFileDesc *fh, PR_fd_set *set)
+{
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) unwarned = _PR_Obsolete("PR_FD_SET (PR_Select)", "PR_Poll");
+ PR_ASSERT( set->hsize < PR_MAX_SELECT_DESC );
+
+ set->harray[set->hsize++] = fh;
+}
+
+PR_IMPLEMENT(void) PR_FD_CLR(PRFileDesc *fh, PR_fd_set *set)
+{
+ PRUint32 index, index2;
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) unwarned = _PR_Obsolete("PR_FD_CLR (PR_Select)", "PR_Poll");
+
+ for (index = 0; index<set->hsize; index++)
+ if (set->harray[index] == fh) {
+ for (index2=index; index2 < (set->hsize-1); index2++) {
+ set->harray[index2] = set->harray[index2+1];
+ }
+ set->hsize--;
+ break;
+ }
+}
+
+PR_IMPLEMENT(PRInt32) PR_FD_ISSET(PRFileDesc *fh, PR_fd_set *set)
+{
+ PRUint32 index;
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) unwarned = _PR_Obsolete("PR_FD_ISSET (PR_Select)", "PR_Poll");
+ for (index = 0; index<set->hsize; index++)
+ if (set->harray[index] == fh) {
+ return 1;
+ }
+ return 0;
+}
+
+PR_IMPLEMENT(void) PR_FD_NSET(PRInt32 fd, PR_fd_set *set)
+{
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) unwarned = _PR_Obsolete("PR_FD_NSET (PR_Select)", "PR_Poll");
+ PR_ASSERT( set->nsize < PR_MAX_SELECT_DESC );
+
+ set->narray[set->nsize++] = fd;
+}
+
+PR_IMPLEMENT(void) PR_FD_NCLR(PRInt32 fd, PR_fd_set *set)
+{
+ PRUint32 index, index2;
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) unwarned = _PR_Obsolete("PR_FD_NCLR (PR_Select)", "PR_Poll");
+
+ for (index = 0; index<set->nsize; index++)
+ if (set->narray[index] == fd) {
+ for (index2=index; index2 < (set->nsize-1); index2++) {
+ set->narray[index2] = set->narray[index2+1];
+ }
+ set->nsize--;
+ break;
+ }
+}
+
+PR_IMPLEMENT(PRInt32) PR_FD_NISSET(PRInt32 fd, PR_fd_set *set)
+{
+ PRUint32 index;
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) unwarned = _PR_Obsolete("PR_FD_NISSET (PR_Select)", "PR_Poll");
+ for (index = 0; index<set->nsize; index++)
+ if (set->narray[index] == fd) {
+ return 1;
+ }
+ return 0;
+}
+
+#include <sys/types.h>
+#include <sys/time.h>
+#if !defined(SUNOS4) && !defined(HPUX) && !defined(LINUX)
+#include <sys/select.h>
+#endif
+
+static PRInt32
+_PR_getset(PR_fd_set *pr_set, fd_set *set)
+{
+ PRUint32 index;
+ PRInt32 max = 0;
+
+ if (!pr_set)
+ return 0;
+
+ FD_ZERO(set);
+
+ /* First set the pr file handle osfds */
+ for (index=0; index<pr_set->hsize; index++) {
+ FD_SET(pr_set->harray[index]->secret->md.osfd, set);
+ if (pr_set->harray[index]->secret->md.osfd > max)
+ max = pr_set->harray[index]->secret->md.osfd;
+ }
+ /* Second set the native osfds */
+ for (index=0; index<pr_set->nsize; index++) {
+ FD_SET(pr_set->narray[index], set);
+ if (pr_set->narray[index] > max)
+ max = pr_set->narray[index];
+ }
+ return max;
+}
+
+static void
+_PR_setset(PR_fd_set *pr_set, fd_set *set)
+{
+ PRUint32 index, last_used;
+
+ if (!pr_set)
+ return;
+
+ for (last_used=0, index=0; index<pr_set->hsize; index++) {
+ if ( FD_ISSET(pr_set->harray[index]->secret->md.osfd, set) ) {
+ pr_set->harray[last_used++] = pr_set->harray[index];
+ }
+ }
+ pr_set->hsize = last_used;
+
+ for (last_used=0, index=0; index<pr_set->nsize; index++) {
+ if ( FD_ISSET(pr_set->narray[index], set) ) {
+ pr_set->narray[last_used++] = pr_set->narray[index];
+ }
+ }
+ pr_set->nsize = last_used;
+}
+
+PR_IMPLEMENT(PRInt32) PR_Select(
+ PRInt32 unused, PR_fd_set *pr_rd, PR_fd_set *pr_wr,
+ PR_fd_set *pr_ex, PRIntervalTime timeout)
+{
+ fd_set rd, wr, ex;
+ struct timeval tv, *tvp;
+ PRInt32 max, max_fd;
+ PRInt32 rv;
+ /*
+ * For restarting select() if it is interrupted by a Unix signal.
+ * We use these variables to figure out how much time has elapsed
+ * and how much of the timeout still remains.
+ */
+ PRIntervalTime start, elapsed, remaining;
+
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) unwarned = _PR_Obsolete( "PR_Select", "PR_Poll");
+
+ FD_ZERO(&rd);
+ FD_ZERO(&wr);
+ FD_ZERO(&ex);
+
+ max_fd = _PR_getset(pr_rd, &rd);
+ max_fd = (max = _PR_getset(pr_wr, &wr))>max_fd?max:max_fd;
+ max_fd = (max = _PR_getset(pr_ex, &ex))>max_fd?max:max_fd;
+
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = (PRInt32)PR_IntervalToSeconds(timeout);
+ tv.tv_usec = (PRInt32)PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ start = PR_IntervalNow();
+ }
+
+retry:
+ rv = select(max_fd + 1, (_PRSelectFdSetArg_t) &rd,
+ (_PRSelectFdSetArg_t) &wr, (_PRSelectFdSetArg_t) &ex, tvp);
+
+ if (rv == -1 && errno == EINTR) {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ goto retry;
+ } else {
+ elapsed = (PRIntervalTime) (PR_IntervalNow() - start);
+ if (elapsed > timeout) {
+ rv = 0; /* timed out */
+ } else {
+ remaining = timeout - elapsed;
+ tv.tv_sec = (PRInt32)PR_IntervalToSeconds(remaining);
+ tv.tv_usec = (PRInt32)PR_IntervalToMicroseconds(
+ remaining - PR_SecondsToInterval(tv.tv_sec));
+ goto retry;
+ }
+ }
+ }
+
+ if (rv > 0) {
+ _PR_setset(pr_rd, &rd);
+ _PR_setset(pr_wr, &wr);
+ _PR_setset(pr_ex, &ex);
+ } else if (rv == -1) {
+ pt_MapError(_PR_MD_MAP_SELECT_ERROR, errno);
+ }
+ return rv;
+}
+
+#endif /* defined(_PR_PTHREADS) */
+
+/* ptio.c */
diff --git a/pr/src/pthreads/ptmisc.c b/pr/src/pthreads/ptmisc.c
new file mode 100644
index 00000000..669b7822
--- /dev/null
+++ b/pr/src/pthreads/ptmisc.c
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+** File: ptmisc.c
+** Descritpion: Implemenation of miscellaneous methods for pthreads
+*/
+
+#if defined(_PR_PTHREADS)
+
+#include "primpl.h"
+
+#include <stdio.h>
+
+#define PT_LOG(f)
+
+void _PR_InitCPUs(void) {PT_LOG("_PR_InitCPUs")}
+void _MD_StartInterrupts(void) {PT_LOG("_MD_StartInterrupts")}
+void _PR_InitStacks(void) {PT_LOG("_PR_InitStacks")}
+void _PR_InitTPD(void) {PT_LOG("_PR_InitTPD")}
+
+PR_IMPLEMENT(void) PR_SetConcurrency(PRUintn numCPUs)
+ {PT_LOG("PR_SetConcurrency")}
+
+PR_IMPLEMENT(void) PR_SetThreadRecycleMode(PRUint32 flag)
+ {PT_LOG("PR_SetThreadRecycleMode")}
+
+#endif /* defined(_PR_PTHREADS) */
+
+/* ptmisc.c */
diff --git a/pr/src/pthreads/ptsynch.c b/pr/src/pthreads/ptsynch.c
new file mode 100644
index 00000000..8906cda3
--- /dev/null
+++ b/pr/src/pthreads/ptsynch.c
@@ -0,0 +1,702 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+** File: ptsynch.c
+** Descritpion: Implemenation for thread synchronization using pthreads
+** Exports: prlock.h, prcvar.h, prmon.h, prcmon.h
+*/
+
+#if defined(_PR_PTHREADS)
+
+#include "primpl.h"
+#include "obsolete/prsem.h"
+
+#include <string.h>
+#include <pthread.h>
+#include <sys/time.h>
+
+static pthread_condattr_t _pt_cvar_attr;
+
+#if defined(DEBUG) && defined(_PR_DCETHREADS)
+static pthread_t pt_zero_tid; /* a null pthread_t (pthread_t is a struct
+ * in DCE threads) to compare with */
+#endif
+
+/**************************************************************/
+/**************************************************************/
+/*****************************LOCKS****************************/
+/**************************************************************/
+/**************************************************************/
+
+void _PR_InitLocks(void)
+{
+ int rv = PTHREAD_CONDATTR_INIT(&_pt_cvar_attr);
+ PR_ASSERT(0 == rv);
+ _PR_MD_INIT_LOCKS();
+}
+
+static void pt_PostNotifies(PRLock *lock, PRBool unlock)
+{
+ PRIntn index, rv;
+ _PT_Notified post;
+ _PT_Notified *notified, *prev = NULL;
+ /*
+ * Time to actually notify any conditions that were affected
+ * while the lock was held. Get a copy of the list that's in
+ * the lock structure and then zero the original. If it's
+ * linked to other such structures, we own that storage.
+ */
+ post = lock->notified; /* a safe copy; we own the lock */
+
+#if defined(DEBUG)
+ memset(&lock->notified, 0, sizeof(_PT_Notified)); /* reset */
+#else
+ lock->notified.length = 0; /* these are really sufficient */
+ lock->notified.link = NULL;
+#endif
+
+ /* should (may) we release lock before notifying? */
+ if (unlock)
+ {
+ rv = pthread_mutex_unlock(&lock->mutex);
+ PR_ASSERT(0 == rv);
+ }
+
+ notified = &post; /* this is where we start */
+ do
+ {
+ for (index = 0; index < notified->length; ++index)
+ {
+ PR_ASSERT(NULL != notified->cv[index].cv);
+ PR_ASSERT(0 != notified->cv[index].times);
+ if (-1 == notified->cv[index].times)
+ {
+ rv = pthread_cond_broadcast(&notified->cv[index].cv->cv);
+ PR_ASSERT(0 == rv);
+ }
+ else
+ {
+ while (notified->cv[index].times-- > 0)
+ {
+ rv = pthread_cond_signal(&notified->cv[index].cv->cv);
+ PR_ASSERT(0 == rv);
+ }
+ }
+ }
+ prev = notified;
+ notified = notified->link;
+ if (&post != prev) PR_DELETE(prev);
+ } while (NULL != notified);
+} /* pt_PostNotifies */
+
+PR_IMPLEMENT(PRLock*) PR_NewLock(void)
+{
+ PRIntn rv;
+ PRLock *lock;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ lock = PR_NEWZAP(PRLock);
+ if (lock != NULL)
+ {
+ pthread_mutexattr_t mattr;
+ rv = PTHREAD_MUTEXATTR_INIT(&mattr);
+ PR_ASSERT(0 == rv);
+ rv = PTHREAD_MUTEX_INIT(lock->mutex, mattr);
+ PR_ASSERT(0 == rv);
+ rv = PTHREAD_MUTEXATTR_DESTROY(&mattr);
+ PR_ASSERT(0 == rv);
+ }
+ return lock;
+} /* PR_NewLock */
+
+PR_IMPLEMENT(void) PR_DestroyLock(PRLock *lock)
+{
+ PRIntn rv;
+ PR_ASSERT(NULL != lock);
+ PR_ASSERT(PTHREAD_THR_HANDLE_IS_ZERO(lock->owner));
+ PR_ASSERT(0 == lock->notified.length);
+ PR_ASSERT(NULL == lock->notified.link);
+ rv = pthread_mutex_destroy(&lock->mutex);
+ PR_ASSERT(0 == rv);
+#if defined(DEBUG)
+ memset(lock, 0xaf, sizeof(PRLock));
+#endif
+ PR_DELETE(lock);
+} /* PR_DestroyLock */
+
+PR_IMPLEMENT(void) PR_Lock(PRLock *lock)
+{
+ PRIntn rv;
+ PR_ASSERT(lock != NULL);
+ rv = pthread_mutex_lock(&lock->mutex);
+ PR_ASSERT(0 == rv);
+ PR_ASSERT(0 == lock->notified.length);
+ PR_ASSERT(NULL == lock->notified.link);
+ PR_ASSERT(PTHREAD_THR_HANDLE_IS_ZERO(lock->owner));
+ PTHREAD_COPY_THR_HANDLE(pthread_self(), lock->owner);
+} /* PR_Lock */
+
+PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock *lock)
+{
+ PRIntn rv;
+
+ PR_ASSERT(lock != NULL);
+ PR_ASSERT(PTHREAD_MUTEX_IS_LOCKED(lock->mutex));
+ PR_ASSERT(pthread_equal(lock->owner, pthread_self()));
+
+ if (!pthread_equal(lock->owner, pthread_self()))
+ return PR_FAILURE;
+
+ PTHREAD_ZERO_THR_HANDLE(lock->owner);
+ if (0 == lock->notified.length) /* shortcut */
+ {
+ rv = pthread_mutex_unlock(&lock->mutex);
+ PR_ASSERT(0 == rv);
+ }
+ else pt_PostNotifies(lock, PR_TRUE);
+
+ return PR_SUCCESS;
+} /* PR_Unlock */
+
+
+/**************************************************************/
+/**************************************************************/
+/***************************CONDITIONS*************************/
+/**************************************************************/
+/**************************************************************/
+
+
+/*
+ * This code is used to compute the absolute time for the wakeup.
+ * It's moderately ugly, so it's defined here and called in a
+ * couple of places.
+ */
+#define PT_NANOPERMICRO 1000UL
+#define PT_BILLION 1000000000UL
+
+static PRIntn pt_TimedWait(
+ pthread_cond_t *cv, pthread_mutex_t *ml, PRIntervalTime timeout)
+{
+ int rv;
+ struct timeval now;
+ struct timespec tmo;
+ PRUint32 ticks = PR_TicksPerSecond();
+
+ tmo.tv_sec = (PRInt32)(timeout / ticks);
+ tmo.tv_nsec = (PRInt32)(timeout - (tmo.tv_sec * ticks));
+ tmo.tv_nsec = (PRInt32)PR_IntervalToMicroseconds(PT_NANOPERMICRO * tmo.tv_nsec);
+
+ /* pthreads wants this in absolute time, off we go ... */
+#if defined(SOLARIS) && defined(_SVID_GETTOD)
+ (void)gettimeofday(&now);
+#else
+ (void)gettimeofday(&now, NULL);
+#endif
+ /* that one's usecs, this one's nsecs - grrrr! */
+ tmo.tv_sec += now.tv_sec;
+ tmo.tv_nsec += (PT_NANOPERMICRO * now.tv_usec);
+ tmo.tv_sec += tmo.tv_nsec / PT_BILLION;
+ tmo.tv_nsec %= PT_BILLION;
+
+ rv = pthread_cond_timedwait(cv, ml, &tmo);
+
+ /* NSPR doesn't report timeouts */
+#ifdef _PR_DCETHREADS
+ return (rv == -1 && errno == EAGAIN) ? 0 : rv;
+#else
+ return (rv == ETIMEDOUT) ? 0 : rv;
+#endif
+} /* pt_TimedWait */
+
+
+/*
+ * Notifies just get posted to the to the protecting mutex. The
+ * actual notification is done when the lock is released so that
+ * MP systems don't contend for a lock that they can't have.
+ */
+static void pt_PostNotifyToCvar(PRCondVar *cvar, PRBool broadcast)
+{
+ PRIntn index = 0;
+ _PT_Notified *notified = &cvar->lock->notified;
+
+ PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self()));
+ PR_ASSERT(PTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex));
+
+ while (1)
+ {
+ for (index = 0; index < notified->length; ++index)
+ {
+ if (notified->cv[index].cv == cvar)
+ {
+ if (broadcast)
+ notified->cv[index].times = -1;
+ else if (-1 != notified->cv[index].times)
+ notified->cv[index].times += 1;
+ goto finished; /* we're finished */
+ }
+ }
+ /* if not full, enter new CV in this array */
+ if (notified->length < PT_CV_NOTIFIED_LENGTH) break;
+
+ /* if there's no link, create an empty array and link it */
+ if (NULL == notified->link)
+ notified->link = PR_NEWZAP(_PT_Notified);
+ notified = notified->link;
+ }
+
+ /* A brand new entry in the array */
+ notified->cv[index].times = (broadcast) ? -1 : 1;
+ notified->cv[index].cv = cvar;
+ notified->length += 1;
+
+finished:
+ PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self()));
+} /* pt_PostNotifyToCvar */
+
+PR_IMPLEMENT(PRCondVar*) PR_NewCondVar(PRLock *lock)
+{
+ PRCondVar *cv = PR_NEW(PRCondVar);
+ PR_ASSERT(lock != NULL);
+ if (cv != NULL)
+ {
+ int rv = PTHREAD_COND_INIT(cv->cv, _pt_cvar_attr);
+ PR_ASSERT(0 == rv);
+ cv->lock = lock;
+ }
+ return cv;
+} /* PR_NewCondVar */
+
+PR_IMPLEMENT(void) PR_DestroyCondVar(PRCondVar *cvar)
+{
+ int rv;
+ rv = pthread_cond_destroy(&cvar->cv); PR_ASSERT(0 == rv);
+#if defined(DEBUG)
+ memset(cvar, 0xaf, sizeof(PRCondVar));
+#endif
+ PR_DELETE(cvar);
+} /* PR_DestroyCondVar */
+
+PR_IMPLEMENT(PRStatus) PR_WaitCondVar(PRCondVar *cvar, PRIntervalTime timeout)
+{
+ PRIntn rv;
+ PRThread *thred = PR_CurrentThread();
+
+ PR_ASSERT(cvar != NULL);
+ /* We'd better be locked */
+ PR_ASSERT(PTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex));
+ /* and it better be by us */
+ PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self()));
+
+ if (thred->state & PT_THREAD_ABORTED) goto aborted;
+
+ /*
+ * The thread waiting is used for PR_Interrupt
+ */
+ thred->waiting = cvar; /* this is where we're waiting */
+
+ /*
+ * If we have pending notifies, post them now.
+ *
+ * This is not optimal. We're going to post these notifies
+ * while we're holding the lock. That means on MP systems
+ * that they are going to collide for the lock that we will
+ * hold until we actually wait.
+ */
+ if (0 != cvar->lock->notified.length)
+ pt_PostNotifies(cvar->lock, PR_FALSE);
+
+ /*
+ * We're surrendering the lock, so clear out the owner field.
+ */
+ PTHREAD_ZERO_THR_HANDLE(cvar->lock->owner);
+
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ rv = pthread_cond_wait(&cvar->cv, &cvar->lock->mutex);
+ else
+ rv = pt_TimedWait(&cvar->cv, &cvar->lock->mutex, timeout);
+
+ /* We just got the lock back - this better be empty */
+ PR_ASSERT(PTHREAD_THR_HANDLE_IS_ZERO(cvar->lock->owner));
+ PTHREAD_COPY_THR_HANDLE(pthread_self(), cvar->lock->owner);
+
+ PR_ASSERT(0 == cvar->lock->notified.length);
+ thred->waiting = NULL; /* and now we're not */
+ if (thred->state & PT_THREAD_ABORTED) goto aborted;
+ return (rv == 0) ? PR_SUCCESS : PR_FAILURE;
+
+aborted:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ thred->state &= ~PT_THREAD_ABORTED;
+ return PR_FAILURE;
+} /* PR_WaitCondVar */
+
+PR_IMPLEMENT(PRStatus) PR_NotifyCondVar(PRCondVar *cvar)
+{
+ PR_ASSERT(cvar != NULL);
+ pt_PostNotifyToCvar(cvar, PR_FALSE);
+ return PR_SUCCESS;
+} /* PR_NotifyCondVar */
+
+PR_IMPLEMENT(PRStatus) PR_NotifyAllCondVar(PRCondVar *cvar)
+{
+ PR_ASSERT(cvar != NULL);
+ pt_PostNotifyToCvar(cvar, PR_TRUE);
+ return PR_SUCCESS;
+} /* PR_NotifyAllCondVar */
+
+/**************************************************************/
+/**************************************************************/
+/***************************MONITORS***************************/
+/**************************************************************/
+/**************************************************************/
+
+PR_IMPLEMENT(PRMonitor*) PR_NewMonitor(void)
+{
+ PRMonitor *ml;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ ml = PR_NEWZAP(PRMonitor);
+ if (ml != NULL)
+ {
+ int rv;
+ pthread_mutexattr_t mattr;
+ rv = PTHREAD_MUTEXATTR_INIT(&mattr);
+ PR_ASSERT(0 == rv);
+ rv += PTHREAD_MUTEX_INIT(ml->lock.mutex, mattr);
+ PR_ASSERT(0 == rv);
+ rv += PTHREAD_MUTEXATTR_DESTROY(&mattr);
+ PR_ASSERT(0 == rv);
+
+ rv += PTHREAD_COND_INIT(ml->cvar.cv, _pt_cvar_attr);
+ PR_ASSERT(0 == rv);
+ ml->entryCount = 0;
+ ml->cvar.lock = &ml->lock;
+ if (0 != rv)
+ {
+ PR_DELETE(ml);
+ ml = NULL;
+ }
+ }
+ return ml;
+} /* PR_NewMonitor */
+
+PR_IMPLEMENT(PRMonitor*) PR_NewNamedMonitor(const char* name)
+{
+ PRMonitor* mon = PR_NewMonitor();
+ mon->name = name;
+ return mon;
+}
+
+PR_IMPLEMENT(void) PR_DestroyMonitor(PRMonitor *mon)
+{
+ int rv;
+ PR_ASSERT(mon != NULL);
+ rv = pthread_cond_destroy(&mon->cvar.cv); PR_ASSERT(0 == rv);
+ rv = pthread_mutex_destroy(&mon->lock.mutex); PR_ASSERT(0 == rv);
+#if defined(DEBUG)
+ memset(mon, 0xaf, sizeof(PRMonitor));
+#endif
+ PR_DELETE(mon);
+} /* PR_DestroyMonitor */
+
+
+/* The GC uses this; it is quite arguably a bad interface. I'm just
+ * duplicating it for now - XXXMB
+ */
+PR_IMPLEMENT(PRInt32) PR_GetMonitorEntryCount(PRMonitor *mon)
+{
+ pthread_t self = pthread_self();
+ if (pthread_equal(mon->owner, self))
+ return mon->entryCount;
+ return 0;
+}
+
+PR_IMPLEMENT(void) PR_EnterMonitor(PRMonitor *mon)
+{
+ int rv;
+ pthread_t self = pthread_self();
+
+ PR_ASSERT(mon != NULL);
+ rv = pthread_mutex_trylock(&mon->lock.mutex);
+#ifdef _PR_DCETHREADS
+ if (1 == rv)
+#else
+ if (0 == rv)
+#endif
+ {
+ /* I now have the lock - I can play in the sandbox */
+ /* could/should/would not have gotten lock if entries != 0 */
+ PR_ASSERT(0 == mon->entryCount);
+ PR_ASSERT(PTHREAD_THR_HANDLE_IS_ZERO(mon->lock.owner));
+ PTHREAD_COPY_THR_HANDLE(pthread_self(), mon->lock.owner);
+ PTHREAD_COPY_THR_HANDLE(self, mon->owner);
+ }
+ else
+ {
+ PR_ASSERT(PT_TRYLOCK_BUSY == rv); /* and if it isn't? */
+ /* somebody has it locked - is it me? */
+ if (!pthread_equal(mon->owner, self))
+ {
+ /* it's not me - this should block */
+ PR_Lock(&mon->lock);
+ /* and now I have the lock */
+ PR_ASSERT(0 == mon->entryCount);
+ PTHREAD_COPY_THR_HANDLE(self, mon->owner);
+ }
+ }
+ mon->entryCount += 1;
+} /* PR_EnterMonitor */
+
+PR_IMPLEMENT(PRStatus) PR_ExitMonitor(PRMonitor *mon)
+{
+ pthread_t self = pthread_self();
+
+ PR_ASSERT(mon != NULL);
+ /* The lock better be that - locked */
+ PR_ASSERT(PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex));
+ /* we'd better be the owner */
+ PR_ASSERT(pthread_equal(mon->owner, self));
+ if (!pthread_equal(mon->owner, self))
+ return PR_FAILURE;
+
+ /* if it's locked and we have it, then the entries should be > 0 */
+ PR_ASSERT(mon->entryCount > 0);
+ mon->entryCount -= 1; /* reduce by one */
+ if (mon->entryCount == 0)
+ {
+ /* and if it transitioned to zero - unlock */
+ PTHREAD_ZERO_THR_HANDLE(mon->owner); /* make the owner unknown */
+ PR_Unlock(&mon->lock);
+ }
+ return PR_SUCCESS;
+} /* PR_ExitMonitor */
+
+PR_IMPLEMENT(PRStatus) PR_Wait(PRMonitor *mon, PRIntervalTime timeout)
+{
+ PRStatus rv;
+ PRInt16 saved_entries;
+ pthread_t saved_owner;
+
+ PR_ASSERT(mon != NULL);
+ /* we'd better be locked */
+ PR_ASSERT(PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex));
+ /* and the entries better be positive */
+ PR_ASSERT(mon->entryCount > 0);
+ /* and it better be by us */
+ PR_ASSERT(pthread_equal(mon->owner, pthread_self()));
+
+ /* tuck these away 'till later */
+ saved_entries = mon->entryCount;
+ mon->entryCount = 0;
+ PTHREAD_COPY_THR_HANDLE(mon->owner, saved_owner);
+ PTHREAD_ZERO_THR_HANDLE(mon->owner);
+
+ rv = PR_WaitCondVar(&mon->cvar, timeout);
+
+ /* reinstate the intresting information */
+ mon->entryCount = saved_entries;
+ PTHREAD_COPY_THR_HANDLE(saved_owner, mon->owner);
+
+ return rv;
+} /* PR_Wait */
+
+PR_IMPLEMENT(PRStatus) PR_Notify(PRMonitor *mon)
+{
+ PR_ASSERT(NULL != mon);
+ /* we'd better be locked */
+ PR_ASSERT(PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex));
+ /* and the entries better be positive */
+ PR_ASSERT(mon->entryCount > 0);
+ /* and it better be by us */
+ PR_ASSERT(pthread_equal(mon->owner, pthread_self()));
+
+ pt_PostNotifyToCvar(&mon->cvar, PR_FALSE);
+
+ return PR_SUCCESS;
+} /* PR_Notify */
+
+PR_IMPLEMENT(PRStatus) PR_NotifyAll(PRMonitor *mon)
+{
+ PR_ASSERT(mon != NULL);
+ /* we'd better be locked */
+ PR_ASSERT(PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex));
+ /* and the entries better be positive */
+ PR_ASSERT(mon->entryCount > 0);
+ /* and it better be by us */
+ PR_ASSERT(pthread_equal(mon->owner, pthread_self()));
+
+ pt_PostNotifyToCvar(&mon->cvar, PR_TRUE);
+
+ return PR_SUCCESS;
+} /* PR_NotifyAll */
+
+/**************************************************************/
+/**************************************************************/
+/**************************SEMAPHORES**************************/
+/**************************************************************/
+/**************************************************************/
+PR_IMPLEMENT(void) PR_PostSem(PRSemaphore *semaphore)
+{
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) unwarned = _PR_Obsolete(
+ "PR_PostSem", "locks & condition variables");
+ PR_Lock(semaphore->cvar->lock);
+ PR_NotifyCondVar(semaphore->cvar);
+ semaphore->count += 1;
+ PR_Unlock(semaphore->cvar->lock);
+} /* PR_PostSem */
+
+PR_IMPLEMENT(PRStatus) PR_WaitSem(PRSemaphore *semaphore)
+{
+ PRStatus status = PR_SUCCESS;
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) unwarned = _PR_Obsolete(
+ "PR_WaitSem", "locks & condition variables");
+ PR_Lock(semaphore->cvar->lock);
+ while ((semaphore->count == 0) && (PR_SUCCESS == status))
+ status = PR_WaitCondVar(semaphore->cvar, PR_INTERVAL_NO_TIMEOUT);
+ if (PR_SUCCESS == status) semaphore->count -= 1;
+ PR_Unlock(semaphore->cvar->lock);
+ return status;
+} /* PR_WaitSem */
+
+PR_IMPLEMENT(void) PR_DestroySem(PRSemaphore *semaphore)
+{
+ static PRBool unwarned = PR_TRUE;
+ if (unwarned) unwarned = _PR_Obsolete(
+ "PR_DestroySem", "locks & condition variables");
+ PR_DestroyLock(semaphore->cvar->lock);
+ PR_DestroyCondVar(semaphore->cvar);
+ PR_DELETE(semaphore);
+} /* PR_DestroySem */
+
+PR_IMPLEMENT(PRSemaphore*) PR_NewSem(PRUintn value)
+{
+ PRSemaphore *semaphore;
+ static PRBool unwarned = PR_TRUE;
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ if (unwarned) unwarned = _PR_Obsolete(
+ "PR_NewSem", "locks & condition variables");
+
+ semaphore = PR_NEWZAP(PRSemaphore);
+ if (NULL != semaphore)
+ {
+ PRLock *lock = PR_NewLock();
+ if (NULL != lock)
+ {
+ semaphore->cvar = PR_NewCondVar(lock);
+ if (NULL != semaphore->cvar)
+ {
+ semaphore->count = value;
+ return semaphore;
+ }
+ PR_DestroyLock(lock);
+ }
+ PR_DELETE(semaphore);
+ }
+ return NULL;
+}
+
+/**************************************************************/
+/**************************************************************/
+/******************ROUTINES FOR DCE EMULATION******************/
+/**************************************************************/
+/**************************************************************/
+
+#include "prpdce.h"
+
+PR_IMPLEMENT(PRStatus) PRP_TryLock(PRLock *lock)
+{
+ PRIntn rv = pthread_mutex_trylock(&lock->mutex);
+ if (rv == PT_TRYLOCK_SUCCESS)
+ {
+ PR_ASSERT(PTHREAD_THR_HANDLE_IS_ZERO(lock->owner));
+ PTHREAD_COPY_THR_HANDLE(pthread_self(), lock->owner);
+ }
+ else
+ PR_ASSERT(!PTHREAD_THR_HANDLE_IS_ZERO(lock->owner));
+ /* XXX set error code? */
+ return (PT_TRYLOCK_SUCCESS == rv) ? PR_SUCCESS : PR_FAILURE;
+} /* PRP_TryLock */
+
+PR_IMPLEMENT(PRCondVar*) PRP_NewNakedCondVar()
+{
+ PRCondVar *cv;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ cv = PR_NEW(PRCondVar);
+ if (cv != NULL)
+ {
+ int rv;
+ rv = PTHREAD_COND_INIT(cv->cv, _pt_cvar_attr);
+ PR_ASSERT(0 == rv);
+ cv->lock = _PR_NAKED_CV_LOCK;
+ }
+ return cv;
+} /* PRP_NewNakedCondVar */
+
+PR_IMPLEMENT(void) PRP_DestroyNakedCondVar(PRCondVar *cvar)
+{
+ int rv;
+ rv = pthread_cond_destroy(&cvar->cv); PR_ASSERT(0 == rv);
+#if defined(DEBUG)
+ memset(cvar, 0xaf, sizeof(PRCondVar));
+#endif
+ PR_DELETE(cvar);
+} /* PRP_DestroyNakedCondVar */
+
+PR_IMPLEMENT(PRStatus) PRP_NakedWait(
+ PRCondVar *cvar, PRLock *ml, PRIntervalTime timeout)
+{
+ PRIntn rv;
+ PR_ASSERT(cvar != NULL);
+ /* XXX do we really want to assert this in a naked wait? */
+ PR_ASSERT(PTHREAD_MUTEX_IS_LOCKED(ml->mutex));
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ rv = pthread_cond_wait(&cvar->cv, &ml->mutex);
+ else
+ rv = pt_TimedWait(&cvar->cv, &ml->mutex, timeout);
+ return (rv == 0) ? PR_SUCCESS : PR_FAILURE;
+} /* PRP_NakedWait */
+
+PR_IMPLEMENT(PRStatus) PRP_NakedNotify(PRCondVar *cvar)
+{
+ int rv;
+ PR_ASSERT(cvar != NULL);
+ rv = pthread_cond_signal(&cvar->cv);
+ PR_ASSERT(0 == rv);
+ return PR_SUCCESS;
+} /* PRP_NakedNotify */
+
+PR_IMPLEMENT(PRStatus) PRP_NakedBroadcast(PRCondVar *cvar)
+{
+ int rv;
+ PR_ASSERT(cvar != NULL);
+ rv = pthread_cond_broadcast(&cvar->cv);
+ PR_ASSERT(0 == rv);
+ return PR_SUCCESS;
+} /* PRP_NakedBroadcast */
+
+#endif /* defined(_PR_PTHREADS) */
+
+/* ptsynch.c */
diff --git a/pr/src/pthreads/ptthread.c b/pr/src/pthreads/ptthread.c
new file mode 100644
index 00000000..784ffb29
--- /dev/null
+++ b/pr/src/pthreads/ptthread.c
@@ -0,0 +1,1309 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+** File: ptthread.c
+** Descritpion: Implemenation for threds using pthreds
+** Exports: ptthread.h
+*/
+
+#if defined(_PR_PTHREADS) || defined(_PR_DCETHREADS)
+
+#include "prlog.h"
+#include "primpl.h"
+#include "prpdce.h"
+
+#include <pthread.h>
+#include <string.h>
+#include <signal.h>
+
+/*
+ * Record whether or not we have the privilege to set the scheduling
+ * policy and priority of threads. 0 means that privilege is available.
+ * EPERM means that privilege is not available.
+ */
+
+PRIntn pt_schedpriv;
+
+struct _PT_Bookeeping pt_book = {0};
+
+static void init_pthread_gc_support(void);
+
+static PRIntn pt_PriorityMap(PRThreadPriority pri)
+{
+ return pt_book.minPrio +
+ pri * (pt_book.maxPrio - pt_book.minPrio) / PR_PRIORITY_LAST;
+}
+
+/*
+** Initialize a stack for a native pthread thread
+*/
+static void _PR_InitializeStack(PRThreadStack *ts)
+{
+ if( ts && (ts->stackTop == 0) ) {
+ ts->allocBase = (char *) &ts;
+ ts->allocSize = ts->stackSize;
+
+ /*
+ ** Setup stackTop and stackBottom values.
+ */
+#ifdef HAVE_STACK_GROWING_UP
+ ts->stackBottom = ts->allocBase + ts->stackSize;
+ ts->stackTop = ts->allocBase;
+#else
+ ts->stackTop = ts->allocBase;
+ ts->stackBottom = ts->allocBase - ts->stackSize;
+#endif
+ }
+}
+
+static void *_pt_root(void *arg)
+{
+ PRIntn rv;
+ PRThread *thred = (PRThread*)arg;
+ PRBool detached = (thred->state & PT_THREAD_DETACHED) ? PR_TRUE : PR_FALSE;
+
+ /*
+ * Both the parent thread and this new thread set thred->id.
+ * The new thread must ensure that thred->id is set before
+ * it executes its startFunc. The parent thread must ensure
+ * that thred->id is set before PR_CreateThread() returns.
+ * Both threads set thred->id without holding a lock. Since
+ * they are writing the same value, this unprotected double
+ * write should be safe.
+ */
+ thred->id = pthread_self();
+
+ /*
+ ** DCE Threads can't detach during creation, so do it late.
+ ** I would like to do it only here, but that doesn't seem
+ ** to work.
+ */
+#if defined(_PR_DCETHREADS)
+ if (detached)
+ {
+ /* pthread_detach() modifies its argument, so we must pass a copy */
+ pthread_t self = thred->id;
+ rv = pthread_detach(&self);
+ PR_ASSERT(0 == rv);
+ }
+#endif /* defined(_PR_DCETHREADS) */
+
+ /* Set up the thread stack information */
+ _PR_InitializeStack(thred->stack);
+
+ /*
+ * Set within the current thread the pointer to our object.
+ * This object will be deleted when the thread termintates,
+ * whether in a join or detached (see _PR_InitThreads()).
+ */
+ rv = pthread_setspecific(pt_book.key, thred);
+ PR_ASSERT(0 == rv);
+
+ /* make the thread visible to the rest of the runtime */
+ PR_Lock(pt_book.ml);
+
+ /* If this is a GCABLE thread, set its state appropriately */
+ if (thred->suspend & PT_THREAD_SETGCABLE)
+ thred->state |= PT_THREAD_GCABLE;
+ thred->suspend = 0;
+
+ thred->prev = pt_book.last;
+ pt_book.last->next = thred;
+ thred->next = NULL;
+ pt_book.last = thred;
+ PR_Unlock(pt_book.ml);
+
+ thred->startFunc(thred->arg); /* make visible to the client */
+
+ /* unhook the thread from the runtime */
+ PR_Lock(pt_book.ml);
+ if (thred->state & PT_THREAD_SYSTEM)
+ pt_book.system -= 1;
+ else if (--pt_book.user == pt_book.this_many)
+ PR_NotifyAllCondVar(pt_book.cv);
+ thred->prev->next = thred->next;
+ if (NULL == thred->next)
+ pt_book.last = thred->prev;
+ else
+ thred->next->prev = thred->prev;
+
+ /*
+ * At this moment, PR_CreateThread() may not have set thred->id yet.
+ * It is safe for a detached thread to free thred only after
+ * PR_CreateThread() has set thred->id.
+ */
+ if (detached)
+ {
+ while (!thred->okToDelete)
+ PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(pt_book.ml);
+
+ /* last chance to delete this puppy if the thread is detached */
+ if (detached)
+ {
+ PR_DELETE(thred->stack);
+#if defined(DEBUG)
+ memset(thred, 0xaf, sizeof(PRThread));
+#endif
+ PR_DELETE(thred);
+ }
+
+ rv = pthread_setspecific(pt_book.key, NULL);
+ PR_ASSERT(0 == rv);
+ return NULL;
+} /* _pt_root */
+
+static PRThread* _PR_CreateThread(
+ PRThreadType type, void (*start)(void *arg),
+ void *arg, PRThreadPriority priority, PRThreadScope scope,
+ PRThreadState state, PRUint32 stackSize, PRBool isGCAble)
+{
+ int rv;
+ PRThread *thred;
+ pthread_attr_t tattr;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)priority)
+ priority = PR_PRIORITY_FIRST;
+ else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)priority)
+ priority = PR_PRIORITY_LAST;
+
+ rv = PTHREAD_ATTR_INIT(&tattr);
+ PR_ASSERT(0 == rv);
+
+ if (EPERM != pt_schedpriv)
+ {
+#if !defined(_PR_DCETHREADS) && !defined(FREEBSD)
+ struct sched_param schedule;
+#endif
+
+#if !defined(FREEBSD)
+ rv = pthread_attr_setinheritsched(&tattr, PTHREAD_EXPLICIT_SCHED);
+ PR_ASSERT(0 == rv);
+#endif
+
+ /* Use the default scheduling policy */
+
+#if defined(_PR_DCETHREADS)
+ rv = pthread_attr_setprio(&tattr, pt_PriorityMap(priority));
+ PR_ASSERT(0 == rv);
+#elif !defined(FREEBSD)
+ rv = pthread_attr_getschedparam(&tattr, &schedule);
+ PR_ASSERT(0 == rv);
+ schedule.sched_priority = pt_PriorityMap(priority);
+ rv = pthread_attr_setschedparam(&tattr, &schedule);
+ PR_ASSERT(0 == rv);
+#endif /* !defined(_PR_DCETHREADS) */
+ }
+
+ /*
+ * DCE threads can't set detach state before creating the thread.
+ * AIX can't set detach late. Why can't we all just get along?
+ */
+#if !defined(_PR_DCETHREADS)
+ rv = pthread_attr_setdetachstate(&tattr,
+ ((PR_JOINABLE_THREAD == state) ?
+ PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED));
+ PR_ASSERT(0 == rv);
+#endif /* !defined(_PR_DCETHREADS) */
+
+ /*
+ * HPUX only supports PTHREAD_SCOPE_SYSTEM.
+ * IRIX only supports PTHREAD_SCOPE_PROCESS.
+ * OSF1 only supports PTHREAD_SCOPE_PROCESS.
+ * AIX only supports PTHREAD_SCOPE_SYSTEM.
+ */
+#if defined(SOLARIS)
+ rv = pthread_attr_setscope(&tattr,
+ ((PR_GLOBAL_THREAD == scope) ?
+ PTHREAD_SCOPE_SYSTEM : PTHREAD_SCOPE_PROCESS));
+ PR_ASSERT(0 == rv);
+#endif
+
+#if defined(IRIX)
+ if ((16 * 1024) > stackSize) stackSize = (16 * 1024); /* IRIX minimum */
+ else
+#endif
+ if (0 == stackSize) stackSize = (64 * 1024); /* default == 64K */
+ /*
+ * Linux doesn't have pthread_attr_setstacksize.
+ */
+#ifndef LINUX
+ rv = pthread_attr_setstacksize(&tattr, stackSize);
+ PR_ASSERT(0 == rv);
+#endif
+
+ thred = PR_NEWZAP(PRThread);
+ if (thred != NULL)
+ {
+ pthread_t id;
+
+ thred->arg = arg;
+ thred->startFunc = start;
+ thred->priority = priority;
+ if (PR_UNJOINABLE_THREAD == state)
+ thred->state |= PT_THREAD_DETACHED;
+ if (PR_GLOBAL_THREAD == scope)
+ thred->state |= PT_THREAD_GLOBAL;
+ if (PR_SYSTEM_THREAD == type)
+ thred->state |= PT_THREAD_SYSTEM;
+
+ thred->suspend =(isGCAble) ? PT_THREAD_SETGCABLE : 0;
+
+ thred->stack = PR_NEWZAP(PRThreadStack);
+ if (thred->stack == NULL) {
+ PR_DELETE(thred); /* all that work ... poof! */
+ thred = NULL; /* and for what? */
+ goto done;
+ }
+ thred->stack->stackSize = stackSize;
+ thred->stack->thr = thred;
+
+#ifdef PT_NO_SIGTIMEDWAIT
+ pthread_mutex_init(&thred->suspendResumeMutex,NULL);
+ pthread_cond_init(&thred->suspendResumeCV,NULL);
+#endif
+
+ /* make the thread counted to the rest of the runtime */
+ PR_Lock(pt_book.ml);
+ if (thred->state & PT_THREAD_SYSTEM)
+ pt_book.system += 1;
+ else pt_book.user += 1;
+ PR_Unlock(pt_book.ml);
+
+ /*
+ * We pass a pointer to a local copy (instead of thred->id)
+ * to pthread_create() because who knows what wacky things
+ * pthread_create() may be doing to its argument.
+ */
+ if (PTHREAD_CREATE(&id, tattr, _pt_root, thred) != 0)
+ {
+ PR_Lock(pt_book.ml);
+ if (thred->state & PT_THREAD_SYSTEM)
+ pt_book.system -= 1;
+ else if (--pt_book.user == pt_book.this_many)
+ PR_NotifyAllCondVar(pt_book.cv);
+ PR_Unlock(pt_book.ml);
+
+ PR_DELETE(thred->stack);
+ PR_DELETE(thred); /* all that work ... poof! */
+ thred = NULL; /* and for what? */
+ goto done;
+ }
+
+ /*
+ * Both the parent thread and this new thread set thred->id.
+ * The parent thread must ensure that thred->id is set before
+ * PR_CreateThread() returns. (See comments in _pt_root().)
+ */
+ thred->id = id;
+
+ /*
+ * If the new thread is detached, tell it that PR_CreateThread()
+ * has set thred->id so it's ok to delete thred.
+ */
+ if (PR_UNJOINABLE_THREAD == state)
+ {
+ PR_Lock(pt_book.ml);
+ thred->okToDelete = PR_TRUE;
+ PR_NotifyAllCondVar(pt_book.cv);
+ PR_Unlock(pt_book.ml);
+ }
+ }
+
+done:
+ rv = PTHREAD_ATTR_DESTROY(&tattr);
+ PR_ASSERT(0 == rv);
+
+ return thred;
+} /* _PR_CreateThread */
+
+PR_IMPLEMENT(PRThread*) PR_CreateThread(
+ PRThreadType type, void (*start)(void *arg), void *arg,
+ PRThreadPriority priority, PRThreadScope scope,
+ PRThreadState state, PRUint32 stackSize)
+{
+ return _PR_CreateThread(
+ type, start, arg, priority, scope, state, stackSize, PR_FALSE);
+} /* PR_CreateThread */
+
+PR_IMPLEMENT(PRThread*) PR_CreateThreadGCAble(
+ PRThreadType type, void (*start)(void *arg), void *arg,
+ PRThreadPriority priority, PRThreadScope scope,
+ PRThreadState state, PRUint32 stackSize)
+{
+ return _PR_CreateThread(
+ type, start, arg, priority, scope, state, stackSize, PR_TRUE);
+} /* PR_CreateThreadGCAble */
+
+PR_IMPLEMENT(void*) GetExecutionEnvironment(PRThread *thred)
+{
+ return thred->environment;
+} /* GetExecutionEnvironment */
+
+PR_IMPLEMENT(void) SetExecutionEnvironment(PRThread *thred, void *env)
+{
+ thred->environment = env;
+} /* SetExecutionEnvironment */
+
+PR_IMPLEMENT(PRThread*) PR_AttachThread(
+ PRThreadType type, PRThreadPriority priority, PRThreadStack *stack)
+{
+ PRThread *thred = NULL;
+ void *privateData = NULL;
+
+ /*
+ * NSPR must have been initialized when PR_AttachThread is called.
+ * We cannot have PR_AttachThread call implicit initialization
+ * because if multiple threads call PR_AttachThread simultaneously,
+ * NSPR may be initialized more than once.
+ */
+ if (!_pr_initialized) {
+ /* Since NSPR is not initialized, we cannot call PR_SetError. */
+ return NULL;
+ }
+
+ PTHREAD_GETSPECIFIC(pt_book.key, privateData);
+ if (NULL != privateData)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+ }
+ thred = PR_NEWZAP(PRThread);
+ if (NULL == thred) PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ else
+ {
+ int rv;
+
+ if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)priority)
+ priority = PR_PRIORITY_FIRST;
+ else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)priority)
+ priority = PR_PRIORITY_LAST;
+
+ thred->priority = priority;
+ thred->id = pthread_self();
+ rv = pthread_setspecific(pt_book.key, thred);
+ PR_ASSERT(0 == rv);
+
+ PR_Lock(pt_book.ml);
+ if (PR_SYSTEM_THREAD == type)
+ {
+ pt_book.system += 1;
+ thred->state |= PT_THREAD_SYSTEM;
+ }
+ else pt_book.user += 1;
+
+ /* then put it into the list */
+ thred->prev = pt_book.last;
+ pt_book.last->next = thred;
+ thred->next = NULL;
+ pt_book.last = thred;
+ PR_Unlock(pt_book.ml);
+
+ }
+ return thred; /* may be NULL */
+} /* PR_AttachThread */
+
+PR_IMPLEMENT(PRStatus) PR_JoinThread(PRThread *thred)
+{
+ int rv = -1;
+ void *result = NULL;
+ PR_ASSERT(thred != NULL);
+
+ if ((0xafafafaf == thred->state)
+ || (PT_THREAD_DETACHED & thred->state))
+ {
+ /*
+ * This might be a bad address, but if it isn't, the state should
+ * either be an unjoinable thread or it's already had the object
+ * deleted. However, the client that called join on a detached
+ * thread deserves all the rath I can muster....
+ */
+ PR_SetError(PR_ILLEGAL_ACCESS_ERROR, 0);
+ PR_LogPrint(
+ "PR_JoinThread: 0x%X not joinable | already smashed\n", thred);
+
+ PR_ASSERT((0xafafafaf == thred->state)
+ || (PT_THREAD_DETACHED & thred->state));
+ }
+ else
+ {
+ pthread_t id = thred->id;
+ rv = pthread_join(id, &result);
+ if (0 != rv)
+ PR_SetError(PR_UNKNOWN_ERROR, errno);
+ PR_DELETE(thred->stack);
+ memset(thred, 0xaf, sizeof(PRThread));
+ PR_ASSERT(result == NULL);
+ PR_DELETE(thred);
+ }
+ return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
+} /* PR_JoinThread */
+
+PR_IMPLEMENT(void) PR_DetachThread()
+{
+ void *tmp;
+ PTHREAD_GETSPECIFIC(pt_book.key, tmp);
+ PR_ASSERT(NULL != tmp);
+
+ if (NULL != tmp)
+ {
+ int rv;
+ PRThread *thred = (PRThread*)tmp;
+
+ PR_Lock(pt_book.ml);
+ if (thred->state & PT_THREAD_SYSTEM)
+ pt_book.system -= 1;
+ else if (--pt_book.user == pt_book.this_many)
+ PR_NotifyAllCondVar(pt_book.cv);
+ thred->prev->next = thred->next;
+ if (NULL == thred->next)
+ pt_book.last = thred->prev;
+ else
+ thred->next->prev = thred->prev;
+ PR_Unlock(pt_book.ml);
+
+ rv = pthread_setspecific(pt_book.key, NULL);
+ PR_ASSERT(0 == rv);
+ PR_DELETE(thred->stack);
+ memset(thred, 0xaf, sizeof(PRThread));
+ PR_DELETE(thred);
+ }
+} /* PR_DetachThread */
+
+PR_IMPLEMENT(PRThread*) PR_GetCurrentThread()
+{
+ void *thred;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ PTHREAD_GETSPECIFIC(pt_book.key, thred);
+ PR_ASSERT(NULL != thred);
+ return (PRThread*)thred;
+} /* PR_GetCurrentThread */
+
+PR_IMPLEMENT(PRThreadScope) PR_GetThreadScope(const PRThread *thred)
+{
+ return (thred->state & PT_THREAD_GLOBAL) ?
+ PR_GLOBAL_THREAD : PR_LOCAL_THREAD;
+} /* PR_GetThreadScope() */
+
+PR_IMPLEMENT(PRThreadType) PR_GetThreadType(const PRThread *thred)
+{
+ return (thred->state & PT_THREAD_SYSTEM) ?
+ PR_SYSTEM_THREAD : PR_USER_THREAD;
+}
+
+PR_IMPLEMENT(PRThreadState) PR_GetThreadState(const PRThread *thred)
+{
+ return (thred->state & PT_THREAD_DETACHED) ?
+ PR_UNJOINABLE_THREAD : PR_JOINABLE_THREAD;
+} /* PR_GetThreadState */
+
+PR_IMPLEMENT(PRThreadPriority) PR_GetThreadPriority(const PRThread *thred)
+{
+ PR_ASSERT(thred != NULL);
+ return thred->priority;
+} /* PR_GetThreadPriority */
+
+PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thred, PRThreadPriority newPri)
+{
+ PRIntn rv;
+
+ PR_ASSERT(NULL != thred);
+
+ if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)newPri)
+ newPri = PR_PRIORITY_FIRST;
+ else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)newPri)
+ newPri = PR_PRIORITY_LAST;
+
+#if defined(_PR_DCETHREADS)
+ rv = pthread_setprio(thred->id, pt_PriorityMap(newPri));
+ /* pthread_setprio returns the old priority */
+ PR_ASSERT(-1 != rv);
+#elif !defined(FREEBSD)
+ if (EPERM != pt_schedpriv)
+ {
+ int policy;
+ struct sched_param schedule;
+
+ rv = pthread_getschedparam(thred->id, &policy, &schedule);
+ PR_ASSERT(0 == rv);
+ schedule.sched_priority = pt_PriorityMap(newPri);
+ rv = pthread_setschedparam(thred->id, policy, &schedule);
+ PR_ASSERT(0 == rv);
+ }
+#endif
+
+ thred->priority = newPri;
+} /* PR_SetThreadPriority */
+
+PR_IMPLEMENT(PRStatus) PR_NewThreadPrivateIndex(
+ PRUintn *newIndex, PRThreadPrivateDTOR destructor)
+{
+ int rv;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ rv = PTHREAD_KEY_CREATE((pthread_key_t*)newIndex, destructor);
+
+ if (0 == rv)
+ {
+ PR_Lock(pt_book.ml);
+ if (*newIndex >= pt_book.highwater)
+ pt_book.highwater = *newIndex + 1;
+ PR_Unlock(pt_book.ml);
+ return PR_SUCCESS;
+ }
+ PR_SetError(PR_UNKNOWN_ERROR, rv);
+ return PR_FAILURE;
+} /* PR_NewThreadPrivateIndex */
+
+PR_IMPLEMENT(PRStatus) PR_SetThreadPrivate(PRUintn index, void *priv)
+{
+ PRIntn rv;
+ if ((pthread_key_t)index >= pt_book.highwater)
+ {
+ PR_SetError(PR_TPD_RANGE_ERROR, 0);
+ return PR_FAILURE;
+ }
+ rv = pthread_setspecific((pthread_key_t)index, priv);
+ PR_ASSERT(0 == rv);
+ if (0 == rv) return PR_SUCCESS;
+
+ PR_SetError(PR_UNKNOWN_ERROR, rv);
+ return PR_FAILURE;
+} /* PR_SetThreadPrivate */
+
+PR_IMPLEMENT(void*) PR_GetThreadPrivate(PRUintn index)
+{
+ void *result = NULL;
+ if ((pthread_key_t)index < pt_book.highwater)
+ PTHREAD_GETSPECIFIC((pthread_key_t)index, result);
+ return result;
+} /* PR_GetThreadPrivate */
+
+PR_IMPLEMENT(PRStatus) PR_Interrupt(PRThread *thred)
+{
+ /*
+ ** If the target thread indicates that it's waiting,
+ ** find the condition and broadcast to it. Broadcast
+ ** since we don't know which thread (if there are more
+ ** than one). This sounds risky, but clients must
+ ** test their invariants when resumed from a wait and
+ ** I don't expect very many threads to be waiting on
+ ** a single condition and I don't expect interrupt to
+ ** be used very often.
+ */
+ PRCondVar *victim;
+ PR_ASSERT(thred != NULL);
+ thred->state |= PT_THREAD_ABORTED;
+ victim = thred->waiting;
+ if (NULL != victim)
+ {
+ PRIntn haveLock = pthread_equal(victim->lock->owner, pthread_self());
+ if (!haveLock) PR_Lock(victim->lock);
+ PR_NotifyAllCondVar(victim);
+ if (!haveLock) PR_Unlock(victim->lock);
+ }
+ return PR_SUCCESS;
+} /* PR_Interrupt */
+
+PR_IMPLEMENT(void) PR_ClearInterrupt()
+{
+ PRThread *me = PR_CurrentThread();
+ me->state &= ~PT_THREAD_ABORTED;
+} /* PR_ClearInterrupt */
+
+PR_IMPLEMENT(PRStatus) PR_Yield()
+{
+ static PRBool warning = PR_TRUE;
+ if (warning) warning = _PR_Obsolete(
+ "PR_Yield()", "PR_Sleep(PR_INTERVAL_NO_WAIT)");
+ return PR_Sleep(PR_INTERVAL_NO_WAIT);
+}
+
+PR_IMPLEMENT(PRStatus) PR_Sleep(PRIntervalTime ticks)
+{
+ PRStatus rv;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ if (PR_INTERVAL_NO_WAIT == ticks)
+ {
+ PTHREAD_YIELD();
+ rv = PR_SUCCESS;
+ }
+ else
+ {
+ PRCondVar *cv = PR_NewCondVar(pt_book.ml);
+ PR_ASSERT(cv != NULL);
+ PR_Lock(pt_book.ml);
+ rv = PR_WaitCondVar(cv, ticks);
+ PR_Unlock(pt_book.ml);
+ PR_DestroyCondVar(cv);
+ }
+ return rv;
+} /* PR_Sleep */
+
+void _PR_InitThreads(
+ PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs)
+{
+ int rv;
+ PRThread *thred;
+
+ /*
+ ** These might be function evaluations
+ */
+ pt_book.minPrio = PT_PRIO_MIN;
+ pt_book.maxPrio = PT_PRIO_MAX;
+
+ PR_ASSERT(NULL == pt_book.ml);
+ pt_book.ml = PR_NewLock();
+ PR_ASSERT(NULL != pt_book.ml);
+ pt_book.cv = PR_NewCondVar(pt_book.ml);
+ PR_ASSERT(NULL != pt_book.cv);
+ thred = PR_NEWZAP(PRThread);
+ PR_ASSERT(NULL != thred);
+ thred->arg = NULL;
+ thred->startFunc = NULL;
+ thred->priority = priority;
+ thred->id = pthread_self();
+
+ thred->state |= (PT_THREAD_DETACHED | PT_THREAD_PRIMORD);
+ if (PR_SYSTEM_THREAD == type)
+ {
+ thred->state |= PT_THREAD_SYSTEM;
+ pt_book.system += 1;
+ pt_book.this_many = 0;
+ }
+ else
+ {
+ pt_book.user += 1;
+ pt_book.this_many = 1;
+ }
+ thred->next = thred->prev = NULL;
+ pt_book.first = pt_book.last = thred;
+
+ thred->stack = PR_NEWZAP(PRThreadStack);
+ PR_ASSERT(thred->stack != NULL);
+ thred->stack->stackSize = 0;
+ thred->stack->thr = thred;
+ _PR_InitializeStack(thred->stack);
+
+ /*
+ * Create a key for our use to store a backpointer in the pthread
+ * to our PRThread object. This object gets deleted when the thread
+ * returns from its root in the case of a detached thread. Other
+ * threads delete the objects in Join.
+ *
+ * NB: The destructor logic seems to have a bug so it isn't used.
+ */
+ rv = PTHREAD_KEY_CREATE(&pt_book.key, NULL);
+ PR_ASSERT(0 == rv);
+ rv = pthread_setspecific(pt_book.key, thred);
+ PR_ASSERT(0 == rv);
+
+ pt_schedpriv = PT_PRIVCHECK();
+ PR_SetThreadPriority(thred, priority);
+
+ /*
+ * Linux pthreads use SIGUSR1 and SIGUSR2 internally, which
+ * conflict with the use of these two signals in our GC support.
+ * So we don't know how to support GC on Linux pthreads.
+ */
+#if !defined(LINUX2_0) && !defined(FREEBSD)
+ init_pthread_gc_support();
+#endif
+
+} /* _PR_InitThreads */
+
+PR_IMPLEMENT(PRStatus) PR_Cleanup()
+{
+ PRThread *me = PR_CurrentThread();
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("PR_Cleanup: shutting down NSPR"));
+ PR_ASSERT(me->state & PT_THREAD_PRIMORD);
+ if (me->state & PT_THREAD_PRIMORD)
+ {
+ PR_Lock(pt_book.ml);
+ while (pt_book.user > pt_book.this_many)
+ PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT);
+ PR_Unlock(pt_book.ml);
+
+ /*
+ * I am not sure if it's safe to delete the cv and lock here,
+ * since there may still be "system" threads around. If this
+ * call isn't immediately prior to exiting, then there's a
+ * problem.
+ */
+ if (0 == pt_book.system)
+ {
+ PR_DestroyCondVar(pt_book.cv); pt_book.cv = NULL;
+ PR_DestroyLock(pt_book.ml); pt_book.ml = NULL;
+ }
+ PR_DELETE(me->stack);
+ PR_DELETE(me);
+ return PR_SUCCESS;
+ }
+ return PR_FAILURE;
+} /* PR_Cleanup */
+
+PR_IMPLEMENT(void) PR_ProcessExit(PRIntn status)
+{
+ _exit(status);
+}
+
+/*
+ * $$$
+ * The following two thread-to-processor affinity functions are not
+ * yet implemented for pthreads. By the way, these functions should return
+ * PRStatus rather than PRInt32 to indicate the success/failure status.
+ * $$$
+ */
+
+PR_IMPLEMENT(PRInt32) PR_GetThreadAffinityMask(PRThread *thread, PRUint32 *mask)
+{
+ return 0; /* not implemented */
+}
+
+PR_IMPLEMENT(PRInt32) PR_SetThreadAffinityMask(PRThread *thread, PRUint32 mask )
+{
+ return 0; /* not implemented */
+}
+
+PR_IMPLEMENT(void)
+PR_SetThreadDumpProc(PRThread* thread, PRThreadDumpProc dump, void *arg)
+{
+ thread->dump = dump;
+ thread->dumpArg = arg;
+}
+
+/*
+ * Garbage collection support follows.
+ */
+
+#if defined(_PR_DCETHREADS)
+
+/*
+ * statics for Garbage Collection support. We don't need to protect these
+ * signal masks since the garbage collector itself is protected by a lock
+ * and multiple threads will not be garbage collecting at the same time.
+ */
+static sigset_t javagc_vtalarm_sigmask;
+static sigset_t javagc_intsoff_sigmask;
+
+#else /* defined(_PR_DCETHREADS) */
+
+/* a bogus signal mask for forcing a timed wait */
+/* Not so bogus in AIX as we really do a sigwait */
+static sigset_t sigwait_set;
+
+static struct timespec onemillisec = {0, 1000000L};
+static struct timespec hundredmillisec = {0, 100000000L};
+
+#endif /* defined(_PR_DCETHREADS) */
+
+static void suspend_signal_handler(PRIntn sig);
+
+#ifdef PT_NO_SIGTIMEDWAIT
+static void null_signal_handler(PRIntn sig);
+#endif
+
+static void init_pthread_gc_support()
+{
+ PRIntn rv;
+
+#if defined(_PR_DCETHREADS)
+ rv = sigemptyset(&javagc_vtalarm_sigmask);
+ PR_ASSERT(0 == rv);
+ rv = sigaddset(&javagc_vtalarm_sigmask, SIGVTALRM);
+ PR_ASSERT(0 == rv);
+#else /* defined(_PR_DCETHREADS) */
+ {
+ struct sigaction sigact_usr2 = {0};
+
+ sigact_usr2.sa_handler = suspend_signal_handler;
+ sigact_usr2.sa_flags = SA_RESTART;
+ sigemptyset (&sigact_usr2.sa_mask);
+
+ rv = sigaction (SIGUSR2, &sigact_usr2, NULL);
+ PR_ASSERT(0 == rv);
+
+ sigemptyset (&sigwait_set);
+#if defined(PT_NO_SIGTIMEDWAIT)
+ sigaddset (&sigwait_set, SIGUSR1);
+#else
+ sigaddset (&sigwait_set, SIGUSR2);
+#endif /* defined(PT_NO_SIGTIMEDWAIT) */
+ }
+#if defined(PT_NO_SIGTIMEDWAIT)
+ {
+ struct sigaction sigact_null = {0};
+ sigact_null.sa_handler = null_signal_handler;
+ sigact_null.sa_flags = SA_RESTART;
+ sigemptyset (&sigact_null.sa_mask);
+ rv = sigaction (SIGUSR1, &sigact_null, NULL);
+ PR_ASSERT(0 ==rv);
+ }
+#endif /* defined(PT_NO_SIGTIMEDWAIT) */
+#endif /* defined(_PR_DCETHREADS) */
+}
+
+PR_IMPLEMENT(void) PR_SetThreadGCAble()
+{
+ PR_Lock(pt_book.ml);
+ PR_CurrentThread()->state |= PT_THREAD_GCABLE;
+ PR_Unlock(pt_book.ml);
+}
+
+PR_IMPLEMENT(void) PR_ClearThreadGCAble()
+{
+ PR_Lock(pt_book.ml);
+ PR_CurrentThread()->state &= (~PT_THREAD_GCABLE);
+ PR_Unlock(pt_book.ml);
+}
+
+#if defined(DEBUG)
+static PRBool suspendAllOn = PR_FALSE;
+#endif
+
+static PRBool suspendAllSuspended = PR_FALSE;
+
+/* Are all GCAble threads (except gc'ing thread) suspended? */
+PR_IMPLEMENT(PRBool) PR_SuspendAllSuspended()
+{
+ return suspendAllSuspended;
+} /* PR_SuspendAllSuspended */
+
+PR_IMPLEMENT(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg)
+{
+ PRIntn count = 0;
+ PRStatus rv = PR_SUCCESS;
+ PRThread* thred = pt_book.first;
+ PRThread *me = PR_CurrentThread();
+
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_EnumerateThreads\n"));
+ /*
+ * $$$
+ * Need to suspend all threads other than me before doing this.
+ * This is really a gross and disgusting thing to do. The only
+ * good thing is that since all other threads are suspended, holding
+ * the lock during a callback seems like child's play.
+ * $$$
+ */
+ PR_ASSERT(suspendAllOn);
+
+ while (thred != NULL)
+ {
+ /* Steve Morse, 4-23-97: Note that we can't walk a queue by taking
+ * qp->next after applying the function "func". In particular, "func"
+ * might remove the thread from the queue and put it into another one in
+ * which case qp->next no longer points to the next entry in the original
+ * queue.
+ *
+ * To get around this problem, we save qp->next in qp_next before applying
+ * "func" and use that saved value as the next value after applying "func".
+ */
+ PRThread* next = thred->next;
+
+ if (thred->state & PT_THREAD_GCABLE)
+ {
+#if !defined(_PR_DCETHREADS)
+ PR_ASSERT((thred == me) || (thred->suspend & PT_THREAD_SUSPENDED));
+#endif
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("In PR_EnumerateThreads callback thread %X thid = %X\n",
+ thred, thred->id));
+
+ rv = func(thred, count++, arg);
+ if (rv != PR_SUCCESS)
+ return rv;
+ }
+ thred = next;
+ }
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("End PR_EnumerateThreads count = %d \n", count));
+ return rv;
+} /* PR_EnumerateThreads */
+
+/*
+ * PR_SuspendAll and PR_ResumeAll are called during garbage collection. The strategy
+ * we use is to send a SIGUSR2 signal to every gc able thread that we intend to suspend.
+ * The signal handler will record the stack pointer and will block until resumed by
+ * the resume call. Since the signal handler is the last routine called for the
+ * suspended thread, the stack pointer will also serve as a place where all the
+ * registers have been saved on the stack for the previously executing routines.
+ *
+ * Through global variables, we also make sure that PR_Suspend and PR_Resume does not
+ * proceed until the thread is suspended or resumed.
+ */
+
+#if !defined(_PR_DCETHREADS)
+
+/*
+ * In the signal handler, we can not use condition variable notify or wait.
+ * This does not work consistently across all pthread platforms. We also can not
+ * use locking since that does not seem to work reliably across platforms.
+ * Only thing we can do is yielding while testing for a global condition
+ * to change. This does work on pthread supported platforms. We may have
+ * to play with priortities if there are any problems detected.
+ */
+
+ /*
+ * In AIX, you cannot use ANY pthread calls in the signal handler except perhaps
+ * pthread_yield. But that is horribly inefficient. Hence we use only sigwait, no
+ * sigtimedwait is available. We need to use another user signal, SIGUSR1. Actually
+ * SIGUSR1 is also used by exec in Java. So our usage here breaks the exec in Java,
+ * for AIX. You cannot use pthread_cond_wait or pthread_delay_np in the signal
+ * handler as all synchronization mechanisms just break down.
+ */
+
+#if defined(PT_NO_SIGTIMEDWAIT)
+static void null_signal_handler(PRIntn sig)
+{
+ return;
+}
+#endif
+
+static void suspend_signal_handler(PRIntn sig)
+{
+ PRThread *me = PR_CurrentThread();
+
+ PR_ASSERT(me != NULL);
+ PR_ASSERT(me->state & PT_THREAD_GCABLE);
+ PR_ASSERT((me->suspend & PT_THREAD_SUSPENDED) == 0);
+
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("Begin suspend_signal_handler thred %X thread id = %X\n",
+ me, me->id));
+
+ /*
+ * save stack pointer
+ */
+ me->sp = &me;
+
+ /*
+ At this point, the thread's stack pointer has been saved,
+ And it is going to enter a wait loop until it is resumed.
+ So it is _really_ suspended
+ */
+
+ me->suspend |= PT_THREAD_SUSPENDED;
+
+ /*
+ * now, block current thread
+ */
+#if defined(PT_NO_SIGTIMEDWAIT)
+ pthread_cond_signal(&me->suspendResumeCV);
+ while (me->suspend & PT_THREAD_SUSPENDED)
+ {
+#if !defined(FREEBSD) /*XXX*/
+ PRIntn rv;
+ sigwait(&sigwait_set, &rv);
+#endif
+ }
+ me->suspend |= PT_THREAD_RESUMED;
+ pthread_cond_signal(&me->suspendResumeCV);
+#else /* defined(PT_NO_SIGTIMEDWAIT) */
+ while (me->suspend & PT_THREAD_SUSPENDED)
+ {
+ PRIntn rv = sigtimedwait(&sigwait_set, NULL, &hundredmillisec);
+ PR_ASSERT(-1 == rv);
+ }
+ me->suspend |= PT_THREAD_RESUMED;
+#endif
+
+ /*
+ * At this point, thread has been resumed, so set a global condition.
+ * The ResumeAll needs to know that this has really been resumed.
+ * So the signal handler sets a flag which PR_ResumeAll will reset.
+ * The PR_ResumeAll must reset this flag ...
+ */
+
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("End suspend_signal_handler thred = %X tid = %X\n", me, me->id));
+} /* suspend_signal_handler */
+
+static void PR_SuspendSet(PRThread *thred)
+{
+ PRIntn rv;
+
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("PR_SuspendSet thred %X thread id = %X\n", thred, thred->id));
+
+
+ /*
+ * Check the thread state and signal the thread to suspend
+ */
+
+ PR_ASSERT((thred->suspend & PT_THREAD_SUSPENDED) == 0);
+
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("doing pthread_kill in PR_SuspendSet thred %X tid = %X\n",
+ thred, thred->id));
+ rv = pthread_kill (thred->id, SIGUSR2);
+ PR_ASSERT(0 == rv);
+}
+
+static void PR_SuspendTest(PRThread *thred)
+{
+ PRIntn rv;
+
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("Begin PR_SuspendTest thred %X thread id = %X\n", thred, thred->id));
+
+
+ /*
+ * Wait for the thread to be really suspended. This happens when the
+ * suspend signal handler stores the stack pointer and sets the state
+ * to suspended.
+ */
+
+#if defined(PT_NO_SIGTIMEDWAIT)
+ pthread_mutex_lock(&thred->suspendResumeMutex);
+ while ((thred->suspend & PT_THREAD_SUSPENDED) == 0)
+ {
+ pthread_cond_timedwait(
+ &thred->suspendResumeCV, &thred->suspendResumeMutex, &onemillisec);
+ }
+ pthread_mutex_unlock(&thred->suspendResumeMutex);
+#else
+ while ((thred->suspend & PT_THREAD_SUSPENDED) == 0)
+ {
+ rv = sigtimedwait(&sigwait_set, NULL, &onemillisec);
+ PR_ASSERT(-1 == rv);
+ }
+#endif
+
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("End PR_SuspendTest thred %X tid %X\n", thred, thred->id));
+} /* PR_SuspendTest */
+
+PR_IMPLEMENT(void) PR_ResumeSet(PRThread *thred)
+{
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("PR_ResumeSet thred %X thread id = %X\n", thred, thred->id));
+
+ /*
+ * Clear the global state and set the thread state so that it will
+ * continue past yield loop in the suspend signal handler
+ */
+
+ PR_ASSERT(thred->suspend & PT_THREAD_SUSPENDED);
+
+
+ thred->suspend &= ~PT_THREAD_SUSPENDED;
+
+#if defined(PT_NO_SIGTIMEDWAIT)
+ pthread_kill(thred->id, SIGUSR1);
+#endif
+
+} /* PR_ResumeSet */
+
+PR_IMPLEMENT(void) PR_ResumeTest(PRThread *thred)
+{
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("Begin PR_ResumeTest thred %X thread id = %X\n", thred, thred->id));
+
+ /*
+ * Wait for the threads resume state to change
+ * to indicate it is really resumed
+ */
+#if defined(PT_NO_SIGTIMEDWAIT)
+ pthread_mutex_lock(&thred->suspendResumeMutex);
+ while ((thred->suspend & PT_THREAD_RESUMED) == 0)
+ {
+ pthread_cond_timedwait(
+ &thred->suspendResumeCV, &thred->suspendResumeMutex, &onemillisec);
+ }
+ pthread_mutex_unlock(&thred->suspendResumeMutex);
+#else
+ while ((thred->suspend & PT_THREAD_RESUMED) == 0) {
+ PRIntn rv = sigtimedwait(&sigwait_set, NULL, &onemillisec);
+ PR_ASSERT(-1 == rv);
+ }
+#endif
+
+ thred->suspend &= ~PT_THREAD_RESUMED;
+
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, (
+ "End PR_ResumeTest thred %X tid %X\n", thred, thred->id));
+} /* PR_ResumeTest */
+
+PR_IMPLEMENT(void) PR_SuspendAll()
+{
+#ifdef DEBUG
+ PRIntervalTime stime, etime;
+#endif
+ PRThread* thred = pt_book.first;
+ PRThread *me = PR_CurrentThread();
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_SuspendAll\n"));
+ /*
+ * Stop all threads which are marked GC able.
+ */
+ PR_Lock(pt_book.ml);
+#ifdef DEBUG
+ suspendAllOn = PR_TRUE;
+ stime = PR_IntervalNow();
+#endif
+ while (thred != NULL)
+ {
+ if ((thred != me) && (thred->state & PT_THREAD_GCABLE))
+ PR_SuspendSet(thred);
+ thred = thred->next;
+ }
+
+ /* Wait till they are really suspended */
+ thred = pt_book.first;
+ while (thred != NULL)
+ {
+ if ((thred != me) && (thred->state & PT_THREAD_GCABLE))
+ PR_SuspendTest(thred);
+ thred = thred->next;
+ }
+
+ suspendAllSuspended = PR_TRUE;
+
+#ifdef DEBUG
+ etime = PR_IntervalNow();
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,\
+ ("End PR_SuspendAll (time %dms)\n",
+ PR_IntervalToMilliseconds(etime - stime)));
+#endif
+} /* PR_SuspendAll */
+
+PR_IMPLEMENT(void) PR_ResumeAll()
+{
+#ifdef DEBUG
+ PRIntervalTime stime, etime;
+#endif
+ PRThread* thred = pt_book.first;
+ PRThread *me = PR_CurrentThread();
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_ResumeAll\n"));
+ /*
+ * Resume all previously suspended GC able threads.
+ */
+ suspendAllSuspended = PR_FALSE;
+#ifdef DEBUG
+ stime = PR_IntervalNow();
+#endif
+
+ while (thred != NULL)
+ {
+ if ((thred != me) && (thred->state & PT_THREAD_GCABLE))
+ PR_ResumeSet(thred);
+ thred = thred->next;
+ }
+
+ thred = pt_book.first;
+ while (thred != NULL)
+ {
+ if ((thred != me) && (thred->state & PT_THREAD_GCABLE))
+ PR_ResumeTest(thred);
+ thred = thred->next;
+ }
+
+ PR_Unlock(pt_book.ml);
+#ifdef DEBUG
+ suspendAllOn = PR_FALSE;
+ etime = PR_IntervalNow();
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("End PR_ResumeAll (time %dms)\n",
+ PR_IntervalToMilliseconds(etime - stime)));
+#endif
+} /* PR_ResumeAll */
+
+/* Return the stack pointer for the given thread- used by the GC */
+PR_IMPLEMENT(void *)PR_GetSP(PRThread *thred)
+{
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
+ ("in PR_GetSP thred %X thid = %X, sp = %X \n",
+ thred, thred->id, thred->sp));
+ return thred->sp;
+} /* PR_GetSP */
+
+#else /* !defined(_PR_DCETHREADS) */
+
+/*
+ * For DCE threads, there is no pthread_kill or a way of suspending or resuming a
+ * particular thread. We will just disable the preemption (virtual timer alarm) and
+ * let the executing thread finish the garbage collection. This stops all other threads
+ * (GC able or not) and is very inefficient but there is no other choice.
+ */
+PR_IMPLEMENT(void) PR_SuspendAll()
+{
+ PRIntn rv;
+
+ suspendAllOn = PR_TRUE;
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_SuspendAll\n"));
+ /*
+ * turn off preemption - i.e add virtual alarm signal to the set of
+ * blocking signals
+ */
+ rv = sigprocmask(
+ SIG_BLOCK, &javagc_vtalarm_sigmask, &javagc_intsoff_sigmask);
+ PR_ASSERT(0 == rv);
+ suspendAllSuspended = PR_TRUE;
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End PR_SuspendAll\n"));
+} /* PR_SuspendAll */
+
+PR_IMPLEMENT(void) PR_ResumeAll()
+{
+ PRIntn rv;
+
+ suspendAllSuspended = PR_FALSE;
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_ResumeAll\n"));
+ /* turn on preemption - i.e re-enable virtual alarm signal */
+
+ rv = sigprocmask(SIG_SETMASK, &javagc_intsoff_sigmask, (sigset_t *)NULL);
+ PR_ASSERT(0 == rv);
+ suspendAllOn = PR_FALSE;
+
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End PR_ResumeAll\n"));
+} /* PR_ResumeAll */
+
+/* Return the stack pointer for the given thread- used by the GC */
+PR_IMPLEMENT(void*)PR_GetSP(PRThread *thred)
+{
+ pthread_t tid = thred->id;
+ char *thread_tcb, *top_sp;
+
+ /*
+ * For HPUX DCE threads, pthread_t is a struct with the
+ * following three fields (see pthread.h, dce/cma.h):
+ * cma_t_address field1;
+ * short int field2;
+ * short int field3;
+ * where cma_t_address is typedef'd to be either void*
+ * or char*.
+ */
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_GetSP\n"));
+ thread_tcb = (char*)tid.field1;
+ top_sp = *(char**)(thread_tcb + 128);
+ PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End PR_GetSP %X \n", top_sp));
+ return top_sp;
+} /* PR_GetSP */
+
+#endif /* !defined(_PR_DCETHREADS) */
+
+#endif /* defined(_PR_PTHREADS) || defined(_PR_DCETHREADS) */
+
+/* ptthread.c */
diff --git a/pr/src/resource.h b/pr/src/resource.h
new file mode 100644
index 00000000..8350f5a9
--- /dev/null
+++ b/pr/src/resource.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by nspr.rc
+//
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/pr/src/threads/Makefile b/pr/src/threads/Makefile
new file mode 100644
index 00000000..91ad0aaa
--- /dev/null
+++ b/pr/src/threads/Makefile
@@ -0,0 +1,59 @@
+#
+# The contents of this file are subject to the Netscape Public License
+# Version 1.0 (the "NPL"); you may not use this file except in
+# compliance with the NPL. You may obtain a copy of the NPL at
+# http://www.mozilla.org/NPL/
+#
+# Software distributed under the NPL is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+# for the specific language governing rights and limitations under the
+# NPL.
+#
+# The Initial Developer of this code under the NPL is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1998 Netscape Communications Corporation. All Rights
+# Reserved.
+#
+
+#! gmake
+
+MOD_DEPTH = ../../..
+
+include $(MOD_DEPTH)/config/config.mk
+
+ifdef USE_PTHREADS
+ DIRS =
+else
+ DIRS = combined
+endif
+
+# Disable optimization of the nspr on SunOS4.1.3
+ifeq ($(OS_ARCH),SunOS)
+ifeq ($(OS_RELEASE),4.1.3_U1)
+OPTIMIZER =
+endif
+endif
+
+ifdef USE_PTHREADS
+CSRCS = prcmon.c \
+ $(NULL)
+else
+CSRCS = \
+ prcmon.c \
+ prdump.c \
+ prmon.c \
+ prsem.c \
+ prcthr.c \
+ prtpd.c \
+ $(NULL)
+endif
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(DIST)/include/private -I$(DIST)/include
+
+include $(MOD_DEPTH)/config/rules.mk
+
+export:: $(TARGETS)
+
+install:: export
diff --git a/pr/src/threads/combined/Makefile b/pr/src/threads/combined/Makefile
new file mode 100644
index 00000000..676d8817
--- /dev/null
+++ b/pr/src/threads/combined/Makefile
@@ -0,0 +1,52 @@
+#
+# The contents of this file are subject to the Netscape Public License
+# Version 1.0 (the "NPL"); you may not use this file except in
+# compliance with the NPL. You may obtain a copy of the NPL at
+# http://www.mozilla.org/NPL/
+#
+# Software distributed under the NPL is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+# for the specific language governing rights and limitations under the
+# NPL.
+#
+# The Initial Developer of this code under the NPL is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1998 Netscape Communications Corporation. All Rights
+# Reserved.
+#
+
+#! gmake
+
+MOD_DEPTH = ../../../..
+
+include $(MOD_DEPTH)/config/config.mk
+
+# Disable optimization of the nspr on SunOS4.1.3
+ifeq ($(OS_ARCH),SunOS)
+ifeq ($(OS_RELEASE),4.1.3_U1)
+OPTIMIZER =
+endif
+endif
+
+ifdef USE_PTHREADS
+CSRCS = \
+ $(NULL)
+else
+CSRCS = \
+ prucpu.c \
+ prucv.c \
+ prulock.c \
+ pruthr.c \
+ prustack.c \
+ $(NULL)
+endif
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(DIST)/include/private -I$(DIST)/include
+
+include $(MOD_DEPTH)/config/rules.mk
+
+export:: $(TARGETS)
+
+install:: export
diff --git a/pr/src/threads/combined/README b/pr/src/threads/combined/README
new file mode 100644
index 00000000..aa266652
--- /dev/null
+++ b/pr/src/threads/combined/README
@@ -0,0 +1,62 @@
+NSPR 2.0 evolution
+------------------
+
+
+Phase 1- today
+
+Currently (Oct 10, 1996) NSPR 2.0 has two modes. Either _PR_NTHREAD
+is defined, in which case the PR_CreateThread() call always creates a
+native kernel thread, or _PR_NTHREAD is not defined and PR_CreateThread()
+always creates user level threads within the single, original process. This
+source code is reflected in two directories, nspr20/pr/src/threads/native, and
+nspr20/pr/src/threads/user. Although the PR_CreateThread() function has
+a paramter to specify the "scope" of a thread, this parameter is not yet
+used- except on solaris where it uses it to specify bound vs unbound threads.
+
+Phase 2 - next week
+
+The next step is to provide a combination of user and native threads. The
+idea, of course, is to have some small number of native threads and each of
+those threads be able to run user level threads. The number of native
+threads created will most likely be proportional to the number of CPUs in
+the system. For this reason, the specific set of native threads which are
+used to run the user-level threads will be called "CPU" threads.
+
+The user level threads which will be run on the CPU threads are able to
+run on any of the CPU threads available, and over the course of a user-level
+thread's lifetime, it may drift from one CPU thread to another. All
+user-level threads will compete for processing time via a single run queue.
+
+Creation of a CPU thread will be primarily controlled by NSPR itself or by
+the user running a function PR_Concurrency(). The details of PR_Concurrency()
+have not yet been worked out; but the idea is that the user can specify to
+NSPR how many CPU threads are desired.
+
+In this system, user-level threads are created by using PR_CreateThread() and
+specifying the PR_LOCAL_SCOPE option. LOCAL_SCOPE indicates that the thread
+will be under the control of the "local" scheduler. Creating threads with
+GLOBAL_SCOPE, on the other hand will create a thread which is under the
+control of the system's scheduler. In otherwords, this creates a native thread
+which is not a CPU thread; it runs a single thread task and never has more
+than one task to run. LOCAL_SCOPE is much like creating a Solaris unbound
+thread, while GLOBAL_SCOPE is similar to creating a Solaris bound thread.
+
+To implement this architecture, the source code will still maintain the "user"
+and "native" directories which is has today. However a third directory
+"combined" will also exist. To compile a version of NSPR which only creates
+native threads, the user can define _PR_NTHREAD. For exclusive user-level
+threads, do not define _PR_NTHREAD. To get the combined threads, define
+_PR_NTHREAD and _PR_USE_CPUS.
+
+
+Phase 3 - later than next week
+
+The goal is to eliminate the 3 directories. Once these three models are in
+place, the remaining work will be to eliminate the native and user thread
+directories for all platforms, so that the entire thread model is contained
+within what is today called the "combined" model. This new and glorious
+source code will attempt to make the "combined" model on any platforms which
+provide the necessary underlying native threading, but will also be
+capable of using exclusive user-level threads on systems which don't have
+native threads.
+
diff --git a/pr/src/threads/combined/prucpu.c b/pr/src/threads/combined/prucpu.c
new file mode 100644
index 00000000..b0e4f5fd
--- /dev/null
+++ b/pr/src/threads/combined/prucpu.c
@@ -0,0 +1,304 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+_PRCPU *_pr_primordialCPU;
+
+PRInt32 _pr_md_idle_cpus; /* number of idle cpus */
+/*
+ * The idle threads in MxN models increment/decrement _pr_md_idle_cpus.
+ * If _PR_HAVE_ATOMIC_OPS is not defined, they can't use the atomic
+ * increment/decrement routines (which are based on PR_Lock/PR_Unlock),
+ * because PR_Lock asserts that the calling thread is not an idle thread.
+ * So we use a _MDLock to protect _pr_md_idle_cpus.
+ */
+#if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
+#ifndef _PR_HAVE_ATOMIC_OPS
+static _MDLock _pr_md_idle_cpus_lock;
+#endif
+#endif
+PRUintn _pr_numCPU;
+PRInt32 _pr_cpus_exit;
+PRInt32 _pr_cpu_affinity_mask = 0;
+
+#if !defined (_PR_GLOBAL_THREADS_ONLY)
+
+static PRUintn _pr_cpuID;
+
+static void PR_CALLBACK _PR_CPU_Idle(void *);
+static _PRCPU *_PR_CreateCPU(PRThread *thread, PRBool needQueue);
+
+void _PR_InitCPUs()
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ _pr_cpuID = 0;
+ _MD_NEW_LOCK( &_pr_cpuLock);
+#if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
+#ifndef _PR_HAVE_ATOMIC_OPS
+ _MD_NEW_LOCK(&_pr_md_idle_cpus_lock);
+#endif
+#endif
+
+#ifdef HAVE_CUSTOM_USER_THREADS
+ _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me);
+#endif
+
+ /* Now start the first CPU. */
+ _pr_primordialCPU = _PR_CreateCPU(me, PR_TRUE);
+ _pr_numCPU = 1;
+
+ _PR_MD_SET_CURRENT_CPU(_pr_primordialCPU);
+
+ /* Initialize cpu for current thread (could be different from me) */
+ _PR_MD_CURRENT_THREAD()->cpu = _pr_primordialCPU;
+
+ _PR_MD_SET_LAST_THREAD(me);
+
+ _PR_MD_INIT_CPUS();
+}
+
+
+static _PRCPUQueue *_PR_CreateCPUQueue(void)
+{
+ PRInt32 index;
+ _PRCPUQueue *cpuQueue;
+ cpuQueue = PR_NEWZAP(_PRCPUQueue);
+
+ _MD_NEW_LOCK( &cpuQueue->runQLock );
+ _MD_NEW_LOCK( &cpuQueue->sleepQLock );
+ _MD_NEW_LOCK( &cpuQueue->miscQLock );
+
+ for (index = 0; index < PR_PRIORITY_LAST + 1; index++)
+ PR_INIT_CLIST( &(cpuQueue->runQ[index]) );
+ PR_INIT_CLIST( &(cpuQueue->sleepQ) );
+ PR_INIT_CLIST( &(cpuQueue->pauseQ) );
+ PR_INIT_CLIST( &(cpuQueue->suspendQ) );
+ PR_INIT_CLIST( &(cpuQueue->waitingToJoinQ) );
+
+ cpuQueue->numCPUs = 1;
+
+ return cpuQueue;
+}
+
+/*
+ * Create a new CPU.
+ */
+static _PRCPU *_PR_CreateCPU(PRThread *thread, PRBool needQueue)
+{
+ _PRCPU *cpu;
+
+ /*
+ ** Create a new cpu. The assumption this code makes is that the
+ ** underlying operating system creates a stack to go with the new
+ ** native thread. That stack will be used by the cpu when pausing.
+ */
+ cpu = PR_NEWZAP(_PRCPU);
+ if (cpu) {
+
+ cpu->last_clock = PR_IntervalNow();
+
+ if (needQueue == PR_TRUE)
+ cpu->queue = _PR_CreateCPUQueue();
+ else
+ cpu->queue = _PR_MD_CURRENT_CPU()->queue;
+
+ if (!cpu->queue) {
+ PR_DELETE(cpu);
+ return NULL;
+ }
+
+ /* Before we create any threads on this CPU we have to
+ * set the current CPU
+ */
+ _PR_MD_SET_CURRENT_CPU(cpu);
+ _PR_MD_INIT_RUNNING_CPU(cpu);
+ thread->cpu = cpu;
+
+ cpu->idle_thread = _PR_CreateThread(PR_SYSTEM_THREAD,
+ _PR_CPU_Idle,
+ (void *)cpu,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0,
+ _PR_IDLE_THREAD);
+
+ if (!cpu->idle_thread) {
+ /* didn't clean up CPU queue XXXMB */
+ PR_DELETE(cpu);
+ return NULL;
+ }
+ cpu->idle_thread->no_sched = 0;
+
+ cpu->thread = thread;
+
+ if (_pr_cpu_affinity_mask)
+ PR_SetThreadAffinityMask(thread, _pr_cpu_affinity_mask);
+
+ /* Created a new CPU */
+ _PR_CPU_LIST_LOCK();
+ cpu->id = _pr_cpuID++;
+ PR_APPEND_LINK(&cpu->links, &_PR_CPUQ());
+ _PR_CPU_LIST_UNLOCK();
+ }
+ return cpu;
+}
+
+/*
+** This code is used during a cpu's initial creation.
+*/
+static void _PR_RunCPU(void *unused)
+{
+#if defined(XP_MAC)
+#pragma unused (unused)
+#endif
+
+ _PRCPU *cpu;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(NULL != me);
+
+#ifdef HAVE_CUSTOM_USER_THREADS
+ _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me);
+#endif
+
+ me->no_sched = 1;
+ cpu = _PR_CreateCPU(me, PR_TRUE);
+
+ _PR_MD_SET_CURRENT_CPU(cpu);
+ _PR_MD_SET_CURRENT_THREAD(cpu->thread);
+ me->cpu = cpu;
+ while(1) {
+ PRInt32 is;
+ if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is);
+ _PR_MD_START_INTERRUPTS();
+ _PR_MD_SWITCH_CONTEXT(me);
+ }
+}
+
+static void PR_CALLBACK _PR_CPU_Idle(void *_cpu)
+{
+ _PRCPU *cpu = (_PRCPU *)_cpu;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(NULL != me);
+
+ me->cpu = cpu;
+ cpu->idle_thread = me;
+ if (_MD_LAST_THREAD())
+ _MD_LAST_THREAD()->no_sched = 0;
+ if (!_PR_IS_NATIVE_THREAD(me)) _PR_SET_INTSOFF(0);
+ while(1) {
+ PRInt32 is;
+ PRIntervalTime timeout;
+
+ if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is);
+
+ _PR_RUNQ_LOCK(cpu);
+#if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
+#ifdef _PR_HAVE_ATOMIC_OPS
+ _PR_MD_ATOMIC_INCREMENT(&_pr_md_idle_cpus);
+#else
+ _PR_MD_LOCK(&_pr_md_idle_cpus_lock);
+ _pr_md_idle_cpus++;
+ _PR_MD_UNLOCK(&_pr_md_idle_cpus_lock);
+#endif /* _PR_HAVE_ATOMIC_OPS */
+#endif
+ /* If someone on runq; do a nonblocking PAUSECPU */
+ if (_PR_RUNQREADYMASK(me->cpu) != 0) {
+ _PR_RUNQ_UNLOCK(cpu);
+ timeout = PR_INTERVAL_NO_WAIT;
+ } else {
+ _PR_RUNQ_UNLOCK(cpu);
+
+ _PR_SLEEPQ_LOCK(cpu);
+ if (PR_CLIST_IS_EMPTY(&_PR_SLEEPQ(me->cpu))) {
+ timeout = PR_INTERVAL_NO_TIMEOUT;
+ } else {
+ PRThread *wakeThread;
+ wakeThread = _PR_THREAD_PTR(_PR_SLEEPQ(me->cpu).next);
+ timeout = wakeThread->sleep;
+ }
+ _PR_SLEEPQ_UNLOCK(cpu);
+ }
+
+
+ /* Wait for an IO to complete */
+ (void)_PR_MD_PAUSE_CPU(timeout);
+
+#if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
+#ifdef _PR_HAVE_ATOMIC_OPS
+ _PR_MD_ATOMIC_DECREMENT(&_pr_md_idle_cpus);
+#else
+ _PR_MD_LOCK(&_pr_md_idle_cpus_lock);
+ _pr_md_idle_cpus--;
+ _PR_MD_UNLOCK(&_pr_md_idle_cpus_lock);
+#endif /* _PR_HAVE_ATOMIC_OPS */
+#endif
+
+ _PR_ClockInterrupt();
+
+ /* Now schedule any thread that is on the runq
+ * INTS must be OFF when calling PR_Schedule()
+ */
+ me->state = _PR_RUNNABLE;
+ _PR_MD_SWITCH_CONTEXT(me);
+ if (!_PR_IS_NATIVE_THREAD(me)) _PR_FAST_INTSON(is);
+ }
+}
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+
+
+PR_IMPLEMENT(void) PR_SetConcurrency(PRUintn numCPUs)
+{
+#if !defined(_PR_GLOBAL_THREADS_ONLY) && !defined(_PR_LOCAL_THREADS_ONLY)
+
+ PRUintn newCPU;
+ PRThread *cpu;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ _PR_CPU_LIST_LOCK();
+ if (_pr_numCPU < numCPUs) {
+ newCPU = numCPUs - _pr_numCPU;
+ _pr_numCPU = numCPUs;
+ } else newCPU = 0;
+ _PR_CPU_LIST_UNLOCK();
+
+ for (; newCPU; newCPU--) {
+ cpu = _PR_CreateThread(PR_SYSTEM_THREAD,
+ _PR_RunCPU,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0,
+ _PR_IDLE_THREAD);
+ }
+#endif
+}
+
+PR_IMPLEMENT(_PRCPU *) _PR_GetPrimordialCPU(void)
+{
+ if (_pr_primordialCPU)
+ return _pr_primordialCPU;
+ else
+ return _PR_MD_CURRENT_CPU();
+}
diff --git a/pr/src/threads/combined/prucv.c b/pr/src/threads/combined/prucv.c
new file mode 100644
index 00000000..08962d9f
--- /dev/null
+++ b/pr/src/threads/combined/prucv.c
@@ -0,0 +1,634 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+
+#include "primpl.h"
+#include "prinrval.h"
+#include "prtypes.h"
+
+
+/*
+** Notify one thread that it has finished waiting on a condition variable
+** Caller must hold the _PR_CVAR_LOCK(cv)
+*/
+PRBool NotifyThread (PRThread *thread, PRThread *me)
+{
+ PRBool rv;
+
+ PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
+
+ _PR_THREAD_LOCK(thread);
+ PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
+ if ( !_PR_IS_NATIVE_THREAD(thread) ) {
+ if (thread->wait.cvar != NULL) {
+ thread->wait.cvar = NULL;
+
+ _PR_SLEEPQ_LOCK(thread->cpu);
+ /* The notify and timeout can collide; in which case both may
+ * attempt to delete from the sleepQ; only let one do it.
+ */
+ if (thread->flags & (_PR_ON_SLEEPQ|_PR_ON_PAUSEQ))
+ _PR_DEL_SLEEPQ(thread, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(thread->cpu);
+
+ if (thread->flags & _PR_SUSPENDING) {
+ /*
+ * set thread state to SUSPENDED; a Resume operation
+ * on the thread will move it to the runQ
+ */
+ thread->state = _PR_SUSPENDED;
+ _PR_MISCQ_LOCK(thread->cpu);
+ _PR_ADD_SUSPENDQ(thread, thread->cpu);
+ _PR_MISCQ_UNLOCK(thread->cpu);
+ _PR_THREAD_UNLOCK(thread);
+ } else {
+ /* Make thread runnable */
+ thread->state = _PR_RUNNABLE;
+ _PR_THREAD_UNLOCK(thread);
+
+ _PR_AddThreadToRunQ(me, thread);
+ _PR_MD_WAKEUP_WAITER(thread);
+ }
+
+ rv = PR_TRUE;
+ } else {
+ /* Thread has already been notified */
+ _PR_THREAD_UNLOCK(thread);
+ rv = PR_FALSE;
+ }
+ } else { /* If the thread is a native thread */
+ if (thread->wait.cvar) {
+ thread->wait.cvar = NULL;
+
+ if (thread->flags & _PR_SUSPENDING) {
+ /*
+ * set thread state to SUSPENDED; a Resume operation
+ * on the thread will enable the thread to run
+ */
+ thread->state = _PR_SUSPENDED;
+ } else
+ thread->state = _PR_RUNNING;
+ _PR_THREAD_UNLOCK(thread);
+ _PR_MD_WAKEUP_WAITER(thread);
+ rv = PR_TRUE;
+ } else {
+ _PR_THREAD_UNLOCK(thread);
+ rv = PR_FALSE;
+ }
+ }
+
+ return rv;
+}
+
+/*
+ * Notify thread waiting on cvar; called when thread is interrupted
+ * The thread lock is held on entry and released before return
+ */
+void _PR_NotifyLockedThread (PRThread *thread)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRCondVar *cvar;
+ PRThreadPriority pri;
+
+ if ( !_PR_IS_NATIVE_THREAD(me))
+ PR_ASSERT(_PR_MD_GET_INTSOFF() != 0);
+
+ cvar = thread->wait.cvar;
+ thread->wait.cvar = NULL;
+ _PR_THREAD_UNLOCK(thread);
+
+ _PR_CVAR_LOCK(cvar);
+ _PR_THREAD_LOCK(thread);
+
+ if (!_PR_IS_NATIVE_THREAD(thread)) {
+ _PR_SLEEPQ_LOCK(thread->cpu);
+ /* The notify and timeout can collide; in which case both may
+ * attempt to delete from the sleepQ; only let one do it.
+ */
+ if (thread->flags & (_PR_ON_SLEEPQ|_PR_ON_PAUSEQ))
+ _PR_DEL_SLEEPQ(thread, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(thread->cpu);
+
+ /* Make thread runnable */
+ pri = thread->priority;
+ thread->state = _PR_RUNNABLE;
+
+ PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
+
+ _PR_AddThreadToRunQ(me, thread);
+ _PR_THREAD_UNLOCK(thread);
+
+ _PR_MD_WAKEUP_WAITER(thread);
+ } else {
+ if (thread->flags & _PR_SUSPENDING) {
+ /*
+ * set thread state to SUSPENDED; a Resume operation
+ * on the thread will enable the thread to run
+ */
+ thread->state = _PR_SUSPENDED;
+ } else
+ thread->state = _PR_RUNNING;
+ _PR_THREAD_UNLOCK(thread);
+ _PR_MD_WAKEUP_WAITER(thread);
+ }
+
+ _PR_CVAR_UNLOCK(cvar);
+ return;
+}
+
+/*
+** Make the given thread wait for the given condition variable
+*/
+PRStatus _PR_WaitCondVar(
+ PRThread *thread, PRCondVar *cvar, PRLock *lock, PRIntervalTime timeout)
+{
+ intn is;
+ PRStatus rv = PR_SUCCESS;
+
+ PR_ASSERT(thread == _PR_MD_CURRENT_THREAD());
+ PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
+
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ if (_PR_PENDING_INTERRUPT(thread)) {
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ thread->flags &= ~_PR_INTERRUPT;
+ return PR_FAILURE;
+ }
+
+ thread->wait.cvar = cvar;
+ lock->owner = NULL;
+ _PR_MD_WAIT_CV(&cvar->md,&lock->ilock, timeout);
+ thread->wait.cvar = NULL;
+ lock->owner = thread;
+ if (_PR_PENDING_INTERRUPT(thread)) {
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ thread->flags &= ~_PR_INTERRUPT;
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+#else /* _PR_GLOBAL_THREADS_ONLY */
+
+ if ( !_PR_IS_NATIVE_THREAD(thread))
+ _PR_INTSOFF(is);
+
+ _PR_CVAR_LOCK(cvar);
+ _PR_THREAD_LOCK(thread);
+
+ if (_PR_PENDING_INTERRUPT(thread)) {
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ thread->flags &= ~_PR_INTERRUPT;
+ _PR_CVAR_UNLOCK(cvar);
+ _PR_THREAD_UNLOCK(thread);
+ if ( !_PR_IS_NATIVE_THREAD(thread))
+ _PR_INTSON(is);
+ return PR_FAILURE;
+ }
+
+ thread->state = _PR_COND_WAIT;
+ thread->wait.cvar = cvar;
+
+ /*
+ ** Put the caller thread on the condition variable's wait Q
+ */
+ PR_APPEND_LINK(&thread->waitQLinks, &cvar->condQ);
+
+ /* Note- for global scope threads, we don't put them on the
+ * global sleepQ, so each global thread must put itself
+ * to sleep only for the time it wants to.
+ */
+ if ( !_PR_IS_NATIVE_THREAD(thread) ) {
+ _PR_SLEEPQ_LOCK(thread->cpu);
+ _PR_ADD_SLEEPQ(thread, timeout);
+ _PR_SLEEPQ_UNLOCK(thread->cpu);
+ }
+ _PR_CVAR_UNLOCK(cvar);
+ _PR_THREAD_UNLOCK(thread);
+
+ /*
+ ** Release lock protecting the condition variable and thereby giving time
+ ** to the next thread which can potentially notify on the condition variable
+ */
+ PR_Unlock(lock);
+
+ PR_LOG(_pr_cvar_lm, PR_LOG_MIN,
+ ("PR_Wait: cvar=%p waiting for %d", cvar, timeout));
+
+ rv = _PR_MD_WAIT(thread, timeout);
+
+ _PR_CVAR_LOCK(cvar);
+ PR_REMOVE_LINK(&thread->waitQLinks);
+ _PR_CVAR_UNLOCK(cvar);
+
+ PR_LOG(_pr_cvar_lm, PR_LOG_MIN,
+ ("PR_Wait: cvar=%p done waiting", cvar));
+
+ if ( !_PR_IS_NATIVE_THREAD(thread))
+ _PR_INTSON(is);
+
+ /* Acquire lock again that we had just relinquished */
+ PR_Lock(lock);
+
+ if (_PR_PENDING_INTERRUPT(thread)) {
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ thread->flags &= ~_PR_INTERRUPT;
+ return PR_FAILURE;
+ }
+
+ return rv;
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+}
+
+void _PR_NotifyCondVar(PRCondVar *cvar, PRThread *me)
+{
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ _PR_MD_NOTIFY_CV(&cvar->md, &cvar->lock->ilock);
+#else /* _PR_GLOBAL_THREADS_ONLY */
+
+ PRCList *q;
+ PRIntn is;
+
+ if ( !_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(is);
+ PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
+
+ _PR_CVAR_LOCK(cvar);
+ q = cvar->condQ.next;
+ while (q != &cvar->condQ) {
+#ifndef XP_MAC
+ PR_LOG(_pr_cvar_lm, PR_LOG_MIN, ("_PR_NotifyCondVar: cvar=%p", cvar));
+#endif
+ if (_PR_THREAD_CONDQ_PTR(q)->wait.cvar) {
+ if (NotifyThread(_PR_THREAD_CONDQ_PTR(q), me) == PR_TRUE)
+ break;
+ }
+ q = q->next;
+ }
+ _PR_CVAR_UNLOCK(cvar);
+
+ if ( !_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSON(is);
+
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+}
+
+/*
+** Cndition variable debugging log info.
+*/
+PRUint32 _PR_CondVarToString(PRCondVar *cvar, char *buf, PRUint32 buflen)
+{
+ PRUint32 nb;
+
+ if (cvar->lock->owner) {
+ nb = PR_snprintf(buf, buflen, "[%p] owner=%ld[%p]",
+ cvar, cvar->lock->owner->id, cvar->lock->owner);
+ } else {
+ nb = PR_snprintf(buf, buflen, "[%p]", cvar);
+ }
+ return nb;
+}
+
+/*
+** Expire condition variable waits that are ready to expire. "now" is the current
+** time.
+*/
+void _PR_ClockInterrupt(void)
+{
+ PRThread *thread, *me = _PR_MD_CURRENT_THREAD();
+ _PRCPU *cpu = me->cpu;
+ PRIntervalTime elapsed, now;
+
+ PR_ASSERT(_PR_MD_GET_INTSOFF() != 0);
+ /* Figure out how much time elapsed since the last clock tick */
+ now = PR_IntervalNow();
+ elapsed = now - cpu->last_clock;
+ cpu->last_clock = now;
+
+#ifndef XP_MAC
+ PR_LOG(_pr_clock_lm, PR_LOG_MAX,
+ ("ExpireWaits: elapsed=%lld usec", elapsed));
+#endif
+
+ while(1) {
+ _PR_SLEEPQ_LOCK(cpu);
+ if (_PR_SLEEPQ(cpu).next == &_PR_SLEEPQ(cpu)) {
+ _PR_SLEEPQ_UNLOCK(cpu);
+ break;
+ }
+
+ thread = _PR_THREAD_PTR(_PR_SLEEPQ(cpu).next);
+
+ if (elapsed < thread->sleep) {
+ thread->sleep -= elapsed;
+ _PR_SLEEPQMAX(thread->cpu) -= elapsed;
+ PR_ASSERT((PRInt32)(thread->sleep) >= 0);
+ _PR_SLEEPQ_UNLOCK(cpu);
+ break;
+ }
+ _PR_SLEEPQ_UNLOCK(cpu);
+
+ PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread));
+
+ _PR_THREAD_LOCK(thread);
+
+ /*
+ ** Consume this sleeper's amount of elapsed time from the elapsed
+ ** time value. The next remaining piece of elapsed time will be
+ ** available for the next sleeping thread's timer.
+ */
+ _PR_SLEEPQ_LOCK(cpu);
+ PR_ASSERT(!(thread->flags & _PR_ON_PAUSEQ));
+ if (thread->flags & _PR_ON_SLEEPQ) {
+ _PR_DEL_SLEEPQ(thread, PR_FALSE);
+ elapsed -= thread->sleep;
+ _PR_SLEEPQ_UNLOCK(cpu);
+ } else {
+ /* Thread was already handled; Go get another one */
+ _PR_SLEEPQ_UNLOCK(cpu);
+ _PR_THREAD_UNLOCK(thread);
+ continue;
+ }
+
+ /* Notify the thread waiting on the condition variable */
+ if (thread->flags & _PR_SUSPENDING) {
+ PR_ASSERT((thread->state == _PR_IO_WAIT) ||
+ (thread->state == _PR_COND_WAIT));
+ /*
+ ** Thread is suspended and its condition timeout
+ ** expired. Transfer thread from sleepQ to suspendQ.
+ */
+ thread->wait.cvar = NULL;
+ _PR_MISCQ_LOCK(cpu);
+ thread->state = _PR_SUSPENDED;
+ _PR_ADD_SUSPENDQ(thread, cpu);
+ _PR_MISCQ_UNLOCK(cpu);
+ } else {
+ if (thread->wait.cvar) {
+ PRThreadPriority pri;
+
+ /* Do work very similar to what NotifyThread does */
+ PR_ASSERT( !_PR_IS_NATIVE_THREAD(thread) );
+
+ /* Make thread runnable */
+ pri = thread->priority;
+ thread->state = _PR_RUNNABLE;
+ PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
+
+ PR_ASSERT(thread->cpu == cpu);
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(thread, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+
+ if (pri > me->priority)
+ _PR_SET_RESCHED_FLAG();
+
+ thread->wait.cvar = NULL;
+
+ _PR_MD_WAKEUP_WAITER(thread);
+
+ } else if (thread->io_pending == PR_TRUE) {
+ /* Need to put IO sleeper back on runq */
+ int pri = thread->priority;
+
+ thread->io_suspended = PR_TRUE;
+
+ PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
+ PR_ASSERT(thread->cpu == cpu);
+ thread->state = _PR_RUNNABLE;
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(thread, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ }
+ }
+ _PR_THREAD_UNLOCK(thread);
+ }
+}
+
+/************************************************************************/
+
+/*
+** Create a new condition variable.
+** "lock" is the lock to use with the condition variable.
+**
+** Condition variables are synchronization objects that threads can use
+** to wait for some condition to occur.
+**
+** This may fail if memory is tight or if some operating system resource
+** is low.
+*/
+PR_IMPLEMENT(PRCondVar*) PR_NewCondVar(PRLock *lock)
+{
+ PRCondVar *cvar;
+
+ PR_ASSERT(lock != NULL);
+
+ cvar = PR_NEWZAP(PRCondVar);
+ if (cvar) {
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ if(_PR_MD_NEW_CV(&cvar->md)) {
+ PR_DELETE(cvar);
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ return NULL;
+ }
+#endif
+ if (_PR_MD_NEW_LOCK(&(cvar->ilock)) == PR_FAILURE) {
+ PR_DELETE(cvar);
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ return NULL;
+ }
+ cvar->lock = lock;
+ PR_INIT_CLIST(&cvar->condQ);
+
+ } else {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ }
+ return cvar;
+}
+
+/*
+** Destroy a condition variable. There must be no thread
+** waiting on the condvar. The caller is responsible for guaranteeing
+** that the condvar is no longer in use.
+**
+*/
+PR_IMPLEMENT(void) PR_DestroyCondVar(PRCondVar *cvar)
+{
+ PR_ASSERT(cvar->condQ.next == &cvar->condQ);
+
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ _PR_MD_FREE_CV(&cvar->md);
+#endif
+ _PR_MD_FREE_LOCK(&(cvar->ilock));
+
+ PR_DELETE(cvar);
+}
+
+/*
+** Wait for a notify on the condition variable. Sleep for "tiemout" amount
+** of ticks (if "timeout" is zero then the sleep is indefinite). While
+** the thread is waiting it unlocks lock. When the wait has
+** finished the thread regains control of the condition variable after
+** locking the associated lock.
+**
+** The thread waiting on the condvar will be resumed when the condvar is
+** notified (assuming the thread is the next in line to receive the
+** notify) or when the timeout elapses.
+**
+** Returns PR_FAILURE if the caller has not locked the lock associated
+** with the condition variable or the thread has been interrupted.
+*/
+extern PRThread *suspendAllThread;
+PR_IMPLEMENT(PRStatus) PR_WaitCondVar(PRCondVar *cvar, PRIntervalTime timeout)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(cvar->lock->owner == me);
+ PR_ASSERT(me != suspendAllThread);
+ if (cvar->lock->owner != me) return PR_FAILURE;
+
+ return _PR_WaitCondVar(me, cvar, cvar->lock, timeout);
+}
+
+/*
+** Notify the highest priority thread waiting on the condition
+** variable. If a thread is waiting on the condition variable (using
+** PR_Wait) then it is awakened and begins waiting on the lock.
+*/
+PR_IMPLEMENT(PRStatus) PR_NotifyCondVar(PRCondVar *cvar)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(cvar->lock->owner == me);
+ PR_ASSERT(me != suspendAllThread);
+ if (cvar->lock->owner != me) return PR_FAILURE;
+
+ _PR_NotifyCondVar(cvar, me);
+ return PR_SUCCESS;
+}
+
+/*
+** Notify all of the threads waiting on the condition variable. All of
+** threads are notified in turn. The highest priority thread will
+** probably acquire the lock.
+*/
+PR_IMPLEMENT(PRStatus) PR_NotifyAllCondVar(PRCondVar *cvar)
+{
+ PRCList *q;
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(cvar->lock->owner == me);
+ if (cvar->lock->owner != me) return PR_FAILURE;
+
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ _PR_MD_NOTIFYALL_CV(&cvar->md, &cvar->lock->ilock);
+ return PR_SUCCESS;
+#else /* _PR_GLOBAL_THREADS_ONLY */
+ if ( !_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(is);
+ _PR_CVAR_LOCK(cvar);
+ q = cvar->condQ.next;
+ while (q != &cvar->condQ) {
+ PR_LOG(_pr_cvar_lm, PR_LOG_MIN, ("PR_NotifyAll: cvar=%p", cvar));
+ NotifyThread(_PR_THREAD_CONDQ_PTR(q), me);
+ q = q->next;
+ }
+ _PR_CVAR_UNLOCK(cvar);
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSON(is);
+
+ return PR_SUCCESS;
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+}
+
+
+/*********************************************************************/
+/*********************************************************************/
+/********************ROUTINES FOR DCE EMULATION***********************/
+/*********************************************************************/
+/*********************************************************************/
+#include "prpdce.h"
+
+PR_IMPLEMENT(PRCondVar*) PRP_NewNakedCondVar(void)
+{
+ PRCondVar *cvar = PR_NEWZAP(PRCondVar);
+ if (NULL != cvar)
+ {
+ if (_PR_MD_NEW_LOCK(&(cvar->ilock)) == PR_FAILURE)
+ {
+ PR_DELETE(cvar); cvar = NULL;
+ }
+ else
+ {
+ PR_INIT_CLIST(&cvar->condQ);
+ cvar->lock = _PR_NAKED_CV_LOCK;
+ }
+
+ }
+ return cvar;
+}
+
+PR_IMPLEMENT(void) PRP_DestroyNakedCondVar(PRCondVar *cvar)
+{
+ PR_ASSERT(cvar->condQ.next == &cvar->condQ);
+ PR_ASSERT(_PR_NAKED_CV_LOCK == cvar->lock);
+
+ _PR_MD_FREE_LOCK(&(cvar->ilock));
+
+ PR_DELETE(cvar);
+}
+
+PR_IMPLEMENT(PRStatus) PRP_NakedWait(
+ PRCondVar *cvar, PRLock *lock, PRIntervalTime timeout)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PR_ASSERT(_PR_NAKED_CV_LOCK == cvar->lock);
+ return _PR_WaitCondVar(me, cvar, lock, timeout);
+} /* PRP_NakedWait */
+
+PR_IMPLEMENT(PRStatus) PRP_NakedNotify(PRCondVar *cvar)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PR_ASSERT(_PR_NAKED_CV_LOCK == cvar->lock);
+
+ _PR_NotifyCondVar(cvar, me);
+
+ return PR_SUCCESS;
+} /* PRP_NakedNotify */
+
+PR_IMPLEMENT(PRStatus) PRP_NakedBroadcast(PRCondVar *cvar)
+{
+ PRCList *q;
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PR_ASSERT(_PR_NAKED_CV_LOCK == cvar->lock);
+
+ if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is);
+ _PR_MD_LOCK( &(cvar->ilock) );
+ q = cvar->condQ.next;
+ while (q != &cvar->condQ) {
+ PR_LOG(_pr_cvar_lm, PR_LOG_MIN, ("PR_NotifyAll: cvar=%p", cvar));
+ NotifyThread(_PR_THREAD_CONDQ_PTR(q), me);
+ q = q->next;
+ }
+ _PR_MD_UNLOCK( &(cvar->ilock) );
+ if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSON(is);
+
+ return PR_SUCCESS;
+} /* PRP_NakedBroadcast */
+
diff --git a/pr/src/threads/combined/prulock.c b/pr/src/threads/combined/prulock.c
new file mode 100644
index 00000000..0ec89fc9
--- /dev/null
+++ b/pr/src/threads/combined/prulock.c
@@ -0,0 +1,420 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+
+void _PR_InitLocks(void)
+{
+ _PR_MD_INIT_LOCKS();
+}
+
+/*
+** Deal with delayed interrupts/requested reschedule during interrupt
+** re-enables.
+*/
+void _PR_IntsOn(_PRCPU *cpu)
+{
+ PRUintn missed, pri, i;
+ _PRInterruptTable *it;
+ PRThread *me;
+
+ PR_ASSERT(cpu); /* Global threads don't have CPUs */
+ PR_ASSERT(_PR_MD_GET_INTSOFF() > 0);
+ me = _PR_MD_CURRENT_THREAD();
+#if !defined(XP_MAC)
+ PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
+#endif
+
+ /*
+ ** Process delayed interrupts. This logic is kinda scary because we
+ ** need to avoid losing an interrupt (it's ok to delay an interrupt
+ ** until later).
+ **
+ ** There are two missed state words. _pr_ints.where indicates to the
+ ** interrupt handler which state word is currently safe for
+ ** modification.
+ **
+ ** This code scans both interrupt state words, using the where flag
+ ** to indicate to the interrupt which state word is safe for writing.
+ ** If an interrupt comes in during a scan the other word will be
+ ** modified. This modification will be noticed during the next
+ ** iteration of the loop or during the next call to this routine.
+ */
+ for (i = 0; i < 2; i++) {
+ cpu->where = (1 - i);
+ missed = cpu->u.missed[i];
+ if (missed != 0) {
+ cpu->u.missed[i] = 0;
+ for (it = _pr_interruptTable; it->name; it++) {
+ if (missed & it->missed_bit) {
+#ifndef XP_MAC
+ PR_LOG(_pr_sched_lm, PR_LOG_MIN,
+ ("IntsOn[0]: %s intr", it->name));
+#endif
+ (*it->handler)();
+ }
+ }
+ }
+ }
+
+ if (cpu->u.missed[3] != 0) {
+ _PRCPU *cpu;
+
+ _PR_THREAD_LOCK(me);
+ me->state = _PR_RUNNABLE;
+ pri = me->priority;
+
+ cpu = me->cpu;
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(me, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ _PR_THREAD_UNLOCK(me);
+ _PR_MD_SWITCH_CONTEXT(me);
+ }
+}
+
+/*
+** Assign an idle lock to the first runnable waiting thread. Skip over
+** threads that are trying to be suspended
+** Note: Caller must hold _PR_LOCK_LOCK()
+*/
+PRThread * _PR_AssignLock(PRLock *lock)
+{
+ PRThread *t = NULL;
+ PRThread *me;
+ PRCList *q;
+ PRThreadPriority pri;
+
+ q = lock->waitQ.next;
+ PR_ASSERT(q != &lock->waitQ);
+ while (q != &lock->waitQ) {
+ /* Assign lock to first waiter */
+ t = _PR_THREAD_CONDQ_PTR(lock->waitQ.next);
+
+ /*
+ ** We are about to change the thread's state to runnable and for local
+ ** threads, we are going to assign a cpu to it. So, protect thread's
+ ** data structure.
+ */
+ _PR_THREAD_LOCK(t);
+
+ if (t->flags & _PR_SUSPENDING) {
+ q = q->next;
+ _PR_THREAD_UNLOCK(t);
+ t = NULL;
+ continue;
+ }
+
+ /* Found a thread to give the lock to */
+ PR_ASSERT(t->state == _PR_LOCK_WAIT);
+ PR_ASSERT(t->wait.lock == lock);
+ pri = t->priority;
+ t->wait.lock = 0;
+ PR_REMOVE_LINK(&t->waitQLinks); /* take it off lock's waitQ */
+
+ /* Lock inherits the thread's priority */
+ lock->priority = pri;
+ lock->boostPriority = PR_PRIORITY_LOW;
+ lock->owner = t;
+
+ /* Add the granted lock to this owning thread's lock list */
+ PR_APPEND_LINK(&lock->links, &t->lockList);
+
+ /*
+ ** If the new owner of the lock is a native thread, nothing else to do
+ ** except to wake it up by calling the machine dependent wakeup routine.
+ **
+ ** If the new owner is a local thread, we need to assign it a cpu and
+ ** put the thread on that cpu's run queue. There are two cases to take care
+ ** of. If the currently running thread is also a local thread, we just
+ ** assign our own cpu to that thread and put it on the cpu's run queue.
+ ** If the the currently running thread is a native thread, we assign the
+ ** primordial cpu to it (on NT, MD_WAKEUP handles the cpu assignment).
+ */
+
+ if ( !_PR_IS_NATIVE_THREAD(t) ) {
+
+ t->state = _PR_RUNNABLE;
+
+ me = _PR_MD_CURRENT_THREAD();
+
+ _PR_AddThreadToRunQ(me, t);
+ _PR_THREAD_UNLOCK(t);
+ } else {
+ t->state = _PR_RUNNING;
+ _PR_THREAD_UNLOCK(t);
+ }
+ _PR_MD_WAKEUP_WAITER(t);
+ break;
+ }
+ return t;
+}
+
+/************************************************************************/
+
+
+PR_IMPLEMENT(PRLock*) PR_NewLock(void)
+{
+ PRLock *lock;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ lock = PR_NEWZAP(PRLock);
+ if (lock) {
+ if (_PR_MD_NEW_LOCK(&lock->ilock) == PR_FAILURE) {
+ PR_DELETE(lock);
+ return(NULL);
+ }
+ PR_INIT_CLIST(&lock->links);
+ PR_INIT_CLIST(&lock->waitQ);
+ }
+ return lock;
+}
+
+/*
+** Destroy the given lock "lock". There is no point in making this race
+** free because if some other thread has the pointer to this lock all
+** bets are off.
+*/
+PR_IMPLEMENT(void) PR_DestroyLock(PRLock *lock)
+{
+ PR_ASSERT(lock->owner == 0);
+ _PR_MD_FREE_LOCK(&lock->ilock);
+ PR_DELETE(lock);
+}
+
+extern PRThread *suspendAllThread;
+/*
+** Lock the lock.
+*/
+PR_IMPLEMENT(void) PR_Lock(PRLock *lock)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRIntn is;
+ PRThread *t;
+ PRCList *q;
+
+ PR_ASSERT(me != suspendAllThread);
+#if !defined(XP_MAC)
+ PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
+#endif
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ PR_ASSERT(lock->owner != me);
+ _PR_MD_LOCK(&lock->ilock);
+ lock->owner = me;
+ return;
+#else /* _PR_GLOBAL_THREADS_ONLY */
+
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(is);
+
+ PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
+
+ _PR_LOCK_LOCK(lock);
+ if (lock->owner == 0) {
+ /* Just got the lock */
+ lock->owner = me;
+ lock->priority = me->priority;
+ /* Add the granted lock to this owning thread's lock list */
+ PR_APPEND_LINK(&lock->links, &me->lockList);
+ _PR_LOCK_UNLOCK(lock);
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_FAST_INTSON(is);
+ return;
+ }
+
+ /* If this thread already owns this lock, then it is a deadlock */
+ PR_ASSERT(lock->owner != me);
+
+ PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
+
+#if 0
+ if (me->priority > lock->owner->priority) {
+ /*
+ ** Give the lock owner a priority boost until we get the
+ ** lock. Record the priority we boosted it to.
+ */
+ lock->boostPriority = me->priority;
+ _PR_SetThreadPriority(lock->owner, me->priority);
+ }
+#endif
+
+ /*
+ Add this thread to the asked for lock's list of waiting threads. We
+ add this thread thread in the right priority order so when the unlock
+ occurs, the thread with the higher priority will get the lock.
+ */
+ /* Sort thread into lock's waitQ at appropriate point */
+ q = lock->waitQ.next;
+
+ /* Now scan the list for where to insert this entry */
+ while (q != &lock->waitQ) {
+ t = _PR_THREAD_CONDQ_PTR(lock->waitQ.next);
+ if (me->priority > t->priority) {
+ /* Found a lower priority thread to insert in front of */
+ break;
+ }
+ q = q->next;
+ }
+ PR_INSERT_BEFORE(&me->waitQLinks, q);
+
+ /*
+ Now grab the threadLock since we are about to change the state. We have
+ to do this since a PR_Suspend or PR_SetThreadPriority type call that takes
+ a PRThread* as an argument could be changing the state of this thread from
+ a thread running on a different cpu.
+ */
+
+ _PR_THREAD_LOCK(me);
+ me->state = _PR_LOCK_WAIT;
+ me->wait.lock = lock;
+ _PR_THREAD_UNLOCK(me);
+
+ _PR_LOCK_UNLOCK(lock);
+
+ _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
+
+ /* When we are here after the context switch, we better own the lock */
+ PR_ASSERT(lock->owner == me);
+
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_FAST_INTSON(is);
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+}
+
+/*
+** Unlock the lock.
+*/
+PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock *lock)
+{
+ PRCList *q;
+ PRThreadPriority pri, boost;
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(lock->owner == me);
+ PR_ASSERT(me != suspendAllThread);
+#if !defined(XP_MAC)
+ PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
+#endif
+ if (lock->owner != me) {
+ return PR_FAILURE;
+ }
+
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ lock->owner = 0;
+ _PR_MD_UNLOCK(&lock->ilock);
+ return PR_SUCCESS;
+#else /* _PR_GLOBAL_THREADS_ONLY */
+
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(is);
+ _PR_LOCK_LOCK(lock);
+
+ /* Remove the lock from the owning thread's lock list */
+ PR_REMOVE_LINK(&lock->links);
+ pri = lock->priority;
+ boost = lock->boostPriority;
+ if (boost > pri) {
+ /*
+ ** We received a priority boost during the time we held the lock.
+ ** We need to figure out what priority to move to by scanning
+ ** down our list of lock's that we are still holding and using
+ ** the highest boosted priority found.
+ */
+ q = me->lockList.next;
+ while (q != &me->lockList) {
+ PRLock *ll = _PR_LOCK_PTR(q);
+ if (ll->boostPriority > pri) {
+ pri = ll->boostPriority;
+ }
+ q = q->next;
+ }
+ if (pri != me->priority) {
+ _PR_SetThreadPriority(me, pri);
+ }
+ }
+
+ /* Assign the lock to the first waiting thread */
+ q = lock->waitQ.next;
+ if (q == &lock->waitQ) {
+ /* Nobody wants the lock right now */
+ lock->boostPriority = PR_PRIORITY_LOW;
+ lock->owner = 0;
+ } else {
+ if (_PR_AssignLock(lock) == NULL) {
+ /* no eligible thread to assign to */
+ lock->boostPriority = PR_PRIORITY_LOW;
+ lock->owner = 0;
+ }
+ }
+ _PR_LOCK_UNLOCK(lock);
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSON(is);
+ return PR_SUCCESS;
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+}
+
+/*
+** Test and then lock the lock if it's not already locked by some other
+** thread. Return PR_FALSE if some other thread owned the lock at the
+** time of the call.
+*/
+PR_IMPLEMENT(PRBool) PR_TestAndLock(PRLock *lock)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRBool rv = PR_FALSE;
+ PRIntn is;
+
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ is = _PR_MD_TEST_AND_LOCK(&lock->ilock);
+ if (is == 0) {
+ lock->owner = me;
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+#else /* _PR_GLOBAL_THREADS_ONLY */
+
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(is);
+
+ _PR_LOCK_LOCK(lock);
+ if (lock->owner == 0) {
+ /* Just got the lock */
+ lock->owner = me;
+ lock->priority = me->priority;
+ /* Add the granted lock to this owning thread's lock list */
+ PR_APPEND_LINK(&lock->links, &me->lockList);
+ rv = PR_TRUE;
+ }
+ _PR_LOCK_UNLOCK(lock);
+
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSON(is);
+ return rv;
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+}
+
+/************************************************************************/
+/************************************************************************/
+/***********************ROUTINES FOR DCE EMULATION***********************/
+/************************************************************************/
+/************************************************************************/
+PR_IMPLEMENT(PRStatus) PRP_TryLock(PRLock *lock)
+ { return (PR_TestAndLock(lock)) ? PR_SUCCESS : PR_FAILURE; }
diff --git a/pr/src/threads/combined/prustack.c b/pr/src/threads/combined/prustack.c
new file mode 100644
index 00000000..09690806
--- /dev/null
+++ b/pr/src/threads/combined/prustack.c
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+#ifdef XP_UNIX
+#include <sys/mman.h>
+#endif
+
+/* List of free stack virtual memory chunks */
+PRLock *_pr_stackLock;
+PRCList _pr_freeStacks = PR_INIT_STATIC_CLIST(&_pr_freeStacks);
+PRIntn _pr_numFreeStacks;
+PRIntn _pr_maxFreeStacks = 4;
+
+#ifdef DEBUG
+/*
+** A variable that can be set via the debugger...
+*/
+PRBool _pr_debugStacks = PR_FALSE;
+#endif
+
+/* How much space to leave between the stacks, at each end */
+#define REDZONE (2 << _pr_pageShift)
+
+#define _PR_THREAD_STACK_PTR(_qp) \
+ ((PRThreadStack*) ((char*) (_qp) - offsetof(PRThreadStack,links)))
+
+void _PR_InitStacks(void)
+{
+ _pr_stackLock = PR_NewLock();
+}
+
+/*
+** Allocate a stack for a thread.
+*/
+PRThreadStack *_PR_NewStack(PRUint32 stackSize)
+{
+ PRCList *qp;
+ PRThreadStack *ts;
+ PRThread *thr;
+
+ /*
+ ** Trim the list of free stacks. Trim it backwards, tossing out the
+ ** oldest stack found first (this way more recent stacks have a
+ ** chance of being present in the data cache).
+ */
+ PR_Lock(_pr_stackLock);
+ qp = _pr_freeStacks.prev;
+ while ((_pr_numFreeStacks > _pr_maxFreeStacks) && (qp != &_pr_freeStacks)) {
+ ts = _PR_THREAD_STACK_PTR(qp);
+ thr = _PR_THREAD_STACK_TO_PTR(ts);
+ qp = qp->prev;
+ /*
+ * skip stacks which are still being used
+ */
+ if (thr->no_sched)
+ continue;
+ PR_REMOVE_LINK(&ts->links);
+
+ /* Give platform OS to clear out the stack for debugging */
+ _PR_MD_CLEAR_STACK(ts);
+
+ _pr_numFreeStacks--;
+ PR_DestroySegment(ts->seg);
+ PR_DELETE(ts);
+ }
+
+ /*
+ ** Find a free thread stack. This searches the list of free'd up
+ ** virtually mapped thread stacks.
+ */
+ qp = _pr_freeStacks.next;
+ ts = 0;
+ while (qp != &_pr_freeStacks) {
+ ts = _PR_THREAD_STACK_PTR(qp);
+ thr = _PR_THREAD_STACK_TO_PTR(ts);
+ qp = qp->next;
+ /*
+ * skip stacks which are still being used
+ */
+ if ((!(thr->no_sched)) && ((ts->allocSize - 2*REDZONE) >= stackSize)) {
+ /*
+ ** Found a stack that is not in use and is big enough. Change
+ ** stackSize to fit it.
+ */
+ stackSize = ts->allocSize - 2*REDZONE;
+ PR_REMOVE_LINK(&ts->links);
+ _pr_numFreeStacks--;
+ ts->links.next = 0;
+ ts->links.prev = 0;
+ PR_Unlock(_pr_stackLock);
+ goto done;
+ }
+ ts = 0;
+ }
+ PR_Unlock(_pr_stackLock);
+
+ if (!ts) {
+ /* Make a new thread stack object. */
+ ts = PR_NEWZAP(PRThreadStack);
+ if (!ts) {
+ return NULL;
+ }
+
+ /*
+ ** Assign some of the virtual space to the new stack object. We
+ ** may not get that piece of VM, but if nothing else we will
+ ** advance the pointer so we don't collide (unless the OS screws
+ ** up).
+ */
+ ts->allocSize = stackSize + 2*REDZONE;
+ ts->seg = PR_NewSegment(ts->allocSize, 0);
+ if (!ts->seg) {
+ PR_DELETE(ts);
+ return NULL;
+ }
+ }
+
+ done:
+ ts->allocBase = ts->seg->vaddr;
+ ts->flags = _PR_STACK_MAPPED;
+ ts->stackSize = stackSize;
+
+#ifdef HAVE_STACK_GROWING_UP
+ ts->stackTop = ts->allocBase + REDZONE;
+ ts->stackBottom = ts->stackTop + stackSize;
+#else
+ ts->stackBottom = ts->allocBase + REDZONE;
+ ts->stackTop = ts->stackBottom + stackSize;
+#endif
+
+ PR_LOG(_pr_thread_lm, PR_LOG_NOTICE,
+ ("thread stack: base=0x%x limit=0x%x bottom=0x%x top=0x%x\n",
+ ts->allocBase, ts->allocBase + ts->allocSize - 1,
+ ts->allocBase + REDZONE,
+ ts->allocBase + REDZONE + stackSize - 1));
+
+ _PR_MD_INIT_STACK(ts,REDZONE);
+
+ return ts;
+}
+
+/*
+** Free the stack for the current thread
+*/
+void _PR_FreeStack(PRThreadStack *ts)
+{
+ if (!ts) {
+ return;
+ }
+ if (ts->flags & _PR_STACK_PRIMORDIAL) {
+ PR_DELETE(ts);
+ return;
+ }
+
+ /*
+ ** Put the stack on the free list. This is done because we are still
+ ** using the stack. Next time a thread is created we will trim the
+ ** list down; it's safe to do it then because we will have had to
+ ** context switch to a live stack before another thread can be
+ ** created.
+ */
+ PR_Lock(_pr_stackLock);
+ PR_APPEND_LINK(&ts->links, _pr_freeStacks.prev);
+ _pr_numFreeStacks++;
+ PR_Unlock(_pr_stackLock);
+}
diff --git a/pr/src/threads/combined/pruthr.c b/pr/src/threads/combined/pruthr.c
new file mode 100644
index 00000000..7baa6534
--- /dev/null
+++ b/pr/src/threads/combined/pruthr.c
@@ -0,0 +1,1794 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+#include <signal.h>
+#include <string.h>
+
+/* _pr_activeLock protects the following global variables */
+PRLock *_pr_activeLock;
+PRUintn _pr_primordialExitCount; /* In PR_Cleanup(), the primordial thread
+ * waits until all other user (non-system)
+ * threads have terminated before it exits.
+ * So whenever we decrement _pr_userActive,
+ * it is compared with
+ * _pr_primordialExitCount.
+ * If the primordial thread is a system
+ * thread, then _pr_primordialExitCount
+ * is 0. If the primordial thread is
+ * itself a user thread, then
+ * _pr_primordialThread is 1.
+ */
+PRCondVar *_pr_primordialExitCVar; /* When _pr_userActive is decremented to
+ * _pr_primordialExitCount, this condition
+ * variable is notified.
+ */
+
+PRLock *_pr_deadQLock;
+PRUint32 _pr_numNativeDead;
+PRUint32 _pr_numUserDead;
+PRCList _pr_deadNativeQ;
+PRCList _pr_deadUserQ;
+
+PRUint32 _pr_join_counter;
+
+PRUint32 _pr_local_threads;
+PRUint32 _pr_global_threads;
+
+PRBool suspendAllOn = PR_FALSE;
+PRThread *suspendAllThread = NULL;
+
+extern PRCList _pr_active_global_threadQ;
+extern PRCList _pr_active_local_threadQ;
+extern _PRCPU *_pr_primordialCPU;
+
+static void _PR_DecrActiveThreadCount(PRThread *thread);
+static PRThread *_PR_AttachThread(PRThreadType, PRThreadPriority, PRThreadStack *);
+static void _PR_InitializeNativeStack(PRThreadStack *ts);
+static void _PR_InitializeRecycledThread(PRThread *thread);
+
+void _PR_InitThreads(PRThreadType type, PRThreadPriority priority,
+ PRUintn maxPTDs)
+{
+#if defined(XP_MAC)
+#pragma unused (maxPTDs)
+#endif
+
+ PRThread *thread;
+ PRThreadStack *stack;
+
+ _pr_terminationCVLock = PR_NewLock();
+ _pr_activeLock = PR_NewLock();
+
+#ifndef HAVE_CUSTOM_USER_THREADS
+ stack = PR_NEWZAP(PRThreadStack);
+#ifdef HAVE_STACK_GROWING_UP
+ stack->stackTop = (char*) ((((long)&type) >> _pr_pageShift)
+ << _pr_pageShift);
+#else
+#if defined(SOLARIS) || defined (UNIXWARE) && defined (USR_SVR4_THREADS)
+ stack->stackTop = (char*) &thread;
+#else
+ stack->stackTop = (char*) ((((long)&type + _pr_pageSize - 1)
+ >> _pr_pageShift) << _pr_pageShift);
+#endif
+#endif
+#else
+ /* If stack is NULL, we're using custom user threads like NT fibers. */
+ stack = PR_NEWZAP(PRThreadStack);
+ if (stack) {
+ stack->stackSize = 0;
+ _PR_InitializeNativeStack(stack);
+ }
+#endif /* HAVE_CUSTOM_USER_THREADS */
+
+ thread = _PR_AttachThread(type, priority, stack);
+ if (thread) {
+ _PR_MD_SET_CURRENT_THREAD(thread);
+
+ if (type == PR_SYSTEM_THREAD) {
+ thread->flags = _PR_SYSTEM;
+ _pr_systemActive++;
+ _pr_primordialExitCount = 0;
+ } else {
+ _pr_userActive++;
+ _pr_primordialExitCount = 1;
+ }
+ thread->no_sched = 1;
+ _pr_primordialExitCVar = PR_NewCondVar(_pr_activeLock);
+ }
+
+ if (!thread) PR_Abort();
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ thread->flags |= _PR_PRIMORDIAL | _PR_GLOBAL_SCOPE;
+#else
+ thread->flags |= _PR_PRIMORDIAL;
+#endif
+
+ /* Needs _PR_PRIMORDIAL flag set before calling _PR_MD_INIT_THREAD() */
+ if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) {
+ /*
+ * XXX do what?
+ */
+ }
+
+ if (_PR_IS_NATIVE_THREAD(thread)) {
+ PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_GLOBAL_THREADQ());
+ _pr_global_threads++;
+ } else {
+ PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_LOCAL_THREADQ());
+ _pr_local_threads++;
+ }
+
+ _pr_recycleThreads = 0;
+ _pr_deadQLock = PR_NewLock();
+ _pr_numNativeDead = 0;
+ _pr_numUserDead = 0;
+ PR_INIT_CLIST(&_pr_deadNativeQ);
+ PR_INIT_CLIST(&_pr_deadUserQ);
+}
+
+/*
+** Initialize a stack for a native thread
+*/
+static void _PR_InitializeNativeStack(PRThreadStack *ts)
+{
+ if( ts && (ts->stackTop == 0) ) {
+ ts->allocSize = ts->stackSize;
+
+ /*
+ ** Setup stackTop and stackBottom values.
+ */
+#ifdef HAVE_STACK_GROWING_UP
+ ts->allocBase = (char*) ((((long)&ts) >> _pr_pageShift)
+ << _pr_pageShift);
+ ts->stackBottom = ts->allocBase + ts->stackSize;
+ ts->stackTop = ts->allocBase;
+#else
+ ts->allocBase = (char*) ((((long)&ts + _pr_pageSize - 1)
+ >> _pr_pageShift) << _pr_pageShift);
+ ts->stackTop = ts->allocBase;
+ ts->stackBottom = ts->allocBase - ts->stackSize;
+#endif
+ }
+}
+
+void _PR_NotifyJoinWaiters(PRThread *thread)
+{
+ /*
+ ** Handle joinable threads. Change the state to waiting for join.
+ ** Remove from our run Q and put it on global waiting to join Q.
+ ** Notify on our "termination" condition variable so that joining
+ ** thread will know about our termination. Switch our context and
+ ** come back later on to continue the cleanup.
+ */
+ PR_ASSERT(thread == _PR_MD_CURRENT_THREAD());
+ if (thread->term != NULL) {
+ PR_Lock(_pr_terminationCVLock);
+ _PR_THREAD_LOCK(thread);
+ thread->state = _PR_JOIN_WAIT;
+ if ( !_PR_IS_NATIVE_THREAD(thread) ) {
+ _PR_MISCQ_LOCK(thread->cpu);
+ _PR_ADD_JOINQ(thread, thread->cpu);
+ _PR_MISCQ_UNLOCK(thread->cpu);
+ }
+ _PR_THREAD_UNLOCK(thread);
+ PR_NotifyCondVar(thread->term);
+ PR_Unlock(_pr_terminationCVLock);
+ _PR_MD_WAIT(thread, PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(thread->state != _PR_JOIN_WAIT);
+ }
+
+}
+
+/*
+ * Zero some of the data members of a recycled thread.
+ *
+ * Note that we can do this either when a dead thread is added to
+ * the dead thread queue or when it is reused. Here, we are doing
+ * this lazily, when the thread is reused in _PR_CreateThread().
+ */
+static void _PR_InitializeRecycledThread(PRThread *thread)
+{
+ /*
+ * Assert that the following data members are already zeroed
+ * by _PR_CleanupThread().
+ */
+#ifdef DEBUG
+ if (thread->privateData) {
+ unsigned int i;
+ for (i = 0; i < thread->tpdLength; i++) {
+ PR_ASSERT(thread->privateData[i] == NULL);
+ }
+ }
+#endif
+ PR_ASSERT(thread->dumpArg == 0 && thread->dump == 0);
+ PR_ASSERT(thread->numExits == 0 && thread->ptes == 0);
+ PR_ASSERT(thread->errorString == 0 && thread->errorStringSize == 0);
+
+ /* Reset data members in thread structure */
+ thread->errorCode = thread->osErrorCode = 0;
+ thread->io_pending = thread->io_suspended = PR_FALSE;
+ thread->environment = 0;
+ PR_INIT_CLIST(&thread->lockList);
+}
+
+PRStatus _PR_RecycleThread(PRThread *thread)
+{
+ if ( _PR_IS_NATIVE_THREAD(thread) &&
+ _PR_NUM_DEADNATIVE < _pr_recycleThreads) {
+ _PR_DEADQ_LOCK;
+ PR_APPEND_LINK(&thread->links, &_PR_DEADNATIVEQ);
+ _PR_INC_DEADNATIVE;
+ _PR_DEADQ_UNLOCK;
+ return (PR_SUCCESS);
+ } else if ( !_PR_IS_NATIVE_THREAD(thread) &&
+ _PR_NUM_DEADUSER < _pr_recycleThreads) {
+ _PR_DEADQ_LOCK;
+ PR_APPEND_LINK(&thread->links, &_PR_DEADUSERQ);
+ _PR_INC_DEADUSER;
+ _PR_DEADQ_UNLOCK;
+ return (PR_SUCCESS);
+ }
+ return (PR_FAILURE);
+}
+
+/*
+ * Decrement the active thread count, either _pr_systemActive or
+ * _pr_userActive, depending on whether the thread is a system thread
+ * or a user thread. If all the user threads, except possibly
+ * the primordial thread, have terminated, we notify the primordial
+ * thread of this condition.
+ *
+ * Since this function will lock _pr_activeLock, do not call this
+ * function while holding the _pr_activeLock lock, as this will result
+ * in a deadlock.
+ */
+
+static void
+_PR_DecrActiveThreadCount(PRThread *thread)
+{
+ PR_Lock(_pr_activeLock);
+ if (thread->flags & _PR_SYSTEM) {
+ _pr_systemActive--;
+ } else {
+ _pr_userActive--;
+ if (_pr_userActive == _pr_primordialExitCount) {
+ PR_NotifyCondVar(_pr_primordialExitCVar);
+ }
+ }
+ PR_Unlock(_pr_activeLock);
+}
+
+/*
+** Detach thread structure
+*/
+static void
+_PR_DetachThread(PRThread *thread)
+{
+ _MD_FREE_LOCK(&thread->threadLock);
+ PR_DELETE(thread);
+}
+
+void
+_PR_NativeDestroyThread(PRThread *thread)
+{
+ if(thread->term) {
+ PR_DestroyCondVar(thread->term);
+ thread->term = 0;
+ }
+
+ PR_DELETE(thread->stack);
+ _PR_DetachThread(thread);
+}
+
+void
+_PR_UserDestroyThread(PRThread *thread)
+{
+ if(thread->term) {
+ PR_DestroyCondVar(thread->term);
+ thread->term = 0;
+ }
+ if (NULL != thread->privateData)
+ {
+ PR_ASSERT(0 != thread->tpdLength);
+ PR_DELETE(thread->privateData);
+ thread->privateData = NULL;
+ thread->tpdLength = 0;
+ }
+ _MD_FREE_LOCK(&thread->threadLock);
+ if (thread->threadAllocatedOnStack == 1) {
+ _PR_MD_CLEAN_THREAD(thread);
+ /*
+ * Because the no_sched field is set, this thread/stack will
+ * will not be re-used until the flag is cleared by the thread
+ * we will context switch to.
+ */
+ _PR_FreeStack(thread->stack);
+ } else {
+#ifdef WINNT
+ _PR_MD_CLEAN_THREAD(thread);
+#else
+ /*
+ * This assertion does not apply to NT. On NT, every fiber,
+ * including the primordial thread, has its threadAllocatedOnStack
+ * equal to 0. Elsewhere, only the primordial thread has its
+ * threadAllocatedOnStack equal to 0.
+ */
+ PR_ASSERT(thread->flags & _PR_PRIMORDIAL);
+#endif
+ }
+}
+
+
+/*
+** Run a thread's start function. When the start function returns the
+** thread is done executing and no longer needs the CPU. If there are no
+** more user threads running then we can exit the program.
+*/
+void _PR_NativeRunThread(void *arg)
+{
+ PRThread *thread = (PRThread *)arg;
+
+ _PR_MD_SET_CURRENT_THREAD(thread);
+
+ _PR_MD_SET_CURRENT_CPU(NULL);
+
+ /* Set up the thread stack information */
+ _PR_InitializeNativeStack(thread->stack);
+
+ /* Set up the thread md information */
+ if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) {
+ /*
+ * thread failed to initialize itself, possibly due to
+ * failure to allocate per-thread resources
+ */
+ return;
+ }
+
+ while(1) {
+ thread->state = _PR_RUNNING;
+
+ if ( !_PR_IS_NATIVE_THREAD(thread)) _PR_SET_INTSOFF(0);
+
+ /*
+ * Add to list of active threads
+ */
+ PR_Lock(_pr_activeLock);
+ PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_GLOBAL_THREADQ());
+ _pr_global_threads++;
+ PR_Unlock(_pr_activeLock);
+
+ (*thread->startFunc)(thread->arg);
+
+ /*
+ * The following two assertions are meant for NT asynch io.
+ *
+ * The thread should have no asynch io in progress when it
+ * exits, otherwise the overlapped buffer, which is part of
+ * the thread structure, would become invalid.
+ */
+ PR_ASSERT(thread->io_pending == PR_FALSE);
+ /*
+ * This assertion enforces the programming guideline that
+ * if an io function times out or is interrupted, the thread
+ * should close the fd to force the asynch io to abort
+ * before it exits. Right now, closing the fd is the only
+ * way to clear the io_suspended flag.
+ */
+ PR_ASSERT(thread->io_suspended == PR_FALSE);
+
+ /*
+ * remove thread from list of active threads
+ */
+ PR_Lock(_pr_activeLock);
+ PR_REMOVE_LINK(&thread->active);
+ _pr_global_threads--;
+ PR_Unlock(_pr_activeLock);
+
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("thread exiting"));
+
+ /* All done, time to go away */
+ _PR_CleanupThread(thread);
+
+ _PR_NotifyJoinWaiters(thread);
+
+ _PR_DecrActiveThreadCount(thread);
+
+ thread->state = _PR_DEAD_STATE;
+
+ if (!_pr_recycleThreads || (_PR_RecycleThread(thread) ==
+ PR_FAILURE)) {
+ /*
+ * thread not recycled
+ * platform-specific thread exit processing
+ * - for stuff like releasing native-thread resources, etc.
+ */
+ _PR_MD_EXIT_THREAD(thread);
+ /*
+ * Free memory allocated for the thread
+ */
+ _PR_NativeDestroyThread(thread);
+ /*
+ * thread gone, cannot de-reference thread now
+ */
+ return;
+ }
+
+ /* Now wait for someone to activate us again... */
+ _PR_MD_WAIT(thread, PR_INTERVAL_NO_TIMEOUT);
+ }
+}
+
+void _PR_UserRunThread(void)
+{
+ PRThread *thread = _PR_MD_CURRENT_THREAD();
+ PRIntn is;
+
+ if (_MD_LAST_THREAD())
+ _MD_LAST_THREAD()->no_sched = 0;
+
+#ifdef HAVE_CUSTOM_USER_THREADS
+ if (thread->stack == NULL) {
+ thread->stack = PR_NEWZAP(PRThreadStack);
+ _PR_InitializeNativeStack(thread->stack);
+ }
+#endif /* HAVE_CUSTOM_USER_THREADS */
+
+ while(1) {
+ /* Run thread main */
+ if ( !_PR_IS_NATIVE_THREAD(thread)) _PR_SET_INTSOFF(0);
+
+ /*
+ * Add to list of active threads
+ */
+ if (!(thread->flags & _PR_IDLE_THREAD)) {
+ PR_Lock(_pr_activeLock);
+ PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_LOCAL_THREADQ());
+ _pr_local_threads++;
+ PR_Unlock(_pr_activeLock);
+ }
+
+ (*thread->startFunc)(thread->arg);
+
+ /*
+ * The following two assertions are meant for NT asynch io.
+ *
+ * The thread should have no asynch io in progress when it
+ * exits, otherwise the overlapped buffer, which is part of
+ * the thread structure, would become invalid.
+ */
+ PR_ASSERT(thread->io_pending == PR_FALSE);
+ /*
+ * This assertion enforces the programming guideline that
+ * if an io function times out or is interrupted, the thread
+ * should close the fd to force the asynch io to abort
+ * before it exits. Right now, closing the fd is the only
+ * way to clear the io_suspended flag.
+ */
+ PR_ASSERT(thread->io_suspended == PR_FALSE);
+
+ PR_Lock(_pr_activeLock);
+ /*
+ * remove thread from list of active threads
+ */
+ if (!(thread->flags & _PR_IDLE_THREAD)) {
+ PR_REMOVE_LINK(&thread->active);
+ _pr_local_threads--;
+ }
+ PR_Unlock(_pr_activeLock);
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("thread exiting"));
+
+ /* All done, time to go away */
+ _PR_CleanupThread(thread);
+
+ _PR_INTSOFF(is);
+
+ _PR_NotifyJoinWaiters(thread);
+
+ _PR_DecrActiveThreadCount(thread);
+
+ thread->state = _PR_DEAD_STATE;
+
+ if (!_pr_recycleThreads || (_PR_RecycleThread(thread) ==
+ PR_FAILURE)) {
+ /*
+ ** Destroy the thread resources
+ */
+ _PR_UserDestroyThread(thread);
+ }
+
+ /*
+ ** Find another user thread to run. This cpu has finished the
+ ** previous threads main and is now ready to run another thread.
+ */
+ {
+ PRInt32 is;
+ _PR_INTSOFF(is);
+ _PR_MD_SWITCH_CONTEXT(thread);
+ }
+
+ /* Will land here when we get scheduled again if we are recycling... */
+ }
+}
+
+void _PR_SetThreadPriority(PRThread *thread, PRThreadPriority newPri)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRIntn is;
+
+ if ( _PR_IS_NATIVE_THREAD(thread) ) {
+ _PR_MD_SET_PRIORITY(&(thread->md), newPri);
+ return;
+ }
+
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(is);
+ _PR_THREAD_LOCK(thread);
+ if (newPri != thread->priority) {
+ PRUintn oldPri;
+ _PRCPU *cpu = thread->cpu;
+
+ oldPri = thread->priority;
+ switch (thread->state) {
+ case _PR_RUNNING:
+ /* Change my priority */
+
+ _PR_RUNQ_LOCK(cpu);
+ thread->priority = newPri;
+ if (_PR_RUNQREADYMASK(cpu) >> (newPri + 1)) {
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_SET_RESCHED_FLAG();
+ }
+ _PR_RUNQ_UNLOCK(cpu);
+ break;
+
+ case _PR_RUNNABLE:
+
+ _PR_RUNQ_LOCK(cpu);
+ /* Move to different runQ */
+ _PR_DEL_RUNQ(thread);
+ thread->priority = newPri;
+ PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
+ _PR_ADD_RUNQ(thread, cpu, newPri);
+ _PR_RUNQ_UNLOCK(cpu);
+
+ if (newPri > me->priority) {
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_SET_RESCHED_FLAG();
+ }
+
+ break;
+
+ case _PR_LOCK_WAIT:
+ case _PR_COND_WAIT:
+ case _PR_IO_WAIT:
+ case _PR_SUSPENDED:
+
+ thread->priority = newPri;
+ break;
+ }
+ }
+ _PR_THREAD_UNLOCK(thread);
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSON(is);
+}
+
+/*
+** Suspend the named thread and copy its gc registers into regBuf
+*/
+static void _PR_Suspend(PRThread *thread)
+{
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(thread != me);
+ PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread) || (!thread->cpu));
+
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(is);
+ _PR_THREAD_LOCK(thread);
+ switch (thread->state) {
+ case _PR_RUNNABLE:
+ if (!_PR_IS_NATIVE_THREAD(thread)) {
+ _PR_RUNQ_LOCK(thread->cpu);
+ _PR_DEL_RUNQ(thread);
+ _PR_RUNQ_UNLOCK(thread->cpu);
+
+ _PR_MISCQ_LOCK(thread->cpu);
+ _PR_ADD_SUSPENDQ(thread, thread->cpu);
+ _PR_MISCQ_UNLOCK(thread->cpu);
+ } else {
+ /*
+ * Only LOCAL threads are suspended by _PR_Suspend
+ */
+ PR_ASSERT(0);
+ }
+ thread->state = _PR_SUSPENDED;
+ break;
+
+ case _PR_RUNNING:
+ /*
+ * The thread being suspended should be a LOCAL thread with
+ * _pr_numCPUs == 1. Hence, the thread cannot be in RUNNING state
+ */
+ PR_ASSERT(0);
+ break;
+
+ case _PR_LOCK_WAIT:
+ case _PR_IO_WAIT:
+ case _PR_COND_WAIT:
+ if (_PR_IS_NATIVE_THREAD(thread)) {
+ _PR_MD_SUSPEND_THREAD(thread);
+ }
+ thread->flags |= _PR_SUSPENDING;
+ break;
+
+ default:
+ PR_Abort();
+ }
+ _PR_THREAD_UNLOCK(thread);
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSON(is);
+}
+
+static void _PR_Resume(PRThread *thread)
+{
+ PRThreadPriority pri;
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(is);
+ _PR_THREAD_LOCK(thread);
+ switch (thread->state) {
+ case _PR_SUSPENDED:
+ thread->state = _PR_RUNNABLE;
+ thread->flags &= ~_PR_SUSPENDING;
+ if (!_PR_IS_NATIVE_THREAD(thread)) {
+ _PR_MISCQ_LOCK(thread->cpu);
+ _PR_DEL_SUSPENDQ(thread);
+ _PR_MISCQ_UNLOCK(thread->cpu);
+
+ pri = thread->priority;
+
+ _PR_RUNQ_LOCK(thread->cpu);
+ _PR_ADD_RUNQ(thread, thread->cpu, pri);
+ _PR_RUNQ_UNLOCK(thread->cpu);
+
+ if (pri > _PR_MD_CURRENT_THREAD()->priority) {
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_SET_RESCHED_FLAG();
+ }
+ } else {
+ PR_ASSERT(0);
+ }
+ break;
+
+ case _PR_IO_WAIT:
+ case _PR_COND_WAIT:
+ thread->flags &= ~_PR_SUSPENDING;
+/* PR_ASSERT(thread->wait.monitor->stickyCount == 0); */
+ break;
+
+ case _PR_LOCK_WAIT:
+ {
+ PRLock *wLock = thread->wait.lock;
+
+ thread->flags &= ~_PR_SUSPENDING;
+
+ _PR_LOCK_LOCK(wLock);
+ if (thread->wait.lock->owner == 0) {
+ _PR_AssignLock(thread->wait.lock);
+ }
+ _PR_LOCK_UNLOCK(wLock);
+ break;
+ }
+ case _PR_RUNNABLE:
+ break;
+ case _PR_RUNNING:
+ /*
+ * The thread being suspended should be a LOCAL thread with
+ * _pr_numCPUs == 1. Hence, the thread cannot be in RUNNING state
+ */
+ PR_ASSERT(0);
+ break;
+
+ default:
+ /*
+ * thread should have been in one of the above-listed blocked states
+ * (_PR_JOIN_WAIT, _PR_IO_WAIT, _PR_UNBORN, _PR_DEAD_STATE)
+ */
+ PR_Abort();
+ }
+ _PR_THREAD_UNLOCK(thread);
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSON(is);
+
+}
+
+static PRThread *get_thread(_PRCPU *cpu)
+{
+ PRThread *thread;
+ PRIntn pri;
+ PRUint32 r;
+ PRCList *qp;
+ PRIntn priMin, priMax;
+
+ _PR_RUNQ_LOCK(cpu);
+ r = _PR_RUNQREADYMASK(cpu);
+ if (r==0) {
+ priMin = priMax = PR_PRIORITY_FIRST;
+ } else if (r == (1<<PR_PRIORITY_NORMAL) ) {
+ priMin = priMax = PR_PRIORITY_NORMAL;
+ } else {
+ priMin = PR_PRIORITY_FIRST;
+ priMax = PR_PRIORITY_LAST;
+ }
+ thread = NULL;
+ for (pri = priMax; pri >= priMin ; pri-- ) {
+ if (r & (1 << pri)) {
+ for (qp = _PR_RUNQ(cpu)[pri].next;
+ qp != &_PR_RUNQ(cpu)[pri];
+ qp = qp->next) {
+ thread = _PR_THREAD_PTR(qp);
+ /*
+ * skip non-schedulable threads
+ */
+ PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
+ if (thread->no_sched){
+ thread = NULL;
+ /*
+ * Need to wakeup cpus to avoid missing a
+ * runnable thread
+ */
+ _PR_MD_WAKEUP_CPUS();
+ continue;
+ } else if (thread->io_pending == PR_TRUE) {
+ /*
+ * A thread that is blocked for I/O needs to run
+ * on the same cpu on which it was blocked
+ */
+ thread = NULL;
+ continue;
+ } else {
+ /* Pull thread off of its run queue */
+ _PR_DEL_RUNQ(thread);
+ _PR_RUNQ_UNLOCK(cpu);
+ return(thread);
+ }
+ }
+ }
+ thread = NULL;
+ }
+ _PR_RUNQ_UNLOCK(cpu);
+ return(thread);
+}
+
+/*
+** Schedule this native thread by finding the highest priority nspr
+** thread that is ready to run.
+**
+** Note- everyone really needs to call _PR_MD_SWITCH_CONTEXT (which calls
+** PR_Schedule() rather than calling PR_Schedule. Otherwise if there
+** is initialization required for switching from SWITCH_CONTEXT,
+** it will not get done!
+*/
+void _PR_Schedule(void)
+{
+ PRThread *thread, *me = _PR_MD_CURRENT_THREAD();
+ _PRCPU *cpu = me->cpu;
+ PRIntn pri;
+ PRUint32 r;
+ PRCList *qp;
+ PRIntn priMin, priMax;
+
+ /* Interrupts must be disabled */
+ PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
+
+ /* Since we are rescheduling, we no longer want to */
+ _PR_CLEAR_RESCHED_FLAG();
+
+ /*
+ ** Find highest priority thread to run. Bigger priority numbers are
+ ** higher priority threads
+ */
+ _PR_RUNQ_LOCK(cpu);
+ /*
+ * if we are in SuspendAll mode, can schedule only the thread
+ * that called PR_SuspendAll
+ *
+ * The thread may be ready to run now, after completing an I/O
+ * operation, for example
+ */
+ if ((thread = suspendAllThread) != 0) {
+ if ((!(thread->no_sched)) && (thread->state == _PR_RUNNABLE)) {
+ /* Pull thread off of its run queue */
+ _PR_DEL_RUNQ(thread);
+ _PR_RUNQ_UNLOCK(cpu);
+ goto found_thread;
+ } else {
+ thread = NULL;
+ _PR_RUNQ_UNLOCK(cpu);
+ goto idle_thread;
+ }
+ }
+ r = _PR_RUNQREADYMASK(cpu);
+ if (r==0) {
+ priMin = priMax = PR_PRIORITY_FIRST;
+ } else if (r == (1<<PR_PRIORITY_NORMAL) ) {
+ priMin = priMax = PR_PRIORITY_NORMAL;
+ } else {
+ priMin = PR_PRIORITY_FIRST;
+ priMax = PR_PRIORITY_LAST;
+ }
+ thread = NULL;
+ for (pri = priMax; pri >= priMin ; pri-- ) {
+ if (r & (1 << pri)) {
+ for (qp = _PR_RUNQ(cpu)[pri].next;
+ qp != &_PR_RUNQ(cpu)[pri];
+ qp = qp->next) {
+ thread = _PR_THREAD_PTR(qp);
+ /*
+ * skip non-schedulable threads
+ */
+#if !defined(XP_MAC)
+ PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
+#endif
+ if ((thread->no_sched) && (me != thread)){
+ thread = NULL;
+ continue;
+ } else {
+ /* Pull thread off of its run queue */
+ _PR_DEL_RUNQ(thread);
+ _PR_RUNQ_UNLOCK(cpu);
+ goto found_thread;
+ }
+ }
+ }
+ thread = NULL;
+ }
+ _PR_RUNQ_UNLOCK(cpu);
+
+#if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX)
+ _PR_CPU_LIST_LOCK();
+ for (qp = _PR_CPUQ().next; qp != &_PR_CPUQ(); qp = qp->next) {
+ if (cpu != _PR_CPU_PTR(qp)) {
+ if ((thread = get_thread(_PR_CPU_PTR(qp))) != NULL) {
+ thread->cpu = cpu;
+ _PR_CPU_LIST_UNLOCK();
+ goto found_thread;
+ }
+ }
+ }
+ _PR_CPU_LIST_UNLOCK();
+#endif /* _PR_LOCAL_THREADS_ONLY */
+
+idle_thread:
+ /*
+ ** There are no threads to run. Switch to the idle thread
+ */
+ PR_LOG(_pr_sched_lm, PR_LOG_MAX, ("pausing"));
+ thread = _PR_MD_CURRENT_CPU()->idle_thread;
+
+found_thread:
+ PR_ASSERT((me == thread) || ((thread->state == _PR_RUNNABLE) &&
+ (!(thread->no_sched))));
+
+ /* Resume the thread */
+ PR_LOG(_pr_sched_lm, PR_LOG_MAX,
+ ("switching to %d[%p]", thread->id, thread));
+ PR_ASSERT(thread->state != _PR_RUNNING);
+ thread->state = _PR_RUNNING;
+
+ /* If we are on the runq, it just means that we went to sleep on some
+ * resource, and by the time we got here another real native thread had
+ * already given us the resource and put us back on the runqueue
+ */
+ if (thread != me)
+ _PR_MD_RESTORE_CONTEXT(thread);
+#if 0
+ /* XXXMB; with setjmp/longjmp it is impossible to land here, but
+ * it is not with fibers... Is this a bad thing? I believe it is
+ * still safe.
+ */
+ PR_NOT_REACHED("impossible return from schedule");
+#endif
+}
+
+/*
+** Attaches a thread.
+** Does not set the _PR_MD_CURRENT_THREAD.
+** Does not specify the scope of the thread.
+*/
+static PRThread *
+_PR_AttachThread(PRThreadType type, PRThreadPriority priority,
+ PRThreadStack *stack)
+{
+#if defined(XP_MAC)
+#pragma unused (type)
+#endif
+
+ PRThread *thread;
+ char *mem;
+
+ if (priority > PR_PRIORITY_LAST) {
+ priority = PR_PRIORITY_LAST;
+ } else if (priority < PR_PRIORITY_FIRST) {
+ priority = PR_PRIORITY_FIRST;
+ }
+
+ mem = (char*) PR_CALLOC(sizeof(PRThread));
+ if (mem) {
+ thread = (PRThread*) mem;
+ thread->priority = priority;
+ thread->stack = stack;
+ thread->state = _PR_RUNNING;
+ PR_INIT_CLIST(&thread->lockList);
+ if (_MD_NEW_LOCK(&thread->threadLock) == PR_FAILURE) {
+ PR_DELETE(thread);
+ return 0;
+ }
+
+ return thread;
+ }
+ return 0;
+}
+
+
+
+PR_IMPLEMENT(PRThread*)
+_PR_NativeCreateThread(PRThreadType type,
+ void (*start)(void *arg),
+ void *arg,
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize,
+ PRUint32 flags)
+{
+#if defined(XP_MAC)
+#pragma unused (scope)
+#endif
+
+ PRThread *thread;
+
+ thread = _PR_AttachThread(type, priority, NULL);
+
+ if (thread) {
+ PR_Lock(_pr_activeLock);
+ thread->flags = (flags | _PR_GLOBAL_SCOPE);
+ thread->id = ++_pr_utid;
+ if (type == PR_SYSTEM_THREAD) {
+ thread->flags |= _PR_SYSTEM;
+ _pr_systemActive++;
+ } else {
+ _pr_userActive++;
+ }
+ PR_Unlock(_pr_activeLock);
+
+ thread->stack = PR_NEWZAP(PRThreadStack);
+ if (!thread->stack) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ goto done;
+ }
+ thread->stack->stackSize = stackSize?stackSize:_MD_DEFAULT_STACK_SIZE;
+ thread->stack->thr = thread;
+ thread->startFunc = start;
+ thread->arg = arg;
+
+ /*
+ Set thread flags related to scope and joinable state. If joinable
+ thread, allocate a "termination" conidition variable.
+ */
+ if (state == PR_JOINABLE_THREAD) {
+ thread->term = PR_NewCondVar(_pr_terminationCVLock);
+ if (thread->term == NULL) {
+ PR_DELETE(thread->stack);
+ goto done;
+ }
+ }
+
+ thread->state = _PR_RUNNING;
+ if (_PR_MD_CREATE_THREAD(thread, _PR_NativeRunThread, priority,
+ scope,state,stackSize) == PR_SUCCESS) {
+ return thread;
+ }
+ if (thread->term) {
+ PR_DestroyCondVar(thread->term);
+ thread->term = NULL;
+ }
+ PR_DELETE(thread->stack);
+ }
+
+done:
+ if (thread) {
+ _PR_DecrActiveThreadCount(thread);
+ _PR_DetachThread(thread);
+ }
+ return NULL;
+}
+
+/************************************************************************/
+
+PR_IMPLEMENT(PRThread*) _PR_CreateThread(PRThreadType type,
+ void (*start)(void *arg),
+ void *arg,
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize,
+ PRUint32 flags)
+{
+ PRThread *me;
+ PRThread *thread = NULL;
+ PRThreadStack *stack;
+ char *top;
+ PRIntn is;
+ PRIntn native = 0;
+ PRIntn useRecycled = 0;
+ PRBool status;
+
+ /*
+ First, pin down the priority. Not all compilers catch passing out of
+ range enum here. If we let bad values thru, priority queues won't work.
+ */
+ if (priority > PR_PRIORITY_LAST) {
+ priority = PR_PRIORITY_LAST;
+ } else if (priority < PR_PRIORITY_FIRST) {
+ priority = PR_PRIORITY_FIRST;
+ }
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ if (! (flags & _PR_IDLE_THREAD))
+ me = _PR_MD_CURRENT_THREAD();
+
+#if defined(_PR_GLOBAL_THREADS_ONLY)
+ scope = PR_GLOBAL_THREAD;
+#endif
+
+ native = ((scope == PR_GLOBAL_THREAD) && _PR_IS_NATIVE_THREAD_SUPPORTED());
+
+ _PR_ADJUST_STACKSIZE(stackSize);
+
+ if (native) {
+ /*
+ * clear the IDLE_THREAD flag which applies to LOCAL
+ * threads only
+ */
+ flags &= ~_PR_IDLE_THREAD;
+ flags |= _PR_GLOBAL_SCOPE;
+ if (_PR_NUM_DEADNATIVE > 0) {
+ _PR_DEADQ_LOCK;
+
+ if (_PR_NUM_DEADNATIVE == 0) { /* Thread safe check */
+ _PR_DEADQ_UNLOCK;
+ } else {
+ thread = _PR_THREAD_PTR(_PR_DEADNATIVEQ.next);
+ PR_REMOVE_LINK(&thread->links);
+ _PR_DEC_DEADNATIVE;
+ _PR_DEADQ_UNLOCK;
+
+ _PR_InitializeRecycledThread(thread);
+ thread->startFunc = start;
+ thread->arg = arg;
+ PR_Lock(_pr_activeLock);
+ thread->flags = (flags | _PR_GLOBAL_SCOPE);
+ if (type == PR_SYSTEM_THREAD) {
+ thread->flags |= _PR_SYSTEM;
+ _pr_systemActive++;
+ } else {
+ _pr_userActive++;
+ }
+ PR_Unlock(_pr_activeLock);
+
+ if (state == PR_JOINABLE_THREAD) {
+ if (!thread->term)
+ thread->term = PR_NewCondVar(_pr_terminationCVLock);
+ }
+ else {
+ if(thread->term) {
+ PR_DestroyCondVar(thread->term);
+ thread->term = 0;
+ }
+ }
+
+ thread->priority = priority;
+ _PR_MD_SET_PRIORITY(&(thread->md), priority);
+ /* XXX what about stackSize? */
+ thread->state = _PR_RUNNING;
+ _PR_MD_WAKEUP_WAITER(thread);
+ return thread;
+ }
+ }
+ thread = _PR_NativeCreateThread(type, start, arg, priority,
+ scope, state, stackSize, flags);
+ } else {
+ if (_PR_NUM_DEADUSER > 0) {
+ _PR_DEADQ_LOCK;
+
+ if (_PR_NUM_DEADUSER == 0) { /* thread safe check */
+ _PR_DEADQ_UNLOCK;
+ } else {
+ PRCList *ptr;
+
+ /* Go down list checking for a recycled thread with a
+ * large enough stack. XXXMB - this has a bad degenerate case.
+ */
+ ptr = _PR_DEADUSERQ.next;
+ while( ptr != &_PR_DEADUSERQ ) {
+ thread = _PR_THREAD_PTR(ptr);
+ if ((thread->stack->stackSize >= stackSize) &&
+ (!thread->no_sched)) {
+ PR_REMOVE_LINK(&thread->links);
+ _PR_DEC_DEADUSER;
+ break;
+ } else {
+ ptr = ptr->next;
+ thread = NULL;
+ }
+ }
+
+ _PR_DEADQ_UNLOCK;
+
+ if (thread) {
+ _PR_InitializeRecycledThread(thread);
+ thread->startFunc = start;
+ thread->arg = arg;
+ thread->priority = priority;
+ if (state == PR_JOINABLE_THREAD) {
+ if (!thread->term)
+ thread->term = PR_NewCondVar(_pr_terminationCVLock);
+ } else {
+ if(thread->term) {
+ PR_DestroyCondVar(thread->term);
+ thread->term = 0;
+ }
+ }
+ useRecycled++;
+ }
+ }
+ }
+ if (thread == NULL) {
+#ifndef HAVE_CUSTOM_USER_THREADS
+ stack = _PR_NewStack(stackSize);
+ if (!stack) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return 0;
+ }
+ stack->thr = thread;
+
+ /* Allocate thread object and per-thread data off the top of the stack*/
+ top = stack->stackTop;
+#ifdef HAVE_STACK_GROWING_UP
+ thread = (PRThread*) top;
+ top = top + sizeof(PRThread);
+ /*
+ * Make stack 64-byte aligned
+ */
+ if ((PRUptrdiff)top & 0x3f) {
+ top = (char*)(((PRUptrdiff)top + 0x40) & ~0x3f);
+ }
+#else
+ top = top - sizeof(PRThread);
+ thread = (PRThread*) top;
+ /*
+ * Make stack 64-byte aligned
+ */
+ if ((PRUptrdiff)top & 0x3f) {
+ top = (char*)((PRUptrdiff)top & ~0x3f);
+ }
+#endif
+ memset(thread, 0, sizeof(PRThread));
+ thread->threadAllocatedOnStack = 1;
+#else
+ thread = _PR_MD_CREATE_USER_THREAD(stackSize, start, arg);
+ if (!thread) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+ thread->threadAllocatedOnStack = 0;
+ stack = NULL;
+#endif
+
+ /* Initialize thread */
+ if (stack)
+ stack->thr = thread;
+ thread->tpdLength = 0;
+ thread->privateData = NULL;
+ thread->stack = stack;
+ thread->priority = priority;
+ thread->startFunc = start;
+ thread->arg = arg;
+ PR_INIT_CLIST(&thread->lockList);
+
+ if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) {
+ if (thread->threadAllocatedOnStack == 1)
+ _PR_FreeStack(thread->stack);
+ else {
+ PR_DELETE(thread);
+ }
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ return NULL;
+ }
+
+ if (_MD_NEW_LOCK(&thread->threadLock) == PR_FAILURE) {
+ if (thread->threadAllocatedOnStack == 1)
+ _PR_FreeStack(thread->stack);
+ else {
+ PR_DELETE(thread->privateData);
+ PR_DELETE(thread);
+ }
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ return NULL;
+ }
+
+ _PR_MD_INIT_CONTEXT(thread, top, _PR_UserRunThread, &status);
+
+ if (status == PR_FALSE) {
+ _MD_FREE_LOCK(&thread->threadLock);
+ if (thread->threadAllocatedOnStack == 1)
+ _PR_FreeStack(thread->stack);
+ else {
+ PR_DELETE(thread->privateData);
+ PR_DELETE(thread);
+ }
+ return NULL;
+ }
+
+ /*
+ Set thread flags related to scope and joinable state. If joinable
+ thread, allocate a "termination" condition variable.
+ */
+ if (state == PR_JOINABLE_THREAD) {
+ thread->term = PR_NewCondVar(_pr_terminationCVLock);
+ if (thread->term == NULL) {
+ _MD_FREE_LOCK(&thread->threadLock);
+ if (thread->threadAllocatedOnStack == 1)
+ _PR_FreeStack(thread->stack);
+ else {
+ PR_DELETE(thread->privateData);
+ PR_DELETE(thread);
+ }
+ return NULL;
+ }
+ }
+
+ }
+
+ /* Update thread type counter */
+ PR_Lock(_pr_activeLock);
+ thread->flags = flags;
+ thread->id = ++_pr_utid;
+ if (type == PR_SYSTEM_THREAD) {
+ thread->flags |= _PR_SYSTEM;
+ _pr_systemActive++;
+ } else {
+ _pr_userActive++;
+ }
+
+ /* Make thread runnable */
+ thread->state = _PR_RUNNABLE;
+ /*
+ * Add to list of active threads
+ */
+ PR_Unlock(_pr_activeLock);
+
+ if ( (thread->flags & _PR_IDLE_THREAD) || _PR_IS_NATIVE_THREAD(me) )
+ thread->cpu = _PR_GetPrimordialCPU();
+ else
+ thread->cpu = _PR_MD_CURRENT_CPU();
+
+ if ((! (thread->flags & _PR_IDLE_THREAD)) && !_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(is);
+ if ((! (thread->flags & _PR_IDLE_THREAD)) && !_PR_IS_NATIVE_THREAD(thread)) {
+ _PR_RUNQ_LOCK(thread->cpu);
+ _PR_ADD_RUNQ(thread, thread->cpu, priority);
+ _PR_RUNQ_UNLOCK(thread->cpu);
+ }
+
+ if ((thread->flags & _PR_IDLE_THREAD) || _PR_IS_NATIVE_THREAD(me)) {
+ /*
+ ** If the creating thread is a kernel thread, we need to
+ ** awaken the user thread idle thread somehow; potentially
+ ** it could be sleeping in its idle loop, and we need to poke
+ ** it. To do so, wake the idle thread...
+ */
+ _PR_MD_WAKEUP_WAITER(NULL);
+ }
+ if ((! (thread->flags & _PR_IDLE_THREAD)) && !_PR_IS_NATIVE_THREAD(me) )
+ _PR_INTSON(is);
+ }
+
+ return thread;
+}
+
+PR_IMPLEMENT(PRThread*) PR_CreateThread(PRThreadType type,
+ void (*start)(void *arg),
+ void *arg,
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ return _PR_CreateThread(type, start, arg, priority, scope, state,
+ stackSize, 0);
+}
+
+/*
+** Associate a thread object with an existing native thread.
+** "type" is the type of thread object to attach
+** "priority" is the priority to assign to the thread
+** "stack" defines the shape of the threads stack
+**
+** This can return NULL if some kind of error occurs, or if memory is
+** tight.
+**
+** This call is not normally needed unless you create your own native
+** thread. PR_Init does this automatically for the primordial thread.
+*/
+PR_IMPLEMENT(PRThread*) _PRI_AttachThread(PRThreadType type,
+ PRThreadPriority priority, PRThreadStack *stack, PRUint32 flags)
+{
+ PRThread *thread;
+
+ if ((thread = _PR_MD_GET_ATTACHED_THREAD()) != NULL) {
+ return thread;
+ }
+
+ /* Clear out any state if this thread was attached before */
+ _PR_MD_SET_CURRENT_CPU(NULL);
+
+ thread = _PR_AttachThread(type, priority, stack);
+ if (thread) {
+ PRIntn is;
+
+ _PR_MD_SET_CURRENT_THREAD(thread);
+
+ thread->flags = flags | _PR_GLOBAL_SCOPE | _PR_ATTACHED;
+
+ if (!stack) {
+ thread->stack = PR_NEWZAP(PRThreadStack);
+ if (!thread->stack) {
+ _PR_DetachThread(thread);
+ return NULL;
+ }
+ thread->stack->stackSize = _MD_DEFAULT_STACK_SIZE;
+ }
+ PR_INIT_CLIST(&thread->links);
+
+ if (_PR_MD_INIT_ATTACHED_THREAD(thread) == PR_FAILURE) {
+ PR_DELETE(thread->stack);
+ _PR_DetachThread(thread);
+ return NULL;
+ }
+
+ _PR_MD_SET_CURRENT_CPU(NULL);
+
+ if (_PR_MD_CURRENT_CPU()) {
+ _PR_INTSOFF(is);
+ PR_Lock(_pr_activeLock);
+ }
+ if (type == PR_SYSTEM_THREAD) {
+ thread->flags |= _PR_SYSTEM;
+ _pr_systemActive++;
+ } else {
+ _pr_userActive++;
+ }
+ if (_PR_MD_CURRENT_CPU()) {
+ PR_Unlock(_pr_activeLock);
+ _PR_INTSON(is);
+ }
+ }
+ return thread;
+}
+
+PR_IMPLEMENT(PRThread*) PR_AttachThread(PRThreadType type,
+ PRThreadPriority priority, PRThreadStack *stack)
+{
+ return _PRI_AttachThread(type, priority, stack, 0);
+}
+
+/*
+** Detach the nspr thread from the currently executing native thread.
+** The thread object will be destroyed and all related data attached
+** to it. The exit procs will be invoked.
+**
+** This call is not normally needed unless you create your own native
+** thread. PR_Exit will automatially detach the nspr thread object
+** created by PR_Init for the primordial thread.
+**
+** This call returns after the nspr thread object is destroyed.
+*/
+PR_IMPLEMENT(void) PR_DetachThread(void)
+{
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(me->flags & _PR_ATTACHED);
+ _PR_CleanupThread(me);
+ PR_DELETE(me->privateData);
+ _PR_MD_CLEAN_THREAD(me);
+
+ /*XXX we need DetachCPU */
+ if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is);
+ _PR_DecrActiveThreadCount(me);
+ if ( !_PR_IS_NATIVE_THREAD(me)) _PR_FAST_INTSON(is);
+ _PR_MD_SET_CURRENT_THREAD(NULL);
+ if (!me->threadAllocatedOnStack)
+ PR_DELETE(me->stack);
+ _MD_FREE_LOCK(&me->threadLock);
+ PR_DELETE(me);
+}
+
+/*
+** Wait for thread termination:
+** "thread" is the target thread
+**
+** This can return PR_FAILURE if no joinable thread could be found
+** corresponding to the specified target thread.
+**
+** The calling thread is suspended until the target thread completes.
+** Several threads cannot wait for the same thread to complete; one thread
+** will complete successfully and others will terminate with an error PR_FAILURE.
+** The calling thread will not be blocked if the target thread has already
+** terminated.
+*/
+PR_IMPLEMENT(PRStatus) PR_JoinThread(PRThread *thread)
+{
+ PRIntn is;
+ PRCondVar *term;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(is);
+ term = thread->term;
+ /* can't join a non-joinable thread */
+ if (term == NULL) {
+ goto ErrorExit;
+ }
+
+ /* multiple threads can't wait on the same joinable thread */
+ if (term->condQ.next != &term->condQ) {
+ goto ErrorExit;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSON(is);
+
+ /* wait for the target thread's termination cv invariant */
+ PR_Lock (_pr_terminationCVLock);
+ while (thread->state != _PR_JOIN_WAIT) {
+ (void) PR_WaitCondVar(term, PR_INTERVAL_NO_TIMEOUT);
+ }
+ (void) PR_Unlock (_pr_terminationCVLock);
+
+ /*
+ Remove target thread from global waiting to join Q; make it runnable
+ again and put it back on its run Q. When it gets scheduled later in
+ _PR_RunThread code, it will clean up its stack.
+ */
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(is);
+ thread->state = _PR_RUNNABLE;
+ if ( !_PR_IS_NATIVE_THREAD(thread) ) {
+ _PR_THREAD_LOCK(thread);
+
+ _PR_MISCQ_LOCK(thread->cpu);
+ _PR_DEL_JOINQ(thread);
+ _PR_MISCQ_UNLOCK(thread->cpu);
+
+ _PR_AddThreadToRunQ(me, thread);
+ _PR_THREAD_UNLOCK(thread);
+ }
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSON(is);
+
+ _PR_MD_WAKEUP_WAITER(thread);
+
+ return PR_SUCCESS;
+
+ErrorExit:
+ if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSON(is);
+ return PR_FAILURE;
+}
+
+PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thread,
+ PRThreadPriority newPri)
+{
+
+ /*
+ First, pin down the priority. Not all compilers catch passing out of
+ range enum here. If we let bad values thru, priority queues won't work.
+ */
+ if (newPri > PR_PRIORITY_LAST) {
+ newPri = PR_PRIORITY_LAST;
+ } else if (newPri < PR_PRIORITY_FIRST) {
+ newPri = PR_PRIORITY_FIRST;
+ }
+
+ if ( _PR_IS_NATIVE_THREAD(thread) ) {
+ thread->priority = newPri;
+ _PR_MD_SET_PRIORITY(&(thread->md), newPri);
+ } else _PR_SetThreadPriority(thread, newPri);
+}
+
+
+/*
+** This routine prevents all other threads from running. This call is needed by
+** the garbage collector.
+*/
+PR_IMPLEMENT(void) PR_SuspendAll(void)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRCList *qp;
+
+ /*
+ * Stop all user and native threads which are marked GC able.
+ */
+ PR_Lock(_pr_activeLock);
+ suspendAllOn = PR_TRUE;
+ suspendAllThread = _PR_MD_CURRENT_THREAD();
+ _PR_MD_BEGIN_SUSPEND_ALL();
+ for (qp = _PR_ACTIVE_LOCAL_THREADQ().next;
+ qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp->next) {
+ if ((me != _PR_ACTIVE_THREAD_PTR(qp)) &&
+ (_PR_ACTIVE_THREAD_PTR(qp)->flags & _PR_GCABLE_THREAD)) {
+ _PR_Suspend(_PR_ACTIVE_THREAD_PTR(qp));
+ PR_ASSERT((_PR_ACTIVE_THREAD_PTR(qp))->state != _PR_RUNNING);
+ }
+ }
+ for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next;
+ qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp->next) {
+ if ((me != _PR_ACTIVE_THREAD_PTR(qp)) &&
+ (_PR_ACTIVE_THREAD_PTR(qp)->flags & _PR_GCABLE_THREAD))
+ /* PR_Suspend(_PR_ACTIVE_THREAD_PTR(qp)); */
+ _PR_MD_SUSPEND_THREAD(_PR_ACTIVE_THREAD_PTR(qp));
+ }
+ _PR_MD_END_SUSPEND_ALL();
+}
+
+/*
+** This routine unblocks all other threads that were suspended from running by
+** PR_SuspendAll(). This call is needed by the garbage collector.
+*/
+PR_IMPLEMENT(void) PR_ResumeAll(void)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRCList *qp;
+
+ /*
+ * Resume all user and native threads which are marked GC able.
+ */
+ _PR_MD_BEGIN_RESUME_ALL();
+ for (qp = _PR_ACTIVE_LOCAL_THREADQ().next;
+ qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp->next) {
+ if ((me != _PR_ACTIVE_THREAD_PTR(qp)) &&
+ (_PR_ACTIVE_THREAD_PTR(qp)->flags & _PR_GCABLE_THREAD))
+ _PR_Resume(_PR_ACTIVE_THREAD_PTR(qp));
+ }
+ for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next;
+ qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp->next) {
+ if ((me != _PR_ACTIVE_THREAD_PTR(qp)) &&
+ (_PR_ACTIVE_THREAD_PTR(qp)->flags & _PR_GCABLE_THREAD))
+ _PR_MD_RESUME_THREAD(_PR_ACTIVE_THREAD_PTR(qp));
+ }
+ _PR_MD_END_RESUME_ALL();
+ suspendAllThread = NULL;
+ suspendAllOn = PR_FALSE;
+ PR_Unlock(_pr_activeLock);
+}
+
+PR_IMPLEMENT(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg)
+{
+ PRCList *qp, *qp_next;
+ PRIntn i = 0;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRStatus rv = PR_SUCCESS;
+ PRThread* t;
+
+ /*
+ ** Currently Enumerate threads happen only with suspension and
+ ** pr_activeLock held
+ */
+ PR_ASSERT(suspendAllOn);
+
+ /* Steve Morse, 4-23-97: Note that we can't walk a queue by taking
+ * qp->next after applying the function "func". In particular, "func"
+ * might remove the thread from the queue and put it into another one in
+ * which case qp->next no longer points to the next entry in the original
+ * queue.
+ *
+ * To get around this problem, we save qp->next in qp_next before applying
+ * "func" and use that saved value as the next value after applying "func".
+ */
+
+ /*
+ * Traverse the list of local and global threads
+ */
+ for (qp = _PR_ACTIVE_LOCAL_THREADQ().next;
+ qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp_next)
+ {
+ qp_next = qp->next;
+ t = _PR_ACTIVE_THREAD_PTR(qp);
+ if (t->flags & _PR_GCABLE_THREAD)
+ {
+ rv = (*func)(t, i, arg);
+ if (rv != PR_SUCCESS)
+ return rv;
+ i++;
+ }
+ }
+ for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next;
+ qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp_next)
+ {
+ qp_next = qp->next;
+ t = _PR_ACTIVE_THREAD_PTR(qp);
+ if (t->flags & _PR_GCABLE_THREAD)
+ {
+ rv = (*func)(t, i, arg);
+ if (rv != PR_SUCCESS)
+ return rv;
+ i++;
+ }
+ }
+ return rv;
+}
+
+/* FUNCTION: _PR_AddSleepQ
+** DESCRIPTION:
+** Adds a thread to the sleep/pauseQ.
+** RESTRICTIONS:
+** Caller must have the RUNQ lock.
+** Caller must be a user level thread
+*/
+PR_IMPLEMENT(void)
+_PR_AddSleepQ(PRThread *thread, PRIntervalTime timeout)
+{
+ _PRCPU *cpu = thread->cpu;
+
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ /* append the thread to the global pause Q */
+ PR_APPEND_LINK(&thread->links, &_PR_PAUSEQ(thread->cpu));
+ thread->flags |= _PR_ON_PAUSEQ;
+ } else {
+ PRIntervalTime sleep;
+ PRCList *q;
+ PRThread *t;
+
+ /* sort onto global sleepQ */
+ sleep = timeout;
+
+ /* Check if we are longest timeout */
+ if (timeout >= _PR_SLEEPQMAX(cpu)) {
+ PR_INSERT_BEFORE(&thread->links, &_PR_SLEEPQ(cpu));
+ thread->sleep = timeout - _PR_SLEEPQMAX(cpu);
+ _PR_SLEEPQMAX(cpu) = timeout;
+ } else {
+ /* Sort thread into global sleepQ at appropriate point */
+ q = _PR_SLEEPQ(cpu).next;
+
+ /* Now scan the list for where to insert this entry */
+ while (q != &_PR_SLEEPQ(cpu)) {
+ t = _PR_THREAD_PTR(q);
+ if (sleep < t->sleep) {
+ /* Found sleeper to insert in front of */
+ break;
+ }
+ sleep -= t->sleep;
+ q = q->next;
+ }
+ thread->sleep = sleep;
+ PR_INSERT_BEFORE(&thread->links, q);
+
+ /*
+ ** Subtract our sleep time from the sleeper that follows us (there
+ ** must be one) so that they remain relative to us.
+ */
+ PR_ASSERT (thread->links.next != &_PR_SLEEPQ(cpu));
+
+ t = _PR_THREAD_PTR(thread->links.next);
+ PR_ASSERT(_PR_THREAD_PTR(t->links.prev) == thread);
+ t->sleep -= sleep;
+ }
+
+ thread->flags |= _PR_ON_SLEEPQ;
+ }
+}
+
+/* FUNCTION: _PR_DelSleepQ
+** DESCRIPTION:
+** Removes a thread from the sleep/pauseQ.
+** INPUTS:
+** If propogate_time is true, then the thread following the deleted
+** thread will be get the time from the deleted thread. This is used
+** when deleting a sleeper that has not timed out.
+** RESTRICTIONS:
+** Caller must have the RUNQ lock.
+** Caller must be a user level thread
+*/
+PR_IMPLEMENT(void)
+_PR_DelSleepQ(PRThread *thread, PRBool propogate_time)
+{
+ _PRCPU *cpu = thread->cpu;
+
+ /* Remove from pauseQ/sleepQ */
+ if (thread->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) {
+ if (thread->flags & _PR_ON_SLEEPQ) {
+ PRCList *q = thread->links.next;
+ if (q != &_PR_SLEEPQ(cpu)) {
+ if (propogate_time == PR_TRUE) {
+ PRThread *after = _PR_THREAD_PTR(q);
+ after->sleep += thread->sleep;
+ } else
+ _PR_SLEEPQMAX(cpu) -= thread->sleep;
+ } else {
+ /* Check if prev is the beggining of the list; if so,
+ * we are the only element on the list.
+ */
+ if (thread->links.prev != &_PR_SLEEPQ(cpu))
+ _PR_SLEEPQMAX(cpu) -= thread->sleep;
+ else
+ _PR_SLEEPQMAX(cpu) = 0;
+ }
+ thread->flags &= ~_PR_ON_SLEEPQ;
+ } else {
+ thread->flags &= ~_PR_ON_PAUSEQ;
+ }
+ PR_REMOVE_LINK(&thread->links);
+ } else
+ PR_ASSERT(0);
+}
+
+void
+_PR_AddThreadToRunQ(
+ PRThread *me, /* the current thread */
+ PRThread *thread) /* the local thread to be added to a run queue */
+{
+ PRThreadPriority pri = thread->priority;
+ _PRCPU *cpu = thread->cpu;
+
+ PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread));
+
+#if defined(WINNT)
+ /*
+ * On NT, we can only reliably know that the current CPU
+ * is not idle. We add the awakened thread to the run
+ * queue of its CPU if its CPU is the current CPU.
+ * For any other CPU, we don't really know whether it
+ * is busy or idle. So in all other cases, we just
+ * "post" the awakened thread to the IO completion port
+ * for the next idle CPU to execute (this is done in
+ * _PR_MD_WAKEUP_WAITER).
+ */
+ if (!_PR_IS_NATIVE_THREAD(me) && (cpu == me->cpu)) {
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(thread, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ }
+#else
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(thread, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ if (!_PR_IS_NATIVE_THREAD(me) && (cpu == me->cpu)) {
+ if (pri > me->priority) {
+ _PR_SET_RESCHED_FLAG();
+ }
+ }
+#endif
+}
diff --git a/pr/src/threads/prcmon.c b/pr/src/threads/prcmon.c
new file mode 100644
index 00000000..138e2083
--- /dev/null
+++ b/pr/src/threads/prcmon.c
@@ -0,0 +1,392 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+
+/* Lock used to lock the monitor cache */
+#ifdef _PR_NO_PREEMPT
+#define _PR_NEW_LOCK_MCACHE()
+#define _PR_LOCK_MCACHE()
+#define _PR_UNLOCK_MCACHE()
+#else
+#ifdef _PR_LOCAL_THREADS_ONLY
+#define _PR_NEW_LOCK_MCACHE()
+#define _PR_LOCK_MCACHE() { PRIntn _is; _PR_INTSOFF(_is)
+#define _PR_UNLOCK_MCACHE() _PR_INTSON(_is); }
+#else
+PRLock *_pr_mcacheLock;
+#define _PR_NEW_LOCK_MCACHE() (_pr_mcacheLock = PR_NewLock())
+#define _PR_LOCK_MCACHE() PR_Lock(_pr_mcacheLock)
+#define _PR_UNLOCK_MCACHE() PR_Unlock(_pr_mcacheLock)
+#endif
+#endif
+
+/************************************************************************/
+
+typedef struct MonitorCacheEntryStr MonitorCacheEntry;
+
+struct MonitorCacheEntryStr {
+ MonitorCacheEntry* next;
+ void* address;
+ PRMonitor* mon;
+ long cacheEntryCount;
+};
+
+static PRUint32 hash_mask;
+static PRUintn num_hash_buckets;
+static PRUintn num_hash_buckets_log2;
+static MonitorCacheEntry **hash_buckets;
+static MonitorCacheEntry *free_entries;
+static PRUintn num_free_entries;
+static PRBool expanding;
+int _pr_mcache_ready;
+
+#define HASH(address) \
+ ((PRUint32) ( ((PRUptrdiff)(address) >> 2) ^ \
+ ((PRUptrdiff)(address) >> 10) ) \
+ & hash_mask)
+
+/*
+** Expand the monitor cache. This grows the hash buckets and allocates a
+** new chunk of cache entries and throws them on the free list. We keep
+** as many hash buckets as there are entries.
+**
+** Because we call malloc and malloc may need the monitor cache, we must
+** ensure that there are several free monitor cache entries available for
+** malloc to get. FREE_THRESHOLD is used to prevent monitor cache
+** starvation during monitor cache expansion.
+*/
+
+#define FREE_THRESHOLD 5
+
+static PRStatus ExpandMonitorCache(PRUintn new_size_log2)
+{
+ MonitorCacheEntry **old_hash_buckets, *p;
+ PRUintn i, entries, old_num_hash_buckets, added;
+ MonitorCacheEntry **new_hash_buckets, *new_entries;
+
+ entries = 1L << new_size_log2;
+
+ /*
+ ** Expand the monitor-cache-entry free list
+ */
+ new_entries = (MonitorCacheEntry*)PR_CALLOC(
+ entries * sizeof(MonitorCacheEntry));
+ if (NULL == new_entries) return PR_FAILURE;
+
+ /*
+ ** Allocate system monitors for the new monitor cache entries. If we
+ ** run out of system monitors, break out of the loop.
+ */
+ for (i = 0, added = 0, p = new_entries; i < entries; i++, p++, added++)
+ {
+ p->mon = PR_NewMonitor();
+ if (!p->mon) break;
+ }
+ if (added != entries)
+ {
+ if (added == 0)
+ {
+ /* Totally out of system monitors. Lossage abounds */
+ PR_DELETE(new_entries);
+ return PR_FAILURE;
+ }
+
+ /*
+ ** We were able to allocate some of the system monitors. Use
+ ** realloc to shrink down the new_entries memory
+ */
+ p = (MonitorCacheEntry*)PR_REALLOC(
+ new_entries, added * sizeof(MonitorCacheEntry));
+ if (p == 0)
+ {
+ /*
+ ** Total lossage. We just leaked a bunch of system monitors
+ ** all over the floor. This should never ever happen.
+ */
+ PR_ASSERT(p != 0);
+ return PR_FAILURE;
+ }
+ }
+
+ /*
+ ** Now that we have allocated all of the system monitors, build up
+ ** the new free list. We can just update the free_list because we own
+ ** the mcache-lock and we aren't calling anyone who might want to use
+ ** it.
+ */
+ for (i = 0, p = new_entries; i < added - 1; i++, p++) p->next = p + 1;
+ p->next = free_entries;
+ free_entries = new_entries;
+ num_free_entries += added;
+
+ /* Try to expand the hash table */
+ new_hash_buckets = (MonitorCacheEntry**)PR_CALLOC(
+ entries * sizeof(MonitorCacheEntry*));
+ if (NULL == new_hash_buckets)
+ {
+ /*
+ ** Partial lossage. In this situation we don't get any more hash
+ ** buckets, which just means that the table lookups will take
+ ** longer. This is bad, but not fatal
+ */
+ PR_LOG(_pr_cmon_lm, PR_LOG_WARNING,
+ ("unable to grow monitor cache hash buckets"));
+ return PR_SUCCESS;
+ }
+
+ /*
+ ** Compute new hash mask value. This value is used to mask an address
+ ** until it's bits are in the right spot for indexing into the hash
+ ** table.
+ */
+ hash_mask = entries - 1;
+
+ /*
+ ** Expand the hash table. We have to rehash everything in the old
+ ** table into the new table.
+ */
+ old_hash_buckets = hash_buckets;
+ old_num_hash_buckets = num_hash_buckets;
+ for (i = 0; i < old_num_hash_buckets; i++)
+ {
+ p = old_hash_buckets[i];
+ while (p)
+ {
+ MonitorCacheEntry *next = p->next;
+
+ /* Hash based on new table size, and then put p in the new table */
+ PRUintn hash = HASH(p->address);
+ p->next = new_hash_buckets[hash];
+ new_hash_buckets[hash] = p;
+
+ p = next;
+ }
+ }
+
+ /*
+ ** Switch over to new hash table and THEN call free of the old
+ ** table. Since free might re-enter _pr_mcache_lock, things would
+ ** break terribly if it used the old hash table.
+ */
+ hash_buckets = new_hash_buckets;
+ num_hash_buckets = entries;
+ num_hash_buckets_log2 = new_size_log2;
+ PR_DELETE(old_hash_buckets);
+
+ PR_LOG(_pr_cmon_lm, PR_LOG_NOTICE,
+ ("expanded monitor cache to %d (buckets %d)",
+ num_free_entries, entries));
+
+ return PR_SUCCESS;
+} /* ExpandMonitorCache */
+
+/*
+** Lookup a monitor cache entry by address. Return a pointer to the
+** pointer to the monitor cache entry on success, null on failure.
+*/
+static MonitorCacheEntry **LookupMonitorCacheEntry(void *address)
+{
+ PRUintn hash;
+ MonitorCacheEntry **pp, *p;
+
+ hash = HASH(address);
+ pp = hash_buckets + hash;
+ while ((p = *pp) != 0) {
+ if (p->address == address) {
+ if (p->cacheEntryCount > 0)
+ return pp;
+ else
+ return NULL;
+ }
+ pp = &p->next;
+ }
+ return NULL;
+}
+
+/*
+** Try to create a new cached monitor. If it's already in the cache,
+** great - return it. Otherwise get a new free cache entry and set it
+** up. If the cache free space is getting low, expand the cache.
+*/
+static PRMonitor *CreateMonitor(void *address)
+{
+ PRUintn hash;
+ MonitorCacheEntry **pp, *p;
+
+ hash = HASH(address);
+ pp = hash_buckets + hash;
+ while ((p = *pp) != 0) {
+ if (p->address == address) goto gotit;
+
+ pp = &p->next;
+ }
+
+ /* Expand the monitor cache if we have run out of free slots in the table */
+ if (num_free_entries < FREE_THRESHOLD)
+ {
+ /* Expand monitor cache */
+
+ /*
+ ** This function is called with the lock held. So what's the 'expanding'
+ ** boolean all about? Seems a bit redundant.
+ */
+ if (!expanding)
+ {
+ PRStatus rv;
+
+ expanding = PR_TRUE;
+ rv = ExpandMonitorCache(num_hash_buckets_log2 + 1);
+ expanding = PR_FALSE;
+ if (PR_FAILURE == rv) return NULL;
+
+ /* redo the hash because it'll be different now */
+ hash = HASH(address);
+ }
+ else
+ {
+ /*
+ ** We are in process of expanding and we need a cache
+ ** monitor. Make sure we have enough!
+ */
+ PR_ASSERT(num_free_entries > 0);
+ }
+ }
+
+ /* Make a new monitor */
+ p = free_entries;
+ free_entries = p->next;
+ num_free_entries--;
+ p->address = address;
+ p->next = hash_buckets[hash];
+ hash_buckets[hash] = p;
+ PR_ASSERT(p->cacheEntryCount == 0);
+
+ gotit:
+ p->cacheEntryCount++;
+ return p->mon;
+}
+
+/*
+** Initialize the monitor cache
+*/
+void _PR_InitCMon(void)
+{
+ _PR_NEW_LOCK_MCACHE();
+ ExpandMonitorCache(3);
+ _pr_mcache_ready = 1;
+}
+
+/*
+** Create monitor for address. Don't enter the monitor while we have the
+** mcache locked because we might block!
+*/
+PR_IMPLEMENT(PRMonitor*) PR_CEnterMonitor(void *address)
+{
+ PRMonitor *mon;
+
+ _PR_LOCK_MCACHE();
+ mon = CreateMonitor(address);
+ _PR_UNLOCK_MCACHE();
+
+ if (!mon) return NULL;
+
+ PR_EnterMonitor(mon);
+ return mon;
+}
+
+PR_IMPLEMENT(PRStatus) PR_CExitMonitor(void *address)
+{
+ MonitorCacheEntry **pp, *p;
+ PRStatus status = PR_SUCCESS;
+
+ _PR_LOCK_MCACHE();
+ pp = LookupMonitorCacheEntry(address);
+ if (pp != NULL) {
+ p = *pp;
+ if (--p->cacheEntryCount == 0) {
+ /*
+ ** Nobody is using the system monitor. Put it on the cached free
+ ** list. We are safe from somebody trying to use it because we
+ ** have the mcache locked.
+ */
+ p->address = 0; /* defensive move */
+ *pp = p->next; /* unlink from hash_buckets */
+ p->next = free_entries; /* link into free list */
+ free_entries = p;
+ num_free_entries++; /* count it as free */
+ }
+ status = PR_ExitMonitor(p->mon);
+ } else {
+ status = PR_FAILURE;
+ }
+ _PR_UNLOCK_MCACHE();
+
+ return status;
+}
+
+PR_IMPLEMENT(PRStatus) PR_CWait(void *address, PRIntervalTime ticks)
+{
+ MonitorCacheEntry **pp;
+ PRMonitor *mon;
+
+ _PR_LOCK_MCACHE();
+ pp = LookupMonitorCacheEntry(address);
+ mon = pp ? ((*pp)->mon) : NULL;
+ _PR_UNLOCK_MCACHE();
+
+ if (mon == NULL)
+ return PR_FAILURE;
+ else
+ return PR_Wait(mon, ticks);
+}
+
+PR_IMPLEMENT(PRStatus) PR_CNotify(void *address)
+{
+ MonitorCacheEntry **pp;
+ PRMonitor *mon;
+
+ _PR_LOCK_MCACHE();
+ pp = LookupMonitorCacheEntry(address);
+ mon = pp ? ((*pp)->mon) : NULL;
+ _PR_UNLOCK_MCACHE();
+
+ if (mon == NULL)
+ return PR_FAILURE;
+ else
+ return PR_Notify(mon);
+}
+
+PR_IMPLEMENT(PRStatus) PR_CNotifyAll(void *address)
+{
+ MonitorCacheEntry **pp;
+ PRMonitor *mon;
+
+ _PR_LOCK_MCACHE();
+ pp = LookupMonitorCacheEntry(address);
+ mon = pp ? ((*pp)->mon) : NULL;
+ _PR_UNLOCK_MCACHE();
+
+ if (mon == NULL)
+ return PR_FAILURE;
+ else
+ return PR_NotifyAll(mon);
+}
diff --git a/pr/src/threads/prcthr.c b/pr/src/threads/prcthr.c
new file mode 100644
index 00000000..cf87e22b
--- /dev/null
+++ b/pr/src/threads/prcthr.c
@@ -0,0 +1,414 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+/*
+** Routines common to both native and user threads.
+**
+**
+** Clean up a thread object, releasing all of the attached data. Do not
+** free the object itself (it may not have been malloc'd)
+*/
+void _PR_CleanupThread(PRThread *thread)
+{
+ PRUintn i;
+ void **ptd;
+ _PRPerThreadExit *pte;
+ PRThreadPrivateDTOR *destructor;
+
+ /* Free up per-thread-data */
+ ptd = thread->privateData;
+ destructor = _pr_tpd_destructors;
+ for (i = 0; i < thread->tpdLength; i++, ptd++, destructor++)
+ {
+ if (*destructor && *ptd) (**destructor)(*ptd);
+ *ptd = NULL;
+ }
+
+ /* Free any thread dump procs */
+ if (thread->dumpArg) {
+ PR_DELETE(thread->dumpArg);
+ }
+ thread->dump = 0;
+
+ /* Invoke per-thread exit functions */
+ pte = &thread->ptes[0];
+ for (i = 0; i < thread->numExits; i++, pte++) {
+ if (pte->func) {
+ (*pte->func)(pte->arg);
+ pte->func = 0;
+ }
+ }
+ if (thread->ptes) {
+ PR_DELETE(thread->ptes);
+ thread->numExits = 0;
+ }
+ PR_ASSERT(thread->numExits == 0);
+ PR_DELETE(thread->errorString);
+ thread->errorStringSize = 0;
+ thread->environment = NULL;
+}
+
+PR_IMPLEMENT(PRStatus) PR_Yield()
+{
+ static PRBool warning = PR_TRUE;
+ if (warning) warning = _PR_Obsolete(
+ "PR_Yield()", "PR_Sleep(PR_INTERVAL_NO_WAIT)");
+ return (PR_Sleep(PR_INTERVAL_NO_WAIT));
+}
+
+/*
+** Make the current thread sleep until "timeout" ticks amount of time
+** has expired. If "timeout" is PR_INTERVAL_NO_WAIT then the call is
+** equivalent to a yield. Waiting for an infinite amount of time is
+** allowed in the expectation that another thread will interrupt().
+**
+** A single lock is used for all threads calling sleep. Each caller
+** does get its own condition variable since each is expected to have
+** a unique 'timeout'.
+*/
+PR_IMPLEMENT(PRStatus) PR_Sleep(PRIntervalTime timeout)
+{
+ static PRLock *ml = NULL;
+ PRStatus rv = PR_SUCCESS;
+ if (PR_INTERVAL_NO_WAIT == timeout)
+ {
+ /*
+ ** This is a simple yield, nothing more, nothing less.
+ */
+ PRIntn is;
+ PRThread *me = PR_GetCurrentThread();
+ PRUintn pri = me->priority;
+ _PRCPU *cpu = _PR_MD_CURRENT_CPU();
+
+ if ( _PR_IS_NATIVE_THREAD(me) ) _PR_MD_YIELD();
+ else
+ {
+ _PR_INTSOFF(is);
+ _PR_RUNQ_LOCK(cpu);
+ if (_PR_RUNQREADYMASK(cpu) >> pri) {
+ me->cpu = cpu;
+ me->state = _PR_RUNNABLE;
+ _PR_ADD_RUNQ(me, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+
+ PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("PR_Yield: yielding"));
+ _PR_MD_SWITCH_CONTEXT(me);
+ PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("PR_Yield: done"));
+
+ _PR_FAST_INTSON(is);
+ }
+ else
+ {
+ _PR_RUNQ_UNLOCK(cpu);
+ _PR_INTSON(is);
+ }
+ }
+ }
+ else
+ {
+ /*
+ ** This is waiting for some finite period of time.
+ ** A thread in this state is interruptible (PR_Interrupt()),
+ ** but the lock and cvar used are local to the implementation
+ ** and not visible to the caller, therefore not notifiable.
+ */
+ if (ml == NULL) ml = PR_NewLock();
+
+ if (ml == NULL) rv = PR_FAILURE;
+ else
+ {
+ PRCondVar *cv = PR_NewCondVar(ml);
+ PRIntervalTime timein = PR_IntervalNow();
+
+ PR_Lock(ml);
+ while (rv == PR_SUCCESS)
+ {
+ PRIntervalTime delta = PR_IntervalNow() - timein;
+ if (delta > timeout) break;
+ rv = PR_WaitCondVar(cv, timeout - delta);
+ }
+ PR_Unlock(ml);
+
+ PR_DestroyCondVar(cv);
+ }
+ }
+ return rv;
+}
+
+PR_IMPLEMENT(PRUint32) PR_GetThreadID(PRThread *thread)
+{
+ return thread->id;
+}
+
+PR_IMPLEMENT(PRThreadPriority) PR_GetThreadPriority(const PRThread *thread)
+{
+ return (PRThreadPriority) thread->priority;
+}
+
+PR_IMPLEMENT(PRThread *) PR_GetCurrentThread()
+{
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+ return _PR_MD_CURRENT_THREAD();
+}
+
+/*
+** Set the interrupt flag for a thread. The thread will be unable to
+** block in i/o functions when this happens. Also, any PR_Wait's in
+** progress will be undone. The interrupt remains in force until
+** PR_ClearInterrupt is called.
+*/
+PR_IMPLEMENT(PRStatus) PR_Interrupt(PRThread *thread)
+{
+#ifdef _PR_GLOBAL_THREADS_ONLY
+ PRCondVar *victim;
+
+ _PR_THREAD_LOCK(thread);
+ thread->flags |= _PR_INTERRUPT;
+ victim = thread->wait.cvar;
+ _PR_THREAD_UNLOCK(thread);
+ if (NULL != victim) {
+ int haveLock = (victim->lock->owner == _PR_MD_CURRENT_THREAD());
+
+ if (!haveLock) PR_Lock(victim->lock);
+ PR_NotifyAllCondVar(victim);
+ if (!haveLock) PR_Unlock(victim->lock);
+ }
+ return PR_SUCCESS;
+#else /* ! _PR_GLOBAL_THREADS_ONLY */
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSOFF(is);
+
+ _PR_THREAD_LOCK(thread);
+ thread->flags |= _PR_INTERRUPT;
+ switch (thread->state) {
+ case _PR_COND_WAIT:
+ /*
+ * call is made with thread locked;
+ * on return lock is released
+ */
+ _PR_NotifyLockedThread(thread);
+ break;
+ case _PR_IO_WAIT:
+ /*
+ * Need to hold the thread lock when calling
+ * _PR_Unblock_IO_Wait(). On return lock is
+ * released.
+ */
+#if defined(XP_UNIX) || defined(WINNT) || defined(WIN16)
+ _PR_Unblock_IO_Wait(thread);
+#else
+ _PR_THREAD_UNLOCK(thread);
+#endif
+ break;
+ case _PR_RUNNING:
+ case _PR_RUNNABLE:
+ case _PR_LOCK_WAIT:
+ default:
+ _PR_THREAD_UNLOCK(thread);
+ break;
+ }
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_INTSON(is);
+ return PR_SUCCESS;
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+}
+
+/*
+** Clear the interrupt flag for self.
+*/
+PR_IMPLEMENT(void) PR_ClearInterrupt()
+{
+ PRIntn is;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is);
+ _PR_THREAD_LOCK(me);
+ me->flags &= ~_PR_INTERRUPT;
+ _PR_THREAD_UNLOCK(me);
+ if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSON(is);
+}
+
+/*
+** Return the thread stack pointer of the given thread.
+*/
+PR_IMPLEMENT(void *) PR_GetSP(PRThread *thread)
+{
+ return (void *)_PR_MD_GET_SP(thread);
+}
+
+PR_IMPLEMENT(void*) GetExecutionEnvironment(PRThread *thread)
+{
+ return thread->environment;
+}
+
+PR_IMPLEMENT(void) SetExecutionEnvironment(PRThread *thread, void *env)
+{
+ thread->environment = env;
+}
+
+
+PR_IMPLEMENT(PRInt32) PR_GetThreadAffinityMask(PRThread *thread, PRUint32 *mask)
+{
+#ifdef HAVE_THREAD_AFFINITY
+/*
+ * Irix ignores the thread argument
+ */
+#ifndef IRIX
+ if (_PR_IS_NATIVE_THREAD(thread))
+ return _PR_MD_GETTHREADAFFINITYMASK(thread, mask);
+ else
+ return 0;
+#else
+ return _PR_MD_GETTHREADAFFINITYMASK(thread, mask);
+#endif /* !IRIX */
+#else
+
+#if defined(XP_MAC)
+#pragma unused (thread, mask)
+#endif
+
+ return 0;
+#endif
+}
+
+PR_IMPLEMENT(PRInt32) PR_SetThreadAffinityMask(PRThread *thread, PRUint32 mask )
+{
+#ifdef HAVE_THREAD_AFFINITY
+#ifndef IRIX
+ return _PR_MD_SETTHREADAFFINITYMASK(thread, mask);
+#endif
+#else
+
+#if defined(XP_MAC)
+#pragma unused (thread, mask)
+#endif
+
+ return 0;
+#endif
+}
+
+/* This call is thread unsafe if another thread is calling SetConcurrency()
+ */
+PR_IMPLEMENT(PRInt32) PR_SetCPUAffinityMask(PRUint32 mask)
+{
+#ifdef HAVE_THREAD_AFFINITY
+ PRCList *qp;
+ extern PRUint32 _pr_cpu_affinity_mask;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ _pr_cpu_affinity_mask = mask;
+
+ qp = _PR_CPUQ().next;
+ while(qp != &_PR_CPUQ()) {
+ _PRCPU *cpu;
+
+ cpu = _PR_CPU_PTR(qp);
+ PR_SetThreadAffinityMask(cpu->thread, mask);
+
+ qp = qp->next;
+ }
+#endif
+
+#if defined(XP_MAC)
+#pragma unused (mask)
+#endif
+
+ return 0;
+}
+
+PRUint32 _pr_recycleThreads = 0;
+PR_IMPLEMENT(void) PR_SetThreadRecycleMode(PRUint32 count)
+{
+ _pr_recycleThreads = count;
+}
+
+PR_IMPLEMENT(PRThread*) PR_CreateThreadGCAble(PRThreadType type,
+ void (*start)(void *arg),
+ void *arg,
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ return _PR_CreateThread(type, start, arg, priority, scope, state,
+ stackSize, _PR_GCABLE_THREAD);
+}
+
+#ifdef SOLARIS
+PR_IMPLEMENT(PRThread*) PR_CreateThreadBound(PRThreadType type,
+ void (*start)(void *arg),
+ void *arg,
+ PRUintn priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+ return _PR_CreateThread(type, start, arg, priority, scope, state,
+ stackSize, _PR_BOUND_THREAD);
+}
+#endif
+
+
+PR_IMPLEMENT(PRThread*) PR_AttachThreadGCAble(
+ PRThreadType type, PRThreadPriority priority, PRThreadStack *stack)
+{
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+ return _PRI_AttachThread(type, priority, stack, _PR_GCABLE_THREAD);
+}
+
+PR_IMPLEMENT(void) PR_SetThreadGCAble()
+{
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+ PR_Lock(_pr_activeLock);
+ _PR_MD_CURRENT_THREAD()->flags |= _PR_GCABLE_THREAD;
+ PR_Unlock(_pr_activeLock);
+}
+
+PR_IMPLEMENT(void) PR_ClearThreadGCAble()
+{
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+ PR_Lock(_pr_activeLock);
+ _PR_MD_CURRENT_THREAD()->flags &= (~_PR_GCABLE_THREAD);
+ PR_Unlock(_pr_activeLock);
+}
+
+PR_IMPLEMENT(PRThreadScope) PR_GetThreadScope(const PRThread *thread)
+{
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ if (_PR_IS_NATIVE_THREAD(thread))
+ return PR_GLOBAL_THREAD;
+ else
+ return PR_LOCAL_THREAD;
+}
+
+PR_IMPLEMENT(PRThreadType) PR_GetThreadType(const PRThread *thread)
+{
+ return (thread->flags & _PR_SYSTEM) ? PR_SYSTEM_THREAD : PR_USER_THREAD;
+}
+
+PR_IMPLEMENT(PRThreadState) PR_GetThreadState(const PRThread *thread)
+{
+ return (NULL == thread->term) ? PR_UNJOINABLE_THREAD : PR_JOINABLE_THREAD;
+} /* PR_GetThreadState */
diff --git a/pr/src/threads/prdump.c b/pr/src/threads/prdump.c
new file mode 100644
index 00000000..471a0dfe
--- /dev/null
+++ b/pr/src/threads/prdump.c
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+/* XXX use unbuffered nspr stdio */
+
+PRFileDesc *_pr_dumpOut;
+
+PRUint32 _PR_DumpPrintf(PRFileDesc *fd, const char *fmt, ...)
+{
+ char buf[100];
+ PRUint32 nb;
+ va_list ap;
+
+ va_start(ap, fmt);
+ nb = PR_vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ PR_Write(fd, buf, nb);
+
+ return nb;
+}
+
+void _PR_DumpThread(PRFileDesc *fd, PRThread *thread)
+{
+
+#ifndef _PR_GLOBAL_THREADS_ONLY
+ _PR_DumpPrintf(fd, "%05d[%08p] pri=%2d flags=0x%02x",
+ thread->id, thread, thread->priority, thread->flags);
+ switch (thread->state) {
+ case _PR_RUNNABLE:
+ case _PR_RUNNING:
+ break;
+ case _PR_LOCK_WAIT:
+ _PR_DumpPrintf(fd, " lock=%p", thread->wait.lock);
+ break;
+ case _PR_COND_WAIT:
+ _PR_DumpPrintf(fd, " condvar=%p sleep=%lldms",
+ thread->wait.cvar, thread->sleep);
+ break;
+ case _PR_SUSPENDED:
+ _PR_DumpPrintf(fd, " suspended");
+ break;
+ }
+ PR_Write(fd, "\n", 1);
+#endif
+
+ /* Now call dump routine */
+ if (thread->dump) {
+ thread->dump(fd, thread, thread->dumpArg);
+ }
+}
+
+static void DumpThreadQueue(PRFileDesc *fd, PRCList *list)
+{
+#ifndef _PR_GLOBAL_THREADS_ONLY
+ PRCList *q;
+
+ q = list->next;
+ while (q != list) {
+ PRThread *t = _PR_THREAD_PTR(q);
+ _PR_DumpThread(fd, t);
+ q = q->next;
+ }
+#endif
+}
+
+void _PR_DumpThreads(PRFileDesc *fd)
+{
+ PRThread *t;
+ PRIntn i;
+
+ _PR_DumpPrintf(fd, "Current Thread:\n");
+ t = _PR_MD_CURRENT_THREAD();
+ _PR_DumpThread(fd, t);
+
+ _PR_DumpPrintf(fd, "Runnable Threads:\n");
+ for (i = 0; i < 32; i++) {
+ DumpThreadQueue(fd, &_PR_RUNQ(t->cpu)[i]);
+ }
+
+ _PR_DumpPrintf(fd, "CondVar timed wait Threads:\n");
+ DumpThreadQueue(fd, &_PR_SLEEPQ(t->cpu));
+
+ _PR_DumpPrintf(fd, "CondVar wait Threads:\n");
+ DumpThreadQueue(fd, &_PR_PAUSEQ(t->cpu));
+
+ _PR_DumpPrintf(fd, "Suspended Threads:\n");
+ DumpThreadQueue(fd, &_PR_SUSPENDQ(t->cpu));
+}
+
+void PR_ShowStatus(void)
+{
+ PRIntn is;
+
+ if ( _PR_MD_CURRENT_THREAD()
+ && !_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) _PR_INTSOFF(is);
+ _pr_dumpOut = _pr_stderr;
+ _PR_DumpThreads(_pr_dumpOut);
+ if ( _PR_MD_CURRENT_THREAD()
+ && !_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) _PR_FAST_INTSON(is);
+}
+
+PR_IMPLEMENT(void)
+PR_SetThreadDumpProc(PRThread* thread, PRThreadDumpProc dump, void *arg)
+{
+ thread->dump = dump;
+ thread->dumpArg = arg;
+}
diff --git a/pr/src/threads/prmon.c b/pr/src/threads/prmon.c
new file mode 100644
index 00000000..d6b708c1
--- /dev/null
+++ b/pr/src/threads/prmon.c
@@ -0,0 +1,202 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+/************************************************************************/
+
+/*
+** Create a new monitor.
+*/
+PR_IMPLEMENT(PRMonitor*) PR_NewMonitor()
+{
+ PRMonitor *mon;
+ PRCondVar *cvar;
+ PRLock *lock;
+
+ mon = PR_NEWZAP(PRMonitor);
+ if (mon) {
+ lock = PR_NewLock();
+ if (!lock) {
+ PR_DELETE(mon);
+ return 0;
+ }
+
+ cvar = PR_NewCondVar(lock);
+ if (!cvar) {
+ PR_DestroyLock(lock);
+ PR_DELETE(mon);
+ return 0;
+ }
+ mon->cvar = cvar;
+ mon->name = NULL;
+ }
+ return mon;
+}
+
+PR_IMPLEMENT(PRMonitor*) PR_NewNamedMonitor(const char* name)
+{
+ PRMonitor* mon = PR_NewMonitor();
+ mon->name = name;
+ return mon;
+}
+
+/*
+** Destroy a monitor. There must be no thread waiting on the monitor's
+** condition variable. The caller is responsible for guaranteeing that the
+** monitor is no longer in use.
+*/
+PR_IMPLEMENT(void) PR_DestroyMonitor(PRMonitor *mon)
+{
+ PR_DestroyLock(mon->cvar->lock);
+ PR_DestroyCondVar(mon->cvar);
+ PR_DELETE(mon);
+}
+
+/*
+** Enter the lock associated with the monitor.
+*/
+PR_IMPLEMENT(void) PR_EnterMonitor(PRMonitor *mon)
+{
+ if (mon->cvar->lock->owner == _PR_MD_CURRENT_THREAD()) {
+ mon->entryCount++;
+ } else {
+ PR_Lock(mon->cvar->lock);
+ mon->entryCount = 1;
+ }
+}
+
+/*
+** Test and then enter the lock associated with the monitor if it's not
+** already entered by some other thread. Return PR_FALSE if some other
+** thread owned the lock at the time of the call.
+*/
+PR_IMPLEMENT(PRBool) PR_TestAndEnterMonitor(PRMonitor *mon)
+{
+ if (mon->cvar->lock->owner == _PR_MD_CURRENT_THREAD()) {
+ mon->entryCount++;
+ return PR_TRUE;
+ } else {
+ if (PR_TestAndLock(mon->cvar->lock)) {
+ mon->entryCount = 1;
+ return PR_TRUE;
+ }
+ }
+ return PR_FALSE;
+}
+
+/*
+** Exit the lock associated with the monitor once.
+*/
+PR_IMPLEMENT(PRStatus) PR_ExitMonitor(PRMonitor *mon)
+{
+ if (mon->cvar->lock->owner != _PR_MD_CURRENT_THREAD()) {
+ return PR_FAILURE;
+ }
+ if (--mon->entryCount == 0) {
+ return PR_Unlock(mon->cvar->lock);
+ }
+ return PR_SUCCESS;
+}
+
+/*
+** Return the number of times that the current thread has entered the
+** lock. Returns zero if the current thread has not entered the lock.
+*/
+PR_IMPLEMENT(PRIntn) PR_GetMonitorEntryCount(PRMonitor *mon)
+{
+ return (mon->cvar->lock->owner == _PR_MD_CURRENT_THREAD()) ?
+ mon->entryCount : 0;
+}
+
+/*
+** Wait for a notify on the condition variable. Sleep for "ticks" amount
+** of time (if "tick" is 0 then the sleep is indefinite). While
+** the thread is waiting it exits the monitors lock (as if it called
+** PR_ExitMonitor as many times as it had called PR_EnterMonitor). When
+** the wait has finished the thread regains control of the monitors lock
+** with the same entry count as before the wait began.
+**
+** The thread waiting on the monitor will be resumed when the monitor is
+** notified (assuming the thread is the next in line to receive the
+** notify) or when the "ticks" elapses.
+**
+** Returns PR_FAILURE if the caller has not locked the lock associated
+** with the condition variable.
+** This routine can return PR_PENDING_INTERRUPT if the waiting thread
+** has been interrupted.
+*/
+PR_IMPLEMENT(PRStatus) PR_Wait(PRMonitor *mon, PRIntervalTime ticks)
+{
+ PRUintn entryCount;
+ PRStatus status;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (mon->cvar->lock->owner != me) return PR_FAILURE;
+
+ entryCount = mon->entryCount;
+ mon->entryCount = 0;
+
+ status = _PR_WaitCondVar(me, mon->cvar, mon->cvar->lock, ticks);
+
+ mon->entryCount = entryCount;
+
+ return status;
+}
+
+/*
+** Notify the highest priority thread waiting on the condition
+** variable. If a thread is waiting on the condition variable (using
+** PR_Wait) then it is awakened and begins waiting on the monitor's lock.
+*/
+PR_IMPLEMENT(PRStatus) PR_Notify(PRMonitor *mon)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ if (mon->cvar->lock->owner != me) return PR_FAILURE;
+ PR_NotifyCondVar(mon->cvar);
+ return PR_SUCCESS;
+}
+
+/*
+** Notify all of the threads waiting on the condition variable. All of
+** threads are notified in turn. The highest priority thread will
+** probably acquire the monitor first when the monitor is exited.
+*/
+PR_IMPLEMENT(PRStatus) PR_NotifyAll(PRMonitor *mon)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ if (mon->cvar->lock->owner != me) return PR_FAILURE;
+ PR_NotifyAllCondVar(mon->cvar);
+ return PR_SUCCESS;
+}
+
+/************************************************************************/
+
+PRUint32 _PR_MonitorToString(PRMonitor *mon, char *buf, PRUint32 buflen)
+{
+ PRUint32 nb;
+
+ if (mon->cvar->lock->owner) {
+ nb = PR_snprintf(buf, buflen, "[%p] owner=%d[%p] count=%ld",
+ mon, mon->cvar->lock->owner->id,
+ mon->cvar->lock->owner, mon->entryCount);
+ } else {
+ nb = PR_snprintf(buf, buflen, "[%p]", mon);
+ }
+ return nb;
+}
diff --git a/pr/src/threads/prsem.c b/pr/src/threads/prsem.c
new file mode 100644
index 00000000..62695e78
--- /dev/null
+++ b/pr/src/threads/prsem.c
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+#if defined(XP_MAC)
+#include "prsem.h"
+#else
+#include "obsolete/prsem.h"
+#endif
+
+/************************************************************************/
+
+/*
+** Create a new semaphore.
+*/
+PR_IMPLEMENT(PRSemaphore*) PR_NewSem(PRUintn value)
+{
+ PRSemaphore *sem;
+ PRCondVar *cvar;
+ PRLock *lock;
+
+ sem = PR_NEWZAP(PRSemaphore);
+ if (sem) {
+#ifdef HAVE_CVAR_BUILT_ON_SEM
+ _PR_MD_NEW_SEM(&sem->md, value);
+#else
+ lock = PR_NewLock();
+ if (!lock) {
+ PR_DELETE(sem);
+ return NULL;
+ }
+
+ cvar = PR_NewCondVar(lock);
+ if (!cvar) {
+ PR_DestroyLock(lock);
+ PR_DELETE(sem);
+ return NULL;
+ }
+ sem->cvar = cvar;
+ sem->count = value;
+#endif
+ }
+ return sem;
+}
+
+/*
+** Destroy a semaphore. There must be no thread waiting on the semaphore.
+** The caller is responsible for guaranteeing that the semaphore is
+** no longer in use.
+*/
+PR_IMPLEMENT(void) PR_DestroySem(PRSemaphore *sem)
+{
+#ifdef HAVE_CVAR_BUILT_ON_SEM
+ _PR_MD_DESTROY_SEM(&sem->md);
+#else
+ PR_ASSERT(sem->waiters == 0);
+
+ PR_DestroyLock(sem->cvar->lock);
+ PR_DestroyCondVar(sem->cvar);
+#endif
+ PR_DELETE(sem);
+}
+
+/*
+** Wait on a Semaphore.
+**
+** This routine allows a calling thread to wait or proceed depending upon the
+** state of the semahore sem. The thread can proceed only if the counter value
+** of the semaphore sem is currently greater than 0. If the value of semaphore
+** sem is positive, it is decremented by one and the routine returns immediately
+** allowing the calling thread to continue. If the value of semaphore sem is 0,
+** the calling thread blocks awaiting the semaphore to be released by another
+** thread.
+**
+** This routine can return PR_PENDING_INTERRUPT if the waiting thread
+** has been interrupted.
+*/
+PR_IMPLEMENT(PRStatus) PR_WaitSem(PRSemaphore *sem)
+{
+ PRStatus status = PR_SUCCESS;
+
+#ifdef HAVE_CVAR_BUILT_ON_SEM
+ return _PR_MD_WAIT_SEM(&sem->md);
+#else
+ PR_Lock(sem->cvar->lock);
+ while (sem->count == 0) {
+ sem->waiters++;
+ status = PR_WaitCondVar(sem->cvar, PR_INTERVAL_NO_TIMEOUT);
+ sem->waiters--;
+ if (status != PR_SUCCESS)
+ break;
+ }
+ if (status == PR_SUCCESS)
+ sem->count--;
+ PR_Unlock(sem->cvar->lock);
+#endif
+
+ return (status);
+}
+
+/*
+** This routine increments the counter value of the semaphore. If other threads
+** are blocked for the semaphore, then the scheduler will determine which ONE
+** thread will be unblocked.
+*/
+PR_IMPLEMENT(void) PR_PostSem(PRSemaphore *sem)
+{
+#ifdef HAVE_CVAR_BUILT_ON_SEM
+ _PR_MD_POST_SEM(&sem->md);
+#else
+ PR_Lock(sem->cvar->lock);
+ if (sem->waiters)
+ PR_NotifyCondVar(sem->cvar);
+ sem->count++;
+ PR_Unlock(sem->cvar->lock);
+#endif
+}
+
+#if DEBUG
+/*
+** Returns the value of the semaphore referenced by sem without affecting
+** the state of the semaphore. The value represents the semaphore vaule
+** at the time of the call, but may not be the actual value when the
+** caller inspects it. (FOR DEBUGGING ONLY)
+*/
+PR_IMPLEMENT(PRUintn) PR_GetValueSem(PRSemaphore *sem)
+{
+ PRUintn rv;
+
+#ifdef HAVE_CVAR_BUILT_ON_SEM
+ rv = _PR_MD_GET_VALUE_SEM(&sem->md);
+#else
+ PR_Lock(sem->cvar->lock);
+ rv = sem->count;
+ PR_Unlock(sem->cvar->lock);
+#endif
+
+ return rv;
+}
+#endif
diff --git a/pr/src/threads/prtpd.c b/pr/src/threads/prtpd.c
new file mode 100644
index 00000000..b92ebbbb
--- /dev/null
+++ b/pr/src/threads/prtpd.c
@@ -0,0 +1,296 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+#include <string.h>
+
+/*
+** Thread Private Data
+**
+** There is an aribitrary limit on the number of keys that will be allocated
+** by the runtime. It's largish, so it is intended to be a sanity check, not
+** an impediment.
+**
+** There is a counter, initialized to zero and incremented every time a
+** client asks for a new key, that holds the high water mark for keys. All
+** threads logically have the same high water mark and are permitted to
+** ask for TPD up to that key value.
+**
+** The vector to hold the TPD are allocated when PR_SetThreadPrivate() is
+** called. The size of the vector will be some value greater than or equal
+** to the current high water mark. Each thread has its own TPD length and
+** vector.
+**
+** Threads that get private data for keys they have not set (or perhaps
+** don't even exist for that thread) get a NULL return. If the key is
+** beyond the high water mark, an error will be returned.
+*/
+
+#define _PR_TPD_MODULO 8 /* vectors are extended by this much */
+#define _PR_TPD_LIMIT 128 /* arbitary limit on the TPD slots */
+static PRUintn _pr_tpd_highwater = 0; /* next TPD key to be assigned */
+static PRUintn _pr_tpd_length = 0; /* current length of destructor vector */
+PRThreadPrivateDTOR *_pr_tpd_destructors = NULL;
+ /* the destructors are associated with
+ the keys, therefore asserting that
+ the TPD key depicts the data's 'type' */
+
+/* Lock protecting the index assignment of per-thread-private data table */
+#ifdef _PR_NO_PREEMPT
+#define _PR_NEW_LOCK_TPINDEX()
+#define _PR_FREE_LOCK_TPINDEX()
+#define _PR_LOCK_TPINDEX()
+#define _PR_UNLOCK_TPINDEX()
+#else
+#ifdef _PR_LOCAL_THREADS_ONLY
+#define _PR_NEW_LOCK_TPINDEX()
+#define _PR_FREE_LOCK_TPINDEX()
+#define _PR_LOCK_TPINDEX() _PR_INTSOFF(_is)
+#define _PR_UNLOCK_TPINDEX() _PR_INTSON(_is)
+#else
+static PRLock *_pr_threadPrivateIndexLock;
+#define _PR_NEW_LOCK_TPINDEX() (_pr_threadPrivateIndexLock = PR_NewLock())
+#define _PR_FREE_LOCK_TPINDEX() (PR_DestroyLock(_pr_threadPrivateIndexLock))
+#define _PR_LOCK_TPINDEX() PR_Lock(_pr_threadPrivateIndexLock)
+#define _PR_UNLOCK_TPINDEX() PR_Unlock(_pr_threadPrivateIndexLock)
+#endif
+#endif
+
+/*
+** Initialize the thread private data manipulation
+*/
+void _PR_InitTPD()
+{
+ _PR_NEW_LOCK_TPINDEX();
+}
+
+/*
+** Clean up the thread private data manipulation
+*/
+void _PR_CleanupTPD(void)
+{
+ _PR_FREE_LOCK_TPINDEX();
+}
+
+/*
+** This routine returns a new index for per-thread-private data table.
+** The index is visible to all threads within a process. This index can
+** be used with the PR_SetThreadPrivate() and PR_GetThreadPrivate() routines
+** to save and retrieve data associated with the index for a thread.
+**
+** The index independently maintains specific values for each binding thread.
+** A thread can only get access to its own thread-specific-data.
+**
+** Upon a new index return the value associated with the index for all threads
+** is NULL, and upon thread creation the value associated with all indices for
+** that thread is NULL.
+**
+** "dtor" is the destructor function to invoke when the private
+** data is destroyed
+**
+** Returns PR_FAILURE if the total number of indices will exceed the maximun
+** allowed.
+*/
+
+PR_IMPLEMENT(PRStatus) PR_NewThreadPrivateIndex(
+ PRUintn *newIndex, PRThreadPrivateDTOR dtor)
+{
+ PRStatus rv;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+
+ if (_pr_tpd_highwater >= _PR_TPD_LIMIT)
+ {
+ PR_SetError(PR_TPD_RANGE_ERROR, 0);
+ rv = PR_FAILURE; /* that's just wrong */
+ }
+ else
+ {
+ PRThreadPrivateDTOR *old = NULL;
+ PRIntn _is;
+
+ _PR_LOCK_TPINDEX();
+ if (_pr_tpd_highwater >= _pr_tpd_length)
+ {
+ old = _pr_tpd_destructors;
+ _pr_tpd_destructors = PR_CALLOC(
+ (_pr_tpd_length + _PR_TPD_MODULO) * sizeof(PRThreadPrivateDTOR*));
+ if (NULL == _pr_tpd_destructors)
+ {
+ _pr_tpd_destructors = old; old = NULL;
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ rv = PR_FAILURE; /* that's just wrong */
+ goto failed; /* then extract one's self */
+ }
+ else
+ {
+ memcpy(
+ _pr_tpd_destructors, old,
+ _pr_tpd_length * sizeof(PRThreadPrivateDTOR*));
+ _pr_tpd_length += _PR_TPD_MODULO;
+ }
+ }
+
+ *newIndex = _pr_tpd_highwater++; /* this is really all we wanted */
+ _pr_tpd_destructors[*newIndex] = dtor; /* record destructor @index */
+
+failed:
+ _PR_UNLOCK_TPINDEX();
+ if (NULL != old) PR_DELETE(old);
+ rv = PR_SUCCESS;
+ }
+
+ return rv;
+}
+
+/*
+** Define some per-thread-private data.
+** "index" is an index into the per-thread private data table
+** "priv" is the per-thread-private data
+**
+** If the per-thread private data table has a previously registered
+** destructor function and a non-NULL per-thread-private data value,
+** the destructor function is invoked.
+**
+** This can return PR_FAILURE if index is invalid (ie., beyond the current
+** high water mark) or memory is insufficient to allocate an exanded vector.
+*/
+
+PR_IMPLEMENT(PRStatus) PR_SetThreadPrivate(PRUintn index, void *priv)
+{
+ PRStatus rv = PR_SUCCESS;
+ PRThread *self = PR_GetCurrentThread();
+
+ /*
+ ** The index being set might not have a sufficient vector in this
+ ** thread. But if the index has been allocated, it's okay to go
+ ** ahead and extend this one now.
+ */
+ if (index >= _pr_tpd_highwater)
+ {
+ PR_ASSERT(index < _pr_tpd_highwater);
+ PR_SetError(PR_TPD_RANGE_ERROR, 0);
+ rv = PR_FAILURE;
+ }
+ else
+ {
+ PRIntn _is;
+
+ _PR_LOCK_TPINDEX();
+ if ((NULL == self->privateData) || (self->tpdLength <= index))
+ {
+ void *extension = PR_CALLOC(_pr_tpd_length * sizeof(void*));
+ PR_ASSERT(
+ ((NULL == self->privateData) && (0 == self->tpdLength))
+ || ((NULL != self->privateData) && (0 != self->tpdLength)));
+ if (NULL != extension)
+ {
+ (void)memcpy(
+ extension, self->privateData,
+ self->tpdLength * sizeof(void*));
+ self->tpdLength = _pr_tpd_length;
+ self->privateData = extension;
+ }
+ }
+ /*
+ ** There wasn't much chance of having to call the destructor
+ ** unless the slot already existed.
+ */
+ else if (self->privateData[index] && _pr_tpd_destructors[index])
+ {
+ void *data = self->privateData[index];
+ self->privateData[index] = NULL;
+ _PR_UNLOCK_TPINDEX();
+ (*_pr_tpd_destructors[index])(data);
+ _PR_LOCK_TPINDEX();
+ }
+
+ /*
+ ** If the thread's private data is still NULL, then we couldn't
+ ** fix the problem. We must be outa-memory (again).
+ */
+ if (NULL == self->privateData)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ rv = PR_FAILURE;
+ }
+ else self->privateData[index] = priv;
+ _PR_UNLOCK_TPINDEX();
+ }
+
+ return rv;
+}
+
+/*
+** Recover the per-thread-private data for the current thread. "index" is
+** the index into the per-thread private data table.
+**
+** The returned value may be NULL which is indistinguishable from an error
+** condition.
+**
+*/
+
+PR_IMPLEMENT(void*) PR_GetThreadPrivate(uintn index)
+{
+ PRThread *self = PR_GetCurrentThread();
+ void *tpd = ((NULL == self->privateData) || (index >= self->tpdLength)) ?
+ NULL : self->privateData[index];
+
+ return tpd;
+}
+
+PR_IMPLEMENT(PRStatus) PR_SetThreadExit(PRUintn index, PRThreadExit func, void *arg)
+{
+ _PRPerThreadExit *pte;
+ PRThread *thread = _PR_MD_CURRENT_THREAD();
+
+ if (index >= thread->numExits) {
+ if (thread->ptes) {
+ thread->ptes = (_PRPerThreadExit*)
+ PR_REALLOC(thread->ptes, (index+1) * sizeof(_PRPerThreadExit));
+ } else {
+ thread->ptes = (_PRPerThreadExit*)
+ PR_CALLOC(index+1 * sizeof(_PRPerThreadExit));
+ }
+ if (!thread->ptes) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return PR_FAILURE;
+ }
+ thread->numExits = index + 1;
+ }
+ pte = &thread->ptes[index];
+ pte->func = func;
+ pte->arg = arg;
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRThreadExit) PR_GetThreadExit(PRUintn index, void **argp)
+{
+ _PRPerThreadExit *pte;
+ PRThread *thread = _PR_MD_CURRENT_THREAD();
+
+ if (index >= thread->numExits) {
+ if (argp) *argp = 0;
+ return 0;
+ }
+ pte = &thread->ptes[index];
+ if (argp) *argp = pte->arg;
+ return pte->func;
+}