summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorwtc%netscape.com <devnull@localhost>1998-10-29 21:04:47 +0000
committerwtc%netscape.com <devnull@localhost>1998-10-29 21:04:47 +0000
commitdc64dc5acd748e9759bf1498213eb9299e121cce (patch)
tree8d3a3fc2836b89267b4c6c18f05ff97f0039dd82
parentb2e26b9a802e9ffcae12a280520b1bd6fc58ebdd (diff)
parent34cef88189d1eed5fb9a7fd29d8e8b901d44b081 (diff)
downloadnspr-hg-dc64dc5acd748e9759bf1498213eb9299e121cce.tar.gz
Linux/m68k porting change (define _MD_SP_TYPE), contributed by
Jes Sorensen <Jes.Sorensen@cern.ch>.
-rw-r--r--Makefile105
-rw-r--r--lib/Makefile32
-rw-r--r--lib/ds/MANIFEST9
-rw-r--r--lib/ds/Makefile119
-rw-r--r--lib/ds/plarena.c353
-rw-r--r--lib/ds/plarena.h193
-rw-r--r--lib/ds/plarenas.h95
-rw-r--r--lib/ds/plevent.c731
-rw-r--r--lib/ds/plevent.h410
-rw-r--r--lib/ds/plhash.c477
-rw-r--r--lib/ds/plhash.h131
-rw-r--r--lib/ds/plvector.c318
-rw-r--r--lib/ds/plvector.h100
-rw-r--r--lib/libc/Makefile32
-rw-r--r--lib/libc/README20
-rw-r--r--lib/libc/include/MANIFEST9
-rw-r--r--lib/libc/include/Makefile37
-rw-r--r--lib/libc/include/README7
-rw-r--r--lib/libc/include/plbase64.h79
-rw-r--r--lib/libc/include/plerror.h47
-rw-r--r--lib/libc/include/plgetopt.h62
-rw-r--r--lib/libc/include/plresolv.h89
-rw-r--r--lib/libc/include/plstr.h428
-rw-r--r--lib/libc/src/Makefile150
-rw-r--r--lib/libc/src/README20
-rw-r--r--lib/libc/src/base64.c403
-rw-r--r--lib/libc/src/plerror.c149
-rw-r--r--lib/libc/src/plgetopt.c165
-rw-r--r--lib/libc/src/plvrsion.c69
-rw-r--r--lib/libc/src/strcat.c70
-rw-r--r--lib/libc/src/strccmp.c80
-rw-r--r--lib/libc/src/strchr.c83
-rw-r--r--lib/libc/src/strcmp.c52
-rw-r--r--lib/libc/src/strcpy.c69
-rw-r--r--lib/libc/src/strcstr.c104
-rw-r--r--lib/libc/src/strdup.c64
-rw-r--r--lib/libc/src/strlen.c50
-rw-r--r--lib/libc/src/strpbrk.c87
-rw-r--r--lib/libc/src/strstr.c104
-rw-r--r--lib/msgc/Makefile28
-rw-r--r--lib/msgc/include/MANIFEST5
-rw-r--r--lib/msgc/include/Makefile37
-rw-r--r--lib/msgc/include/gcint.h110
-rw-r--r--lib/msgc/include/prgc.h400
-rw-r--r--lib/msgc/src/Makefile116
-rw-r--r--lib/msgc/src/macgc.c56
-rw-r--r--lib/msgc/src/os2gc.c64
-rw-r--r--lib/msgc/src/prgcapi.c332
-rw-r--r--lib/msgc/src/prmsgc.c3495
-rw-r--r--lib/msgc/src/unixgc.c136
-rw-r--r--lib/msgc/src/win16gc.c57
-rw-r--r--lib/msgc/src/win32gc.c110
-rw-r--r--lib/msgc/tests/Makefile292
-rw-r--r--lib/msgc/tests/gc1.c238
-rw-r--r--lib/msgc/tests/thrashgc.c255
-rw-r--r--lib/prstreams/Makefile116
-rw-r--r--lib/prstreams/prstrms.cpp525
-rw-r--r--lib/prstreams/prstrms.h128
-rw-r--r--lib/prstreams/tests/testprstrm/Makefile213
-rw-r--r--lib/prstreams/tests/testprstrm/testprstrm.cpp182
-rw-r--r--lib/tests/Makefile192
-rw-r--r--lib/tests/arena.c265
-rw-r--r--lib/tests/base64t.c3028
-rw-r--r--lib/tests/string.c3043
-rw-r--r--macbuild/NSPR.Debug.Prefix1
-rw-r--r--macbuild/NSPR.Prefix1
-rw-r--r--macbuild/NSPR20PPC.mcpbin0 -> 85613 bytes
-rw-r--r--macbuild/NSPRConfig.h26
-rw-r--r--macbuild/NSPRDebug.Prefix11
-rw-r--r--makefile.win97
-rw-r--r--pr/Makefile24
-rw-r--r--pr/include/MANIFEST45
-rw-r--r--pr/include/Makefile37
-rw-r--r--pr/include/gencfg.c284
-rw-r--r--pr/include/md/Makefile148
-rw-r--r--pr/include/md/_aix.cfg121
-rw-r--r--pr/include/md/_aix.h221
-rw-r--r--pr/include/md/_bsdi.cfg121
-rw-r--r--pr/include/md/_bsdi.h177
-rw-r--r--pr/include/md/_darwin.cfg127
-rw-r--r--pr/include/md/_darwin.h205
-rw-r--r--pr/include/md/_dgux.cfg119
-rw-r--r--pr/include/md/_dgux.h202
-rw-r--r--pr/include/md/_freebsd.cfg119
-rw-r--r--pr/include/md/_freebsd.h207
-rw-r--r--pr/include/md/_hpux.h221
-rw-r--r--pr/include/md/_hpux32.cfg121
-rw-r--r--pr/include/md/_hpux64.cfg122
-rw-r--r--pr/include/md/_irix.h442
-rw-r--r--pr/include/md/_irix32.cfg128
-rw-r--r--pr/include/md/_irix64.cfg127
-rw-r--r--pr/include/md/_linux.cfg404
-rw-r--r--pr/include/md/_linux.h412
-rw-r--r--pr/include/md/_macos.h566
-rw-r--r--pr/include/md/_ncr.cfg121
-rw-r--r--pr/include/md/_ncr.h212
-rw-r--r--pr/include/md/_nec.cfg121
-rw-r--r--pr/include/md/_nec.h177
-rw-r--r--pr/include/md/_netbsd.cfg262
-rw-r--r--pr/include/md/_netbsd.h211
-rw-r--r--pr/include/md/_nspr_pthread.h266
-rw-r--r--pr/include/md/_openbsd.cfg262
-rw-r--r--pr/include/md/_openbsd.h171
-rw-r--r--pr/include/md/_os2.cfg127
-rw-r--r--pr/include/md/_os2.h498
-rw-r--r--pr/include/md/_os2_errors.h133
-rw-r--r--pr/include/md/_osf1.cfg125
-rw-r--r--pr/include/md/_osf1.h209
-rw-r--r--pr/include/md/_pcos.h86
-rw-r--r--pr/include/md/_pth.h223
-rw-r--r--pr/include/md/_reliantunix.cfg126
-rw-r--r--pr/include/md/_reliantunix.h249
-rw-r--r--pr/include/md/_rhapsody.cfg127
-rw-r--r--pr/include/md/_rhapsody.h205
-rw-r--r--pr/include/md/_scoos.cfg121
-rw-r--r--pr/include/md/_scoos.h185
-rw-r--r--pr/include/md/_solaris.cfg129
-rw-r--r--pr/include/md/_solaris.h709
-rw-r--r--pr/include/md/_sony.cfg121
-rw-r--r--pr/include/md/_sony.h185
-rw-r--r--pr/include/md/_sunos4.cfg119
-rw-r--r--pr/include/md/_sunos4.h217
-rw-r--r--pr/include/md/_unix_errors.h152
-rw-r--r--pr/include/md/_unixos.h571
-rw-r--r--pr/include/md/_unixware.cfg121
-rw-r--r--pr/include/md/_unixware.h200
-rw-r--r--pr/include/md/_win16.cfg158
-rw-r--r--pr/include/md/_win16.h556
-rw-r--r--pr/include/md/_win32_errors.h132
-rw-r--r--pr/include/md/_win95.cfg179
-rw-r--r--pr/include/md/_win95.h437
-rw-r--r--pr/include/md/_winnt.cfg179
-rw-r--r--pr/include/md/_winnt.h504
-rw-r--r--pr/include/md/prosdep.h130
-rw-r--r--pr/include/md/sunos4.h146
-rw-r--r--pr/include/nspr.h49
-rw-r--r--pr/include/obsolete/Makefile35
-rw-r--r--pr/include/obsolete/pralarm.h175
-rw-r--r--pr/include/obsolete/probslet.h225
-rw-r--r--pr/include/obsolete/protypes.h176
-rw-r--r--pr/include/obsolete/prsem.h77
-rw-r--r--pr/include/pratom.h151
-rw-r--r--pr/include/prbit.h92
-rw-r--r--pr/include/prclist.h121
-rw-r--r--pr/include/prcmon.h73
-rw-r--r--pr/include/prcountr.h512
-rw-r--r--pr/include/prcvar.h107
-rw-r--r--pr/include/prdtoa.h71
-rw-r--r--pr/include/prenv.h40
-rw-r--r--pr/include/prerror.h205
-rw-r--r--pr/include/prinet.h93
-rw-r--r--pr/include/prinit.h223
-rw-r--r--pr/include/prinrval.h156
-rw-r--r--pr/include/prio.h1685
-rw-r--r--pr/include/private/Makefile35
-rw-r--r--pr/include/private/pprio.h202
-rw-r--r--pr/include/private/pprmwait.h117
-rw-r--r--pr/include/private/pprthred.h344
-rw-r--r--pr/include/private/primpl.h1683
-rw-r--r--pr/include/private/prpriv.h34
-rw-r--r--pr/include/prlink.h140
-rw-r--r--pr/include/prlock.h102
-rw-r--r--pr/include/prlog.h237
-rw-r--r--pr/include/prlong.h409
-rw-r--r--pr/include/prmem.h140
-rw-r--r--pr/include/prmon.h94
-rw-r--r--pr/include/prmwait.h393
-rw-r--r--pr/include/prnetdb.h295
-rw-r--r--pr/include/prolock.h196
-rw-r--r--pr/include/prpdce.h99
-rw-r--r--pr/include/prprf.h135
-rw-r--r--pr/include/prproces.h93
-rw-r--r--pr/include/prsystem.h79
-rw-r--r--pr/include/prthread.h254
-rw-r--r--pr/include/prtime.h278
-rw-r--r--pr/include/prtrace.h635
-rw-r--r--pr/include/prtypes.h391
-rwxr-xr-xpr/include/prvrsion.h108
-rw-r--r--pr/include/prwin16.h177
-rw-r--r--pr/src/Makefile367
-rw-r--r--pr/src/io/Makefile66
-rw-r--r--pr/src/io/prdir.c83
-rw-r--r--pr/src/io/prfdcach.c287
-rw-r--r--pr/src/io/prfile.c565
-rw-r--r--pr/src/io/prio.c112
-rw-r--r--pr/src/io/priometh.c276
-rw-r--r--pr/src/io/pripv6.c26
-rw-r--r--pr/src/io/prlayer.c581
-rw-r--r--pr/src/io/prlog.c485
-rw-r--r--pr/src/io/prmapopt.c423
-rw-r--r--pr/src/io/prmmap.c69
-rw-r--r--pr/src/io/prmwait.c1460
-rw-r--r--pr/src/io/prpolevt.c121
-rw-r--r--pr/src/io/prprf.c1226
-rw-r--r--pr/src/io/prscanf.c637
-rw-r--r--pr/src/io/prsocket.c1605
-rw-r--r--pr/src/io/prstdio.c46
-rw-r--r--pr/src/linking/Makefile43
-rw-r--r--pr/src/linking/prlink.c973
-rw-r--r--pr/src/malloc/Makefile41
-rw-r--r--pr/src/malloc/prmalloc.c1156
-rw-r--r--pr/src/malloc/prmem.c254
-rw-r--r--pr/src/md/Makefile57
-rw-r--r--pr/src/md/mac/MANIFEST7
-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.c1875
-rw-r--r--pr/src/md/mac/macsockclient.c543
-rw-r--r--pr/src/md/mac/macsocket.h219
-rw-r--r--pr/src/md/mac/macsockotpt.c1585
-rw-r--r--pr/src/md/mac/macthr.c380
-rw-r--r--pr/src/md/mac/mactime.c266
-rw-r--r--pr/src/md/mac/mdmac.c840
-rw-r--r--pr/src/md/mac/prcpucfg.h115
-rw-r--r--pr/src/md/os2/Makefile50
-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.c258
-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/Makefile265
-rw-r--r--pr/src/md/unix/aix.c301
-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/darwin.c99
-rw-r--r--pr/src/md/unix/dgux.c90
-rw-r--r--pr/src/md/unix/freebsd.c100
-rw-r--r--pr/src/md/unix/hpux.c242
-rw-r--r--pr/src/md/unix/irix.c1657
-rw-r--r--pr/src/md/unix/linux.c104
-rw-r--r--pr/src/md/unix/ncr.c376
-rw-r--r--pr/src/md/unix/nec.c81
-rw-r--r--pr/src/md/unix/netbsd.c102
-rw-r--r--pr/src/md/unix/objs.mk211
-rw-r--r--pr/src/md/unix/openbsd.c102
-rw-r--r--pr/src/md/unix/os_AIX.s103
-rw-r--r--pr/src/md/unix/os_BSD_386_2.s55
-rw-r--r--pr/src/md/unix/os_Irix.s89
-rw-r--r--pr/src/md/unix/os_ReliantUNIX.s109
-rw-r--r--pr/src/md/unix/os_SunOS.s134
-rw-r--r--pr/src/md/unix/os_SunOS_ultrasparc.s184
-rw-r--r--pr/src/md/unix/os_SunOS_x86.s60
-rw-r--r--pr/src/md/unix/osf1.c145
-rw-r--r--pr/src/md/unix/pthreads_user.c461
-rw-r--r--pr/src/md/unix/reliantunix.c114
-rw-r--r--pr/src/md/unix/rhapsody.c99
-rw-r--r--pr/src/md/unix/scoos.c162
-rw-r--r--pr/src/md/unix/solaris.c835
-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.c3705
-rw-r--r--pr/src/md/unix/unix_errors.c1558
-rw-r--r--pr/src/md/unix/unixware.c562
-rw-r--r--pr/src/md/unix/uxpoll.c681
-rw-r--r--pr/src/md/unix/uxproces.c760
-rw-r--r--pr/src/md/unix/uxwrap.c517
-rw-r--r--pr/src/md/windows/Makefile77
-rw-r--r--pr/src/md/windows/ntdllmn.c69
-rw-r--r--pr/src/md/windows/ntgc.c107
-rw-r--r--pr/src/md/windows/ntinrval.c84
-rw-r--r--pr/src/md/windows/ntio.c3912
-rw-r--r--pr/src/md/windows/ntmisc.c719
-rw-r--r--pr/src/md/windows/ntsem.c65
-rw-r--r--pr/src/md/windows/ntthread.c486
-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.c65
-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.c288
-rw-r--r--pr/src/md/windows/w95cv.c328
-rw-r--r--pr/src/md/windows/w95dllmain.c52
-rw-r--r--pr/src/md/windows/w95io.c885
-rw-r--r--pr/src/md/windows/w95sock.c612
-rw-r--r--pr/src/md/windows/w95thred.c257
-rw-r--r--pr/src/md/windows/win32_errors.c1240
-rw-r--r--pr/src/memory/Makefile42
-rw-r--r--pr/src/memory/prseg.c61
-rw-r--r--pr/src/misc/Makefile67
-rw-r--r--pr/src/misc/pralarm.c263
-rw-r--r--pr/src/misc/pratom.c222
-rw-r--r--pr/src/misc/prcountr.c510
-rw-r--r--pr/src/misc/prdtoa.c2569
-rw-r--r--pr/src/misc/prenv.c57
-rw-r--r--pr/src/misc/prerror.c89
-rw-r--r--pr/src/misc/prinit.c577
-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.c827
-rw-r--r--pr/src/misc/prolock.c91
-rw-r--r--pr/src/misc/prsystem.c91
-rw-r--r--pr/src/misc/prthinfo.c225
-rw-r--r--pr/src/misc/prtime.c1938
-rw-r--r--pr/src/misc/prtrace.c912
-rw-r--r--pr/src/nspr.rc126
-rw-r--r--pr/src/prvrsion.c69
-rw-r--r--pr/src/pthreads/Makefile47
-rw-r--r--pr/src/pthreads/ptio.c3565
-rw-r--r--pr/src/pthreads/ptmisc.c44
-rw-r--r--pr/src/pthreads/ptsynch.c741
-rw-r--r--pr/src/pthreads/ptthread.c1399
-rw-r--r--pr/src/resource.h33
-rw-r--r--pr/src/threads/Makefile61
-rw-r--r--pr/src/threads/combined/Makefile52
-rw-r--r--pr/src/threads/combined/README62
-rw-r--r--pr/src/threads/combined/prucpu.c308
-rw-r--r--pr/src/threads/combined/prucv.c633
-rw-r--r--pr/src/threads/combined/prulock.c428
-rw-r--r--pr/src/threads/combined/prustack.c183
-rw-r--r--pr/src/threads/combined/pruthr.c1818
-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.c284
-rw-r--r--pr/tests/Makefile481
-rw-r--r--pr/tests/accept.c505
-rw-r--r--pr/tests/alarm.c546
-rw-r--r--pr/tests/atomic.c103
-rw-r--r--pr/tests/attach.c336
-rw-r--r--pr/tests/bigfile.c299
-rw-r--r--pr/tests/bug1test.c238
-rw-r--r--pr/tests/cleanup.c112
-rw-r--r--pr/tests/cltsrv.c1197
-rw-r--r--pr/tests/concur.c173
-rw-r--r--pr/tests/cvar.c315
-rw-r--r--pr/tests/cvar2.c989
-rw-r--r--pr/tests/dbmalloc.c328
-rw-r--r--pr/tests/dbmalloc1.c122
-rw-r--r--pr/tests/dceemu.c113
-rw-r--r--pr/tests/depend.c134
-rw-r--r--pr/tests/dll/Makefile94
-rw-r--r--pr/tests/dll/mygetval.c37
-rw-r--r--pr/tests/dll/mysetval.c26
-rw-r--r--pr/tests/dlltest.c202
-rw-r--r--pr/tests/dtoa.c198
-rw-r--r--pr/tests/exit.c118
-rw-r--r--pr/tests/fileio.c231
-rw-r--r--pr/tests/foreign.c355
-rw-r--r--pr/tests/forktest.c327
-rw-r--r--pr/tests/fsync.c136
-rw-r--r--pr/tests/getproto.c95
-rw-r--r--pr/tests/i2l.c114
-rw-r--r--pr/tests/initclk.c87
-rw-r--r--pr/tests/inrval.c223
-rw-r--r--pr/tests/instrumt.c469
-rw-r--r--pr/tests/intrupt.c283
-rw-r--r--pr/tests/io_timeout.c217
-rw-r--r--pr/tests/io_timeoutk.c214
-rw-r--r--pr/tests/io_timeoutu.c215
-rw-r--r--pr/tests/ioconthr.c123
-rw-r--r--pr/tests/ipv6.c241
-rw-r--r--pr/tests/join.c199
-rw-r--r--pr/tests/joinkk.c174
-rw-r--r--pr/tests/joinku.c180
-rw-r--r--pr/tests/joinuk.c176
-rw-r--r--pr/tests/joinuu.c178
-rw-r--r--pr/tests/layer.c371
-rw-r--r--pr/tests/lazyinit.c120
-rw-r--r--pr/tests/lltest.c732
-rw-r--r--pr/tests/lock.c528
-rw-r--r--pr/tests/lockfile.c260
-rw-r--r--pr/tests/logger.c143
-rw-r--r--pr/tests/many_cv.c129
-rw-r--r--pr/tests/multiwait.c704
-rw-r--r--pr/tests/nbconn.c572
-rw-r--r--pr/tests/nblayer.c635
-rw-r--r--pr/tests/nonblock.c251
-rw-r--r--pr/tests/op_2long.c96
-rw-r--r--pr/tests/op_filnf.c72
-rw-r--r--pr/tests/op_filok.c83
-rw-r--r--pr/tests/op_noacc.c75
-rw-r--r--pr/tests/op_nofil.c80
-rw-r--r--pr/tests/parent.c129
-rw-r--r--pr/tests/perf.c466
-rwxr-xr-xpr/tests/poll_er.c214
-rw-r--r--pr/tests/poll_nm.c377
-rw-r--r--pr/tests/poll_to.c188
-rw-r--r--pr/tests/pollable.c273
-rw-r--r--pr/tests/prftest.c77
-rw-r--r--pr/tests/prftest1.c133
-rw-r--r--pr/tests/prftest2.c110
-rw-r--r--pr/tests/priotest.c214
-rw-r--r--pr/tests/provider.c1378
-rw-r--r--pr/tests/prpoll.c273
-rw-r--r--pr/tests/prselect.c353
-rw-r--r--pr/tests/prttools.h24
-rw-r--r--pr/tests/ranfile.c410
-rw-r--r--pr/tests/sel_spd.c546
-rwxr-xr-xpr/tests/selct_er.c204
-rw-r--r--pr/tests/selct_nm.c297
-rw-r--r--pr/tests/selct_to.c185
-rw-r--r--pr/tests/select2.c330
-rw-r--r--pr/tests/sem.c234
-rw-r--r--pr/tests/server_test.c617
-rw-r--r--pr/tests/servr_kk.c584
-rw-r--r--pr/tests/servr_ku.c564
-rw-r--r--pr/tests/servr_uk.c566
-rw-r--r--pr/tests/servr_uu.c564
-rw-r--r--pr/tests/short_thread.c71
-rw-r--r--pr/tests/sigpipe.c74
-rw-r--r--pr/tests/sleep.c108
-rw-r--r--pr/tests/socket.c1633
-rw-r--r--pr/tests/sockopt.c252
-rw-r--r--pr/tests/sprintf.c435
-rw-r--r--pr/tests/sproc_ch.c100
-rw-r--r--pr/tests/sproc_p.c82
-rw-r--r--pr/tests/stack.c266
-rw-r--r--pr/tests/stat.c88
-rw-r--r--pr/tests/stdio.c64
-rw-r--r--pr/tests/strod.c87
-rw-r--r--pr/tests/suspend.c201
-rw-r--r--pr/tests/switch.c255
-rw-r--r--pr/tests/system.c62
-rw-r--r--pr/tests/testbit.c110
-rw-r--r--pr/tests/testfile.c799
-rw-r--r--pr/tests/threads.c230
-rw-r--r--pr/tests/thruput.c398
-rw-r--r--pr/tests/time.c182
-rw-r--r--pr/tests/timemac.c134
-rw-r--r--pr/tests/timetest.c760
-rw-r--r--pr/tests/tmoacc.c309
-rw-r--r--pr/tests/tmocon.c439
-rw-r--r--pr/tests/tpd.c317
-rw-r--r--pr/tests/udpsrv.c547
-rw-r--r--pr/tests/ut_ttools.h24
-rw-r--r--pr/tests/version.c101
-rw-r--r--pr/tests/w16gui/Makefile76
-rw-r--r--pr/tests/w16gui/popfile.c148
-rw-r--r--pr/tests/w16gui/popfind.c130
-rw-r--r--pr/tests/w16gui/popfont.c75
-rw-r--r--pr/tests/w16gui/poppad.c653
-rw-r--r--pr/tests/w16gui/poppad.h47
-rw-r--r--pr/tests/w16gui/poppad.icobin0 -> 326 bytes
-rw-r--r--pr/tests/w16gui/poppad.rc102
-rw-r--r--pr/tests/w16gui/popprnt0.c30
-rw-r--r--pr/tests/w16gui/readme.1st37
-rw-r--r--pr/tests/writev.c215
-rw-r--r--pr/tests/xnotify.c370
-rw-r--r--pr/tests/yield.c62
-rw-r--r--tools/Makefile212
-rw-r--r--tools/httpget.c446
-rw-r--r--tools/tail.c147
457 files changed, 148888 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..45678d0f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,105 @@
+#! gmake
+
+#
+# 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 = .
+
+DIRS = config pr lib
+
+ifdef MOZILLA_CLIENT
+PR_CLIENT_BUILD = 1
+PR_CLIENT_BUILD_UNIX = 1
+endif
+
+include $(MOD_DEPTH)/config/rules.mk
+
+#
+# The -ll option of zip converts CR LF to LF.
+#
+ifeq ($(OS_ARCH),WINNT)
+ZIP_ASCII_OPT = -ll
+endif
+
+ifdef PR_CLIENT_BUILD
+export::
+ rm -r -f $(DIST)/../public/nspr
+ifdef PR_CLIENT_BUILD_UNIX
+ rm -f $(DIST)/lib/libnspr.a
+ rm -f $(DIST)/bin/libnspr.$(DLL_SUFFIX)
+endif
+endif
+
+release::
+ echo $(BUILD_NUMBER) > $(RELEASE_DIR)/$(BUILD_NUMBER)/version.df
+ @if test -f imports.df; then \
+ echo "cp -f imports.df $(RELEASE_DIR)/$(BUILD_NUMBER)/imports.df"; \
+ cp -f imports.df $(RELEASE_DIR)/$(BUILD_NUMBER)/imports.df; \
+ else \
+ echo "echo > $(RELEASE_DIR)/$(BUILD_NUMBER)/imports.df"; \
+ echo > $(RELEASE_DIR)/$(BUILD_NUMBER)/imports.df; \
+ fi
+ cd $(RELEASE_DIR)/$(BUILD_NUMBER)/$(OBJDIR_NAME); \
+ rm -rf META-INF; mkdir META-INF; cd META-INF; \
+ echo "Manifest-Version: 1.0" > MANIFEST.MF; \
+ echo "" >> MANIFEST.MF; \
+ cd ..; rm -f mdbinary.jar; zip -r mdbinary.jar META-INF lib; \
+ rm -rf META-INF; \
+ cd include; \
+ rm -rf META-INF; mkdir META-INF; cd META-INF; \
+ echo "Manifest-Version: 1.0" > MANIFEST.MF; \
+ echo "" >> MANIFEST.MF; \
+ cd ..; rm -f mdheader.jar; zip $(ZIP_ASCII_OPT) -r mdheader.jar *; \
+ rm -rf META-INF
+ifeq ($(OS_ARCH),WINNT)
+ @if test ! -d $(MDIST)/$(MOD_NAME)/$(BUILD_NUMBER); then \
+ rm -rf $(MDIST)/$(MOD_NAME)/$(BUILD_NUMBER); \
+ echo "making directory /m/dist/$(MOD_NAME)/$(BUILD_NUMBER)"; \
+ config/prmkdir.bat $(MDIST_DOS)\\$(MOD_NAME)\\$(BUILD_NUMBER); \
+ fi
+ @if test ! -d $(MDIST)/$(MOD_NAME)/$(BUILD_NUMBER)/$(OBJDIR_NAME); then \
+ rm -rf $(MDIST)/$(MOD_NAME)/$(BUILD_NUMBER)/$(OBJDIR_NAME); \
+ echo "making directory /m/dist/$(MOD_NAME)/$(BUILD_NUMBER)/$(OBJDIR_NAME)"; \
+ config/prmkdir.bat $(MDIST_DOS)\\$(MOD_NAME)\\$(BUILD_NUMBER)\\$(OBJDIR_NAME); \
+ fi
+else
+ @if test ! -d $(MDIST)/$(MOD_NAME)/$(BUILD_NUMBER); then \
+ rm -rf $(MDIST)/$(MOD_NAME)/$(BUILD_NUMBER); \
+ echo "making directory /m/dist/$(MOD_NAME)/$(BUILD_NUMBER)"; \
+ $(NSINSTALL) -D $(MDIST)/$(MOD_NAME)/$(BUILD_NUMBER); \
+ chmod 775 $(MDIST)/$(MOD_NAME)/$(BUILD_NUMBER); \
+ fi
+ @if test ! -d $(MDIST)/$(MOD_NAME)/$(BUILD_NUMBER)/$(OBJDIR_NAME); then \
+ rm -rf $(MDIST)/$(MOD_NAME)/$(BUILD_NUMBER)/$(OBJDIR_NAME); \
+ echo "making directory /m/dist/$(MOD_NAME)/$(BUILD_NUMBER)/$(OBJDIR_NAME)"; \
+ $(NSINSTALL) -D $(MDIST)/$(MOD_NAME)/$(BUILD_NUMBER)/$(OBJDIR_NAME); \
+ chmod 775 $(MDIST)/$(MOD_NAME)/$(BUILD_NUMBER)/$(OBJDIR_NAME); \
+ fi
+endif
+ cd $(RELEASE_DIR)/$(BUILD_NUMBER); \
+ cp -f version.df imports.df $(MDIST)/$(MOD_NAME)/$(BUILD_NUMBER); \
+ chmod 664 $(MDIST)/$(MOD_NAME)/$(BUILD_NUMBER)/version.df; \
+ chmod 664 $(MDIST)/$(MOD_NAME)/$(BUILD_NUMBER)/imports.df; \
+ cd $(OBJDIR_NAME); \
+ cp -f mdbinary.jar $(MDIST)/$(MOD_NAME)/$(BUILD_NUMBER)/$(OBJDIR_NAME); \
+ chmod 664 $(MDIST)/$(MOD_NAME)/$(BUILD_NUMBER)/$(OBJDIR_NAME)/mdbinary.jar; \
+ cd include; \
+ cp -f mdheader.jar $(MDIST)/$(MOD_NAME)/$(BUILD_NUMBER)/$(OBJDIR_NAME); \
+ chmod 664 $(MDIST)/$(MOD_NAME)/$(BUILD_NUMBER)/$(OBJDIR_NAME)/mdheader.jar
+
+depend:
+ @echo "NSPR20 has no dependencies. Skipped."
diff --git a/lib/Makefile b/lib/Makefile
new file mode 100644
index 00000000..cf279130
--- /dev/null
+++ b/lib/Makefile
@@ -0,0 +1,32 @@
+#
+# 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 = ..
+
+export NSPR20=1
+
+include $(MOD_DEPTH)/config/config.mk
+
+DIRS = ds libc
+
+include $(MOD_DEPTH)/config/rules.mk
+
+export:: $(TARGETS)
+
+install:: export
diff --git a/lib/ds/MANIFEST b/lib/ds/MANIFEST
new file mode 100644
index 00000000..b4ce1eb0
--- /dev/null
+++ b/lib/ds/MANIFEST
@@ -0,0 +1,9 @@
+#
+# This is a list of local files which get copied to the mozilla:dist directory
+#
+
+plarenas.h
+plarena.h
+plevent.h
+plhash.h
+plvector.h
diff --git a/lib/ds/Makefile b/lib/ds/Makefile
new file mode 100644
index 00000000..23e8c7bc
--- /dev/null
+++ b/lib/ds/Makefile
@@ -0,0 +1,119 @@
+#
+# 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
+
+INCLUDES = -I$(DIST)/include/private -I$(DIST)/include
+
+CSRCS = \
+ plarena.c \
+ plevent.c \
+ plhash.c \
+ plvector.c \
+ $(NULL)
+
+HEADERS = \
+ plarenas.h \
+ plarena.h \
+ plevent.h \
+ plhash.h \
+ plvector.h \
+ $(NULL)
+
+ifeq ($(OS_ARCH), WINNT)
+ifeq (,$(filter-out WIN16 OS2,$(OS_TARGET)))
+# OS_CFLAGS = $(OS_EXE_CFLAGS)
+EXTRA_LIBS = $(DIST)/lib/nspr$(MOD_VERSION).lib
+else
+DLLBASE=/BASE:0x30000000
+RES=$(OBJDIR)/ds.res
+RESNAME=$(MOD_DEPTH)/pr/src/nspr.rc
+OS_LIBS = user32.lib
+EXTRA_LIBS = $(DIST)/lib/libnspr$(MOD_VERSION).lib
+
+ifdef MOZ_DEBUG
+ifdef GLOWCODE
+EXTRA_LIBS += $(GLOWDIR)/glowcode.lib
+endif
+endif
+
+endif
+else
+ifeq ($(OS_ARCH), AIX)
+ifeq ($(CLASSIC_NSPR),1)
+OS_LIBS += -lc
+else
+OS_LIBS += -lc_r
+endif
+endif
+ifeq ($(OS_ARCH)$(OS_RELEASE), AIX4.1)
+EXTRA_LIBS = -L$(DIST)/lib -lnspr$(MOD_VERSION)_shr
+else
+EXTRA_LIBS = -L$(DIST)/lib -lnspr$(MOD_VERSION)
+endif
+endif
+
+# On NCR and SCOOS, we can't link with extra libraries when
+# we build a shared library. If we do so, the linker doesn't
+# complain, but we would run into weird problems at run-time.
+# Therefore on these platforms, we link just the .o files.
+ifeq ($(OS_ARCH),NCR)
+EXTRA_LIBS =
+endif
+ifeq ($(OS_ARCH),SCOOS)
+EXTRA_LIBS =
+endif
+
+LIBRARY_NAME = plds
+LIBRARY_VERSION = $(MOD_VERSION)
+
+RELEASE_HEADERS = $(HEADERS)
+RELEASE_HEADERS_DEST = $(RELEASE_INCLUDE_DIR)
+RELEASE_LIBS = $(TARGETS)
+
+include $(MOD_DEPTH)/config/rules.mk
+
+#
+# The Client build wants the shared libraries in $(DIST)/bin,
+# so we also install them there.
+#
+
+export:: $(TARGETS)
+ $(INSTALL) -m 444 $(HEADERS) $(DIST)/include
+ $(INSTALL) -m 444 $(TARGETS) $(DIST)/lib
+ifdef SHARED_LIBRARY
+ $(INSTALL) -m 444 $(SHARED_LIBRARY) $(DIST)/bin
+endif
+ifeq ($(MOZ_BITS),16)
+ $(INSTALL) -m 444 $(HEADERS) $(MOZ_INCL)
+ $(INSTALL) -m 444 $(TARGETS) $(MOZ_DIST)/lib
+ $(INSTALL) -m 444 $(TARGETS) $(MOZ_DIST)/bin
+endif
+
+install:: export
+
diff --git a/lib/ds/plarena.c b/lib/ds/plarena.c
new file mode 100644
index 00000000..da6c1d01
--- /dev/null
+++ b/lib/ds/plarena.c
@@ -0,0 +1,353 @@
+/* -*- 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.
+ */
+
+/*
+ * Lifetime-based fast allocation, inspired by much prior art, including
+ * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes"
+ * David R. Hanson, Software -- Practice and Experience, Vol. 20(1).
+ */
+#include <stdlib.h>
+#include <string.h>
+#include "plarena.h"
+#include "prmem.h"
+#include "prbit.h"
+#include "prlog.h"
+#include "prmon.h"
+#include "prinit.h"
+
+static PLArena *arena_freelist;
+
+#ifdef PL_ARENAMETER
+static PLArenaStats *arena_stats_list;
+
+#define COUNT(pool,what) (pool)->stats.what++
+#else
+#define COUNT(pool,what) /* nothing */
+#endif
+
+#define PL_ARENA_DEFAULT_ALIGN sizeof(double)
+
+static PRMonitor *arenaLock;
+static PRCallOnceType once;
+
+/*
+** InitializeArenas() -- Initialize arena operations.
+**
+** InitializeArenas() is called exactly once and only once from
+** LockArena(). This function creates the arena protection
+** monitor: arenaLock.
+**
+** Note: If the arenaLock cannot be created, InitializeArenas()
+** fails quietly, returning only PR_FAILURE. This percolates up
+** to the application using the Arena API. He gets no arena
+** from PL_ArenaAllocate(). It's up to him to fail gracefully
+** or recover.
+**
+*/
+static PRStatus InitializeArenas( void )
+{
+ PR_ASSERT( arenaLock == NULL );
+ arenaLock = PR_NewMonitor();
+ if ( arenaLock == NULL )
+ return PR_FAILURE;
+ else
+ return PR_SUCCESS;
+} /* end ArenaInitialize() */
+
+static PRStatus LockArena( void )
+{
+ PRStatus rc = PR_CallOnce( &once, InitializeArenas );
+
+ if ( PR_FAILURE != rc )
+ PR_EnterMonitor( arenaLock );
+ return(rc);
+} /* end LockArena() */
+
+static void UnlockArena( void )
+{
+ PR_ExitMonitor( arenaLock );
+ return;
+} /* end UnlockArena() */
+
+PR_IMPLEMENT(void) PL_InitArenaPool(
+ PLArenaPool *pool, const char *name, PRUint32 size, PRUint32 align)
+{
+#if defined(XP_MAC)
+#pragma unused (name)
+#endif
+
+ if (align == 0)
+ align = PL_ARENA_DEFAULT_ALIGN;
+ pool->mask = PR_BITMASK(PR_CeilingLog2(align));
+ pool->first.next = NULL;
+ pool->first.base = pool->first.avail = pool->first.limit =
+ (PRUword)PL_ARENA_ALIGN(pool, &pool->first + 1);
+ pool->current = &pool->first;
+ pool->arenasize = size;
+#ifdef PL_ARENAMETER
+ memset(&pool->stats, 0, sizeof pool->stats);
+ pool->stats.name = strdup(name);
+ pool->stats.next = arena_stats_list;
+ arena_stats_list = &pool->stats;
+#endif
+}
+
+PR_IMPLEMENT(void *) PL_ArenaAllocate(PLArenaPool *pool, PRUint32 nb)
+{
+ PLArena **ap, *a, *b;
+ PRUint32 sz;
+ void *p;
+
+ PR_ASSERT((nb & pool->mask) == 0);
+#if defined(WIN16)
+ if (nb >= 60000U)
+ return 0;
+#endif /* WIN16 */
+ if ( PR_FAILURE == LockArena())
+ return(0);
+ ap = &arena_freelist;
+ for (a = pool->current; a->avail + nb > a->limit; pool->current = a) {
+ if (a->next) { /* move to next arena */
+ a = a->next;
+ continue;
+ }
+ while ((b = *ap) != 0) { /* reclaim a free arena */
+ if (b->limit - b->base == pool->arenasize) {
+ *ap = b->next;
+ b->next = 0;
+ a = a->next = b;
+ COUNT(pool, nreclaims);
+ goto claim;
+ }
+ ap = &b->next;
+ }
+ sz = PR_MAX(pool->arenasize, nb); /* allocate a new arena */
+ sz += sizeof *a + pool->mask; /* header and alignment slop */
+ b = (PLArena*)PR_MALLOC(sz);
+ if (!b)
+ {
+ UnlockArena();
+ return 0;
+ }
+ a = a->next = b;
+ a->next = 0;
+ a->limit = (PRUword)a + sz;
+ PL_COUNT_ARENA(pool,++);
+ COUNT(pool, nmallocs);
+ claim:
+ a->base = a->avail = (PRUword)PL_ARENA_ALIGN(pool, a + 1);
+ }
+ UnlockArena();
+ p = (void *)a->avail;
+ a->avail += nb;
+ return p;
+}
+
+PR_IMPLEMENT(void *) PL_ArenaGrow(
+ PLArenaPool *pool, void *p, PRUint32 size, PRUint32 incr)
+{
+ void *newp;
+
+ PL_ARENA_ALLOCATE(newp, pool, size + incr);
+ if (newp)
+ memcpy(newp, p, size);
+ return newp;
+}
+
+/*
+ * Free tail arenas linked after head, which may not be the true list head.
+ * Reset pool->current to point to head in case it pointed at a tail arena.
+ */
+static void FreeArenaList(PLArenaPool *pool, PLArena *head, PRBool reallyFree)
+{
+ PLArena **ap, *a;
+
+ ap = &head->next;
+ a = *ap;
+ if (!a)
+ return;
+
+#ifdef DEBUG
+ do {
+ PR_ASSERT(a->base <= a->avail && a->avail <= a->limit);
+ a->avail = a->base;
+ PL_CLEAR_UNUSED(a);
+ } while ((a = a->next) != 0);
+ a = *ap;
+#endif
+
+ if (reallyFree) {
+ do {
+ *ap = a->next;
+ PL_CLEAR_ARENA(a);
+ PL_COUNT_ARENA(pool,--);
+ PR_DELETE(a);
+ } while ((a = *ap) != 0);
+ } else {
+ /* Insert the whole arena chain at the front of the freelist. */
+ do {
+ ap = &(*ap)->next;
+ } while (*ap);
+ LockArena();
+ *ap = arena_freelist;
+ arena_freelist = a;
+ head->next = 0;
+ UnlockArena();
+ }
+
+ pool->current = head;
+}
+
+PR_IMPLEMENT(void) PL_ArenaRelease(PLArenaPool *pool, char *mark)
+{
+ PLArena *a;
+
+ for (a = pool->first.next; a; a = a->next) {
+ if (PR_UPTRDIFF(mark, a) < PR_UPTRDIFF(a->avail, a)) {
+ a->avail = (PRUword)PL_ARENA_ALIGN(pool, mark);
+ FreeArenaList(pool, a, PR_TRUE);
+ return;
+ }
+ }
+}
+
+PR_IMPLEMENT(void) PL_FreeArenaPool(PLArenaPool *pool)
+{
+ FreeArenaList(pool, &pool->first, PR_FALSE);
+ COUNT(pool, ndeallocs);
+}
+
+PR_IMPLEMENT(void) PL_FinishArenaPool(PLArenaPool *pool)
+{
+ FreeArenaList(pool, &pool->first, PR_TRUE);
+#ifdef PL_ARENAMETER
+ {
+ PLArenaStats *stats, **statsp;
+
+ if (pool->stats.name)
+ PR_DELETE(pool->stats.name);
+ for (statsp = &arena_stats_list; (stats = *statsp) != 0;
+ statsp = &stats->next) {
+ if (stats == &pool->stats) {
+ *statsp = stats->next;
+ return;
+ }
+ }
+ }
+#endif
+}
+
+PR_IMPLEMENT(void) PL_CompactArenaPool(PLArenaPool *ap)
+{
+#if XP_MAC
+#pragma unused (ap)
+#if 0
+ PRArena *curr = &(ap->first);
+ while (curr) {
+ reallocSmaller(curr, curr->avail - (uprword_t)curr);
+ curr->limit = curr->avail;
+ curr = curr->next;
+ }
+#endif
+#endif
+}
+
+PR_IMPLEMENT(void) PL_ArenaFinish()
+{
+ PLArena *a, *next;
+
+ LockArena();
+ for (a = arena_freelist; a; a = next) {
+ next = a->next;
+ PR_DELETE(a);
+ }
+ arena_freelist = NULL;
+ UnlockArena();
+}
+
+#ifdef PL_ARENAMETER
+PR_IMPLEMENT(void) PL_ArenaCountAllocation(PLArenaPool *pool, PRUint32 nb)
+{
+ pool->stats.nallocs++;
+ pool->stats.nbytes += nb;
+ if (nb > pool->stats.maxalloc)
+ pool->stats.maxalloc = nb;
+ pool->stats.variance += nb * nb;
+}
+
+PR_IMPLEMENT(void) PL_ArenaCountInplaceGrowth(
+ PLArenaPool *pool, PRUint32 size, PRUint32 incr)
+{
+ pool->stats.ninplace++;
+}
+
+PR_IMPLEMENT(void) PL_ArenaCountGrowth(
+ PLArenaPool *pool, PRUint32 size, PRUint32 incr)
+{
+ pool->stats.ngrows++;
+ pool->stats.nbytes += incr;
+ pool->stats.variance -= size * size;
+ size += incr;
+ if (size > pool->stats.maxalloc)
+ pool->stats.maxalloc = size;
+ pool->stats.variance += size * size;
+}
+
+PR_IMPLEMENT(void) PL_ArenaCountRelease(PLArenaPool *pool, char *mark)
+{
+ pool->stats.nreleases++;
+}
+
+PR_IMPLEMENT(void) PL_ArenaCountRetract(PLArenaPool *pool, char *mark)
+{
+ pool->stats.nfastrels++;
+}
+
+#include <math.h>
+#include <stdio.h>
+
+PR_IMPLEMENT(void) PL_DumpArenaStats(FILE *fp)
+{
+ PLArenaStats *stats;
+ double mean, variance;
+
+ for (stats = arena_stats_list; stats; stats = stats->next) {
+ if (stats->nallocs != 0) {
+ mean = (double)stats->nbytes / stats->nallocs;
+ variance = fabs(stats->variance / stats->nallocs - mean * mean);
+ } else {
+ mean = variance = 0;
+ }
+
+ fprintf(fp, "\n%s allocation statistics:\n", stats->name);
+ fprintf(fp, " number of arenas: %u\n", stats->narenas);
+ fprintf(fp, " number of allocations: %u\n", stats->nallocs);
+ fprintf(fp, " number of free arena reclaims: %u\n", stats->nreclaims);
+ fprintf(fp, " number of malloc calls: %u\n", stats->nmallocs);
+ fprintf(fp, " number of deallocations: %u\n", stats->ndeallocs);
+ fprintf(fp, " number of allocation growths: %u\n", stats->ngrows);
+ fprintf(fp, " number of in-place growths: %u\n", stats->ninplace);
+ fprintf(fp, "number of released allocations: %u\n", stats->nreleases);
+ fprintf(fp, " number of fast releases: %u\n", stats->nfastrels);
+ fprintf(fp, " total bytes allocated: %u\n", stats->nbytes);
+ fprintf(fp, " mean allocation size: %g\n", mean);
+ fprintf(fp, " standard deviation: %g\n", sqrt(variance));
+ fprintf(fp, " maximum allocation size: %u\n", stats->maxalloc);
+ }
+}
+#endif /* PL_ARENAMETER */
diff --git a/lib/ds/plarena.h b/lib/ds/plarena.h
new file mode 100644
index 00000000..cb998bb3
--- /dev/null
+++ b/lib/ds/plarena.h
@@ -0,0 +1,193 @@
+/* -*- 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 plarena_h___
+#define plarena_h___
+/*
+ * Lifetime-based fast allocation, inspired by much prior art, including
+ * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes"
+ * David R. Hanson, Software -- Practice and Experience, Vol. 20(1).
+ *
+ * Also supports LIFO allocation (PL_ARENA_MARK/PL_ARENA_RELEASE).
+ */
+#include "prtypes.h"
+#include "plarenas.h"
+
+PR_BEGIN_EXTERN_C
+
+typedef struct PLArena PLArena;
+
+struct PLArena {
+ PLArena *next; /* next arena for this lifetime */
+ PRUword base; /* aligned base address, follows this header */
+ PRUword limit; /* one beyond last byte in arena */
+ PRUword avail; /* points to next available byte */
+};
+
+#ifdef PL_ARENAMETER
+typedef struct PLArenaStats PLArenaStats;
+
+struct PLArenaStats {
+ PLArenaStats *next; /* next in arenaStats list */
+ char *name; /* name for debugging */
+ PRUint32 narenas; /* number of arenas in pool */
+ PRUint32 nallocs; /* number of PL_ARENA_ALLOCATE() calls */
+ PRUint32 nreclaims; /* number of reclaims from freeArenas */
+ PRUint32 nmallocs; /* number of malloc() calls */
+ PRUint32 ndeallocs; /* number of lifetime deallocations */
+ PRUint32 ngrows; /* number of PL_ARENA_GROW() calls */
+ PRUint32 ninplace; /* number of in-place growths */
+ PRUint32 nreleases; /* number of PL_ARENA_RELEASE() calls */
+ PRUint32 nfastrels; /* number of "fast path" releases */
+ PRUint32 nbytes; /* total bytes allocated */
+ PRUint32 maxalloc; /* maximum allocation size in bytes */
+ PRFloat64 variance; /* size variance accumulator */
+};
+#endif
+
+struct PLArenaPool {
+ PLArena first; /* first arena in pool list */
+ PLArena *current; /* arena from which to allocate space */
+ PRUint32 arenasize; /* net exact size of a new arena */
+ PRUword mask; /* alignment mask (power-of-2 - 1) */
+#ifdef PL_ARENAMETER
+ PLArenaStats stats;
+#endif
+};
+
+/*
+ * If the including .c file uses only one power-of-2 alignment, it may define
+ * PL_ARENA_CONST_ALIGN_MASK to the alignment mask and save a few instructions
+ * per ALLOCATE and GROW.
+ */
+#ifdef PL_ARENA_CONST_ALIGN_MASK
+#define PL_ARENA_ALIGN(pool, n) (((PRUword)(n) + PL_ARENA_CONST_ALIGN_MASK) \
+ & ~PL_ARENA_CONST_ALIGN_MASK)
+
+#define PL_INIT_ARENA_POOL(pool, name, size) \
+ PL_InitArenaPool(pool, name, size, PL_ARENA_CONST_ALIGN_MASK + 1)
+#else
+#define PL_ARENA_ALIGN(pool, n) (((PRUword)(n) + (pool)->mask) & ~(pool)->mask)
+#endif
+
+#define PL_ARENA_ALLOCATE(p, pool, nb) \
+ PR_BEGIN_MACRO \
+ PLArena *_a = (pool)->current; \
+ PRUint32 _nb = PL_ARENA_ALIGN(pool, nb); \
+ PRUword _p = _a->avail; \
+ PRUword _q = _p + _nb; \
+ if (_q > _a->limit) \
+ _p = (PRUword)PL_ArenaAllocate(pool, _nb); \
+ else \
+ _a->avail = _q; \
+ p = (void *)_p; \
+ PL_ArenaCountAllocation(pool, nb); \
+ PR_END_MACRO
+
+#define PL_ARENA_GROW(p, pool, size, incr) \
+ PR_BEGIN_MACRO \
+ PLArena *_a = (pool)->current; \
+ PRUint32 _incr = PL_ARENA_ALIGN(pool, incr); \
+ PRUword _p = _a->avail; \
+ PRUword _q = _p + _incr; \
+ if (_p == (PRUword)(p) + PL_ARENA_ALIGN(pool, size) && \
+ _q <= _a->limit) { \
+ _a->avail = _q; \
+ PL_ArenaCountInplaceGrowth(pool, size, incr); \
+ } else { \
+ p = PL_ArenaGrow(pool, p, size, incr); \
+ } \
+ PL_ArenaCountGrowth(pool, size, incr); \
+ PR_END_MACRO
+
+#define PL_ARENA_MARK(pool) ((void *) (pool)->current->avail)
+#define PR_UPTRDIFF(p,q) ((PRUword)(p) - (PRUword)(q))
+
+#ifdef DEBUG
+#define PL_FREE_PATTERN 0xDA
+#define PL_CLEAR_UNUSED(a) (PR_ASSERT((a)->avail <= (a)->limit), \
+ memset((void*)(a)->avail, PL_FREE_PATTERN, \
+ (a)->limit - (a)->avail))
+#define PL_CLEAR_ARENA(a) memset((void*)(a), PL_FREE_PATTERN, \
+ (a)->limit - (PRUword)(a))
+#else
+#define PL_CLEAR_UNUSED(a)
+#define PL_CLEAR_ARENA(a)
+#endif
+
+#define PL_ARENA_RELEASE(pool, mark) \
+ PR_BEGIN_MACRO \
+ char *_m = (char *)(mark); \
+ PLArena *_a = (pool)->current; \
+ if (PR_UPTRDIFF(_m, _a) <= PR_UPTRDIFF(_a->avail, _a)) { \
+ _a->avail = (PRUword)PL_ARENA_ALIGN(pool, _m); \
+ PL_CLEAR_UNUSED(_a); \
+ PL_ArenaCountRetract(pool, _m); \
+ } else { \
+ PL_ArenaRelease(pool, _m); \
+ } \
+ PL_ArenaCountRelease(pool, _m); \
+ PR_END_MACRO
+
+#ifdef PL_ARENAMETER
+#define PL_COUNT_ARENA(pool,op) ((pool)->stats.narenas op)
+#else
+#define PL_COUNT_ARENA(pool,op)
+#endif
+
+#define PL_ARENA_DESTROY(pool, a, pnext) \
+ PR_BEGIN_MACRO \
+ PL_COUNT_ARENA(pool,--); \
+ if ((pool)->current == (a)) (pool)->current = &(pool)->first; \
+ *(pnext) = (a)->next; \
+ PL_CLEAR_ARENA(a); \
+ free(a); \
+ (a) = 0; \
+ PR_END_MACRO
+
+#ifdef PL_ARENAMETER
+
+#include <stdio.h>
+
+PR_EXTERN(void) PL_ArenaCountAllocation(PLArenaPool *pool, PRUint32 nb);
+
+PR_EXTERN(void) PL_ArenaCountInplaceGrowth(
+ PLArenaPool *pool, PRUint32 size, PRUint32 incr);
+
+PR_EXTERN(void) PL_ArenaCountGrowth(
+ PLArenaPool *pool, PRUint32 size, PRUint32 incr);
+
+PR_EXTERN(void) PL_ArenaCountRelease(PLArenaPool *pool, char *mark);
+
+PR_EXTERN(void) PL_ArenaCountRetract(PLArenaPool *pool, char *mark);
+
+PR_EXTERN(void) PL_DumpArenaStats(FILE *fp);
+
+#else /* !PL_ARENAMETER */
+
+#define PL_ArenaCountAllocation(ap, nb) /* nothing */
+#define PL_ArenaCountInplaceGrowth(ap, size, incr) /* nothing */
+#define PL_ArenaCountGrowth(ap, size, incr) /* nothing */
+#define PL_ArenaCountRelease(ap, mark) /* nothing */
+#define PL_ArenaCountRetract(ap, mark) /* nothing */
+
+#endif /* !PL_ARENAMETER */
+
+PR_END_EXTERN_C
+
+#endif /* plarena_h___ */
diff --git a/lib/ds/plarenas.h b/lib/ds/plarenas.h
new file mode 100644
index 00000000..5e0d68e5
--- /dev/null
+++ b/lib/ds/plarenas.h
@@ -0,0 +1,95 @@
+/* -*- 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(PLARENAS_H)
+#else /* defined(PLARENAS_H) */
+
+PR_BEGIN_EXTERN_C
+
+typedef struct PLArenaPool PLArenaPool;
+
+/*
+** Allocate an arena pool as specified by the parameters.
+**
+** This is equivelant to allocating the space yourself and then
+** calling PL_InitArenaPool().
+**
+** This function may fail (and return a NULL) for a variety of
+** reasons. The reason for a particular failure can be discovered
+** by calling PR_GetError().
+*/
+#if 0 /* Not implemented */
+PR_EXTERN(PLArenaPool*) PL_AllocArenaPool(
+ const char *name, PRUint32 size, PRUint32 align);
+#endif
+
+/*
+** Destroy an arena pool previously allocated by PL_AllocArenaPool().
+**
+** This function may fail if the arena is not empty and the caller
+** wishes to check for empty upon descruction.
+*/
+#if 0 /* Not implemented */
+PR_EXTERN(PRStatus) PL_DestroyArenaPool(PLArenaPool *pool, PRBool checkEmpty);
+#endif
+
+
+/*
+** Initialize an arena pool with the given name for debugging and metering,
+** with a minimum size per arena of size bytes.
+**/
+PR_EXTERN(void) PL_InitArenaPool(
+ PLArenaPool *pool, const char *name, PRUint32 size, PRUint32 align);
+
+/*
+** Finish using arenas, freeing all memory associated with them.
+**/
+PR_EXTERN(void) PL_ArenaFinish(void);
+
+/*
+** Free the arenas in pool. The user may continue to allocate from pool
+** after calling this function. There is no need to call PL_InitArenaPool()
+** again unless PL_FinishArenaPool(pool) has been called.
+**/
+PR_EXTERN(void) PL_FreeArenaPool(PLArenaPool *pool);
+
+/*
+** Free the arenas in pool and finish using it altogether.
+**/
+PR_EXTERN(void) PL_FinishArenaPool(PLArenaPool *pool);
+
+/*
+** Compact all of the arenas in a pool so that no space is wasted.
+**/
+PR_EXTERN(void) PL_CompactArenaPool(PLArenaPool *pool);
+
+/*
+** Friend functions used by the PL_ARENA_*() macros.
+**/
+PR_EXTERN(void *) PL_ArenaAllocate(PLArenaPool *pool, PRUint32 nb);
+
+PR_EXTERN(void *) PL_ArenaGrow(
+ PLArenaPool *pool, void *p, PRUint32 size, PRUint32 incr);
+
+PR_EXTERN(void) PL_ArenaRelease(PLArenaPool *pool, char *mark);
+
+PR_END_EXTERN_C
+
+#endif /* defined(PLARENAS_H) */
+
+/* plarenas */
diff --git a/lib/ds/plevent.c b/lib/ds/plevent.c
new file mode 100644
index 00000000..32271025
--- /dev/null
+++ b/lib/ds/plevent.c
@@ -0,0 +1,731 @@
+/* -*- 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 "plevent.h"
+#include "prmem.h"
+#include "prcmon.h"
+#include "prlog.h"
+#if !defined(WIN32)
+#include <errno.h>
+#include <stddef.h>
+#if !defined(OS2)
+#include <unistd.h>
+#endif
+#endif
+
+#if defined(XP_MAC)
+#include <AppleEvents.h>
+#include "pprthred.h"
+#include "primpl.h"
+#else
+#include "private/pprthred.h"
+#include "private/primpl.h"
+#endif
+
+static PRLogModuleInfo *event_lm = NULL;
+
+/*******************************************************************************
+ * Private Stuff
+ ******************************************************************************/
+
+struct PLEventQueue {
+ char* name;
+ PRCList queue;
+ PRMonitor* monitor;
+ PRThread* handlerThread;
+#ifdef XP_UNIX
+ PRInt32 eventPipe[2];
+ PRPackedBool nativeNotifier;
+ int notifyCount;
+#endif
+};
+
+#define PR_EVENT_PTR(_qp) \
+ ((PLEvent*) ((char*) (_qp) - offsetof(PLEvent, link)))
+
+static PRStatus _pl_SetupNativeNotifier(PLEventQueue* self);
+static void _pl_CleanupNativeNotifier(PLEventQueue* self);
+static PRStatus _pl_NativeNotify(PLEventQueue* self);
+static PRStatus _pl_AcknowledgeNativeNotify(PLEventQueue* self);
+
+
+#if defined(_WIN32) || defined(WIN16)
+PLEventQueue * _pr_main_event_queue;
+UINT _pr_PostEventMsgId;
+HWND _pr_eventReceiverWindow;
+static char *_pr_eventWindowClass = "NSPR:EventWindow";
+#endif
+
+/*******************************************************************************
+ * Event Queue Operations
+ ******************************************************************************/
+
+PR_IMPLEMENT(PLEventQueue*)
+PL_CreateEventQueue(char* name, PRThread* handlerThread)
+{
+ PRStatus err;
+ PLEventQueue* self = NULL;
+ PRMonitor* mon = NULL;
+
+ if (event_lm == NULL)
+ event_lm = PR_LOG_DEFINE("event");
+
+ self = PR_NEWZAP(PLEventQueue);
+ if (self == NULL) return NULL;
+
+ mon = PR_NewNamedMonitor(name);
+ if (mon == NULL) goto error;
+
+ self->name = name;
+ self->monitor = mon;
+ self->handlerThread = handlerThread;
+ PR_INIT_CLIST(&self->queue);
+ err = _pl_SetupNativeNotifier(self);
+ if (err) goto error;
+
+ return self;
+
+ error:
+ if (mon != NULL)
+ PR_DestroyMonitor(mon);
+ PR_DELETE(self);
+ return NULL;
+}
+
+PR_IMPLEMENT(PRMonitor*)
+PL_GetEventQueueMonitor(PLEventQueue* self)
+{
+ return self->monitor;
+}
+
+static void PR_CALLBACK
+_pl_destroyEvent(PLEvent* event, void* data, PLEventQueue* queue)
+{
+#ifdef XP_MAC
+#pragma unused (data, queue)
+#endif
+ PL_DequeueEvent(event, queue);
+ PL_DestroyEvent(event);
+}
+
+PR_IMPLEMENT(void)
+PL_DestroyEventQueue(PLEventQueue* self)
+{
+ PR_EnterMonitor(self->monitor);
+
+ /* destroy undelivered events */
+ PL_MapEvents(self, _pl_destroyEvent, NULL);
+
+ _pl_CleanupNativeNotifier(self);
+
+ /* destroying the monitor also destroys the name */
+ PR_ExitMonitor(self->monitor);
+ PR_DestroyMonitor(self->monitor);
+ PR_DELETE(self);
+
+}
+
+PR_IMPLEMENT(PRStatus)
+PL_PostEvent(PLEventQueue* self, PLEvent* event)
+{
+ PRStatus err;
+ PRMonitor* mon;
+
+ if (self == NULL)
+ return PR_FAILURE;
+
+ mon = self->monitor;
+ PR_EnterMonitor(mon);
+
+ /* insert event into thread's event queue: */
+ if (event != NULL) {
+ PR_APPEND_LINK(&event->link, &self->queue);
+ }
+
+ /* notify even if event is NULL */
+ err = _pl_NativeNotify(self);
+ if (err != PR_SUCCESS) goto done;
+
+ /*
+ * This may fall on deaf ears if we're really notifying the native
+ * thread, and no one has called PL_WaitForEvent (or PL_EventLoop):
+ */
+ err = PR_Notify(mon);
+
+ done:
+ PR_ExitMonitor(mon);
+ return err;
+}
+
+PR_IMPLEMENT(void*)
+PL_PostSynchronousEvent(PLEventQueue* self, PLEvent* event)
+{
+ void* result;
+
+ if (self == NULL)
+ return NULL;
+
+ PR_ASSERT(event != NULL);
+ PR_CEnterMonitor(event);
+
+ if (PR_CurrentThread() == self->handlerThread) {
+ /* Handle the case where the thread requesting the event handling
+ is also the thread that's supposed to do the handling. */
+ result = event->handler(event);
+ }
+ else {
+ int inEventQueueMon = PR_GetMonitorEntryCount(self->monitor);
+ int i, entryCount = self->monitor->entryCount;
+
+ event->synchronousResult = (void*)PR_TRUE;
+ PL_PostEvent(self, event);
+ /* We need to temporarily give up our event queue monitor if
+ we're holding it, otherwise, the thread we're going to wait
+ for notification from won't be able to enter it to process
+ the event. */
+ if (inEventQueueMon) {
+ for (i = 0; i < entryCount; i++)
+ PR_ExitMonitor(self->monitor);
+ }
+ PR_CWait(event, PR_INTERVAL_NO_TIMEOUT); /* wait for event to be handled or destroyed */
+ if (inEventQueueMon) {
+ for (i = 0; i < entryCount; i++)
+ PR_EnterMonitor(self->monitor);
+ }
+ result = event->synchronousResult;
+ event->synchronousResult = NULL;
+ }
+
+ PR_CExitMonitor(event);
+
+ /* For synchronous events, they're destroyed here on the caller's
+ thread before the result is returned. See PL_HandleEvent. */
+ PL_DestroyEvent(event);
+
+ return result;
+}
+
+PR_IMPLEMENT(PLEvent*)
+PL_GetEvent(PLEventQueue* self)
+{
+ PLEvent* event = NULL;
+ PRStatus err;
+ PRMonitor* mon;
+
+ if (self == NULL)
+ return NULL;
+
+ mon = self->monitor;
+ PR_EnterMonitor(mon);
+
+ err = _pl_AcknowledgeNativeNotify(self);
+ if (err) goto done;
+
+ if (!PR_CLIST_IS_EMPTY(&self->queue)) {
+ /* then grab the event and return it: */
+ event = PR_EVENT_PTR(self->queue.next);
+ PR_REMOVE_AND_INIT_LINK(&event->link);
+ }
+
+ done:
+ PR_ExitMonitor(mon);
+ return event;
+}
+
+PR_IMPLEMENT(PRBool)
+PL_EventAvailable(PLEventQueue* self)
+{
+ PRBool result = PR_FALSE;
+
+ if (self == NULL)
+ return PR_FALSE;
+
+ PR_EnterMonitor(self->monitor);
+
+ if (!PR_CLIST_IS_EMPTY(&self->queue))
+ result = PR_TRUE;
+
+ PR_ExitMonitor(self->monitor);
+ return result;
+}
+
+PR_IMPLEMENT(void)
+PL_MapEvents(PLEventQueue* self, PLEventFunProc fun, void* data)
+{
+ PRCList* qp;
+
+ if (self == NULL)
+ return;
+
+ PR_EnterMonitor(self->monitor);
+ qp = self->queue.next;
+ while (qp != &self->queue) {
+ PLEvent* event = PR_EVENT_PTR(qp);
+ qp = qp->next;
+ (*fun)(event, data, self);
+ }
+ PR_ExitMonitor(self->monitor);
+}
+
+static void PR_CALLBACK
+_pl_DestroyEventForOwner(PLEvent* event, void* owner, PLEventQueue* queue)
+{
+ PR_ASSERT(PR_GetMonitorEntryCount(queue->monitor) > 0);
+ if (event->owner == owner) {
+ PR_LOG(event_lm, PR_LOG_DEBUG,
+ ("$$$ \tdestroying event %0x for owner %0x", event, owner));
+ PL_DequeueEvent(event, queue);
+ if (event->synchronousResult == (void*)PR_TRUE) {
+ PR_CEnterMonitor(event);
+ event->synchronousResult = NULL;
+ PR_CNotify(event);
+ PR_CExitMonitor(event);
+ }
+ else {
+ PL_DestroyEvent(event);
+ }
+ }
+ else {
+ PR_LOG(event_lm, PR_LOG_DEBUG,
+ ("$$$ \tskipping event %0x for owner %0x", event, owner));
+ }
+}
+
+PR_IMPLEMENT(void)
+PL_RevokeEvents(PLEventQueue* self, void* owner)
+{
+ if (self == NULL)
+ return;
+
+ PR_LOG_BEGIN(event_lm, PR_LOG_DEBUG,
+ ("$$$ revoking events for owner %0x", owner));
+
+ /*
+ ** First we enter the monitor so that no one else can post any events
+ ** to the queue:
+ */
+ PR_EnterMonitor(self->monitor);
+ PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ owner %0x, entered monitor", owner));
+
+ /*
+ ** Discard any pending events for this owner:
+ */
+ PL_MapEvents(self, _pl_DestroyEventForOwner, owner);
+
+#ifdef DEBUG
+ {
+ PRCList* qp = self->queue.next;
+ while (qp != &self->queue) {
+ PLEvent* event = PR_EVENT_PTR(qp);
+ qp = qp->next;
+ PR_ASSERT(event->owner != owner);
+ }
+ }
+#endif /* DEBUG */
+
+ PR_ExitMonitor(self->monitor);
+
+ PR_LOG_END(event_lm, PR_LOG_DEBUG,
+ ("$$$ revoking events for owner %0x", owner));
+}
+
+PR_IMPLEMENT(void)
+PL_ProcessPendingEvents(PLEventQueue* self)
+{
+ if (self == NULL)
+ return;
+
+ while (PR_TRUE) {
+ PLEvent* event = PL_GetEvent(self);
+ if (event == NULL) return;
+
+ PR_LOG_BEGIN(event_lm, PR_LOG_DEBUG, ("$$$ processing event"));
+ PL_HandleEvent(event);
+ PR_LOG_END(event_lm, PR_LOG_DEBUG, ("$$$ done processing event"));
+ }
+}
+
+/*******************************************************************************
+ * Event Operations
+ ******************************************************************************/
+
+PR_IMPLEMENT(void)
+PL_InitEvent(PLEvent* self, void* owner,
+ PLHandleEventProc handler,
+ PLDestroyEventProc destructor)
+{
+ PR_INIT_CLIST(&self->link);
+ self->handler = handler;
+ self->destructor = destructor;
+ self->owner = owner;
+ self->synchronousResult = NULL;
+}
+
+PR_IMPLEMENT(void*)
+PL_GetEventOwner(PLEvent* self)
+{
+ return self->owner;
+}
+
+PR_IMPLEMENT(void)
+PL_HandleEvent(PLEvent* self)
+{
+ void* result;
+
+ if (self == NULL)
+ return;
+
+ /* This event better not be on an event queue anymore. */
+ PR_ASSERT(PR_CLIST_IS_EMPTY(&self->link));
+
+ result = (*self->handler)(self);
+ if (NULL != self->synchronousResult) {
+ PR_CEnterMonitor(self);
+ self->synchronousResult = result;
+ PR_CNotify(self); /* wake up the guy waiting for the result */
+ PR_CExitMonitor(self);
+ }
+ else {
+ /* For asynchronous events, they're destroyed by the event-handler
+ thread. See PR_PostSynchronousEvent. */
+ PL_DestroyEvent(self);
+ }
+}
+
+PR_IMPLEMENT(void)
+PL_DestroyEvent(PLEvent* self)
+{
+ if (self == NULL)
+ return;
+
+ /* This event better not be on an event queue anymore. */
+ PR_ASSERT(PR_CLIST_IS_EMPTY(&self->link));
+
+ (*self->destructor)(self);
+}
+
+PR_IMPLEMENT(void)
+PL_DequeueEvent(PLEvent* self, PLEventQueue* queue)
+{
+#ifdef XP_MAC
+#pragma unused (queue)
+#endif
+ if (self == NULL)
+ return;
+
+ /* Only the owner is allowed to dequeue events because once the
+ client has put it in the queue, they have no idea whether it's
+ been processed and destroyed or not. */
+/* PR_ASSERT(queue->handlerThread == PR_CurrentThread());*/
+
+ PR_EnterMonitor(queue->monitor);
+
+ PR_ASSERT(!PR_CLIST_IS_EMPTY(&self->link));
+ PR_REMOVE_AND_INIT_LINK(&self->link);
+
+ PR_ExitMonitor(queue->monitor);
+}
+
+/*******************************************************************************
+ * Pure Event Queues
+ *
+ * For when you're only processing PLEvents and there is no native
+ * select, thread messages, or AppleEvents.
+ ******************************************************************************/
+
+PR_IMPLEMENT(PLEvent*)
+PL_WaitForEvent(PLEventQueue* self)
+{
+ PLEvent* event;
+ PRMonitor* mon;
+
+ if (self == NULL)
+ return NULL;
+
+ mon = self->monitor;
+ PR_EnterMonitor(mon);
+
+ while ((event = PL_GetEvent(self)) == NULL) {
+ PRStatus err;
+ PR_LOG_END(event_lm, PR_LOG_DEBUG, ("$$$ waiting for event"));
+ err = PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
+ if ((err == PR_FAILURE)
+ && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) break;
+ }
+
+ PR_ExitMonitor(mon);
+ return event;
+}
+
+PR_IMPLEMENT(void)
+PL_EventLoop(PLEventQueue* self)
+{
+ if (self == NULL)
+ return;
+
+ while (PR_TRUE) {
+ PLEvent* event = PL_WaitForEvent(self);
+ if (event == NULL) {
+ /* This can only happen if the current thread is interrupted */
+ return;
+ }
+
+ PR_LOG_BEGIN(event_lm, PR_LOG_DEBUG, ("$$$ processing event"));
+ PL_HandleEvent(event);
+ PR_LOG_END(event_lm, PR_LOG_DEBUG, ("$$$ done processing event"));
+ }
+}
+
+/*******************************************************************************
+ * Native Event Queues
+ *
+ * For when you need to call select, or WaitNextEvent, and yet also want
+ * to handle PLEvents.
+ ******************************************************************************/
+
+static PRStatus
+_pl_SetupNativeNotifier(PLEventQueue* self)
+{
+#if defined(XP_MAC)
+#pragma unused (self)
+#endif
+
+ PRInt32 err = 0;
+#if defined(XP_UNIX)
+ err = pipe(self->eventPipe);
+#endif
+ return err == 0 ? PR_SUCCESS : PR_FAILURE;
+}
+
+static void
+_pl_CleanupNativeNotifier(PLEventQueue* self)
+{
+#if defined(XP_MAC)
+#pragma unused (self)
+#endif
+
+#if defined(XP_UNIX)
+ close(self->eventPipe[0]);
+ close(self->eventPipe[1]);
+#endif
+}
+
+static PRStatus
+_pl_NativeNotify(PLEventQueue* self)
+{
+#if defined(XP_UNIX)
+
+# define NOTIFY_TOKEN 0xFA
+ PRInt32 count;
+ unsigned char buf[] = { NOTIFY_TOKEN };
+
+ count = write(self->eventPipe[1], buf, 1);
+ self->notifyCount++;
+ return (count == 1) ? PR_SUCCESS : PR_FAILURE;
+
+#elif defined(XP_PC) && ( defined(WINNT) || defined(WIN95) || defined(WIN16))
+ /*
+ ** Post a message to the NSPR window on the main thread requesting
+ ** it to process the pending events. This is only necessary for the
+ ** main event queue, since the main thread is waiting for OS events.
+ */
+ if (self == _pr_main_event_queue) {
+ PostMessage( _pr_eventReceiverWindow, _pr_PostEventMsgId,
+ (WPARAM)0, (LPARAM)self);
+ }
+ return PR_SUCCESS;
+
+#elif defined(XP_MAC)
+
+#pragma unused (self)
+ return PR_SUCCESS; /* XXX can fail? */
+
+#endif
+}
+
+static PRStatus
+_pl_AcknowledgeNativeNotify(PLEventQueue* self)
+{
+#if defined(XP_UNIX)
+
+ PRInt32 count;
+ unsigned char c;
+
+ if (self->notifyCount <= 0) return PR_SUCCESS;
+ /* consume the byte NativeNotify put in our pipe: */
+ count = read(self->eventPipe[0], &c, 1);
+ self->notifyCount--;
+ return ((count == 1 && c == NOTIFY_TOKEN) || count == 0)
+ ? PR_SUCCESS : PR_FAILURE;
+
+#else
+
+#if defined(XP_MAC)
+#pragma unused (self)
+#endif
+
+ /* nothing to do on the other platforms */
+ return PR_SUCCESS;
+#endif
+}
+
+PR_IMPLEMENT(PRInt32)
+PL_GetEventQueueSelectFD(PLEventQueue* self)
+{
+ if (self == NULL)
+ return -1;
+
+#if defined(XP_UNIX)
+ return self->eventPipe[0];
+#else
+ return -1; /* other platforms don't handle this (yet) */
+#endif
+}
+
+
+
+#if defined(WIN16) || defined(_WIN32)
+/*
+** Global Instance handle...
+** In Win32 this is the module handle of the DLL.
+**
+*/
+HINSTANCE _pr_hInstance;
+#endif
+
+#if defined(WIN16)
+/*
+** Function LibMain() is required by Win16
+**
+*/
+int CALLBACK LibMain( HINSTANCE hInst, WORD wDataSeg,
+ WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ _pr_hInstance = hInst;
+ return TRUE;
+}
+#endif /* WIN16 */
+
+#if defined(_WIN32)
+
+/*
+** Initialization routine for the NSPR DLL...
+*/
+
+BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved)
+{
+ switch (dwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ _pr_hInstance = hDLL;
+ break;
+
+ case DLL_THREAD_ATTACH:
+ break;
+
+ case DLL_THREAD_DETACH:
+ break;
+
+ case DLL_PROCESS_DETACH:
+ _pr_hInstance = NULL;
+ break;
+ }
+
+ return TRUE;
+}
+#endif
+
+
+#if defined(WIN16) || defined(_WIN32)
+PR_IMPLEMENT(PLEventQueue *)
+PL_GetMainEventQueue()
+{
+ PR_ASSERT(_pr_main_event_queue);
+
+ return _pr_main_event_queue;
+}
+
+LRESULT CALLBACK
+#if defined(WIN16)
+__loadds
+#endif
+_md_EventReceiverProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ if (_pr_PostEventMsgId == uMsg )
+ {
+ PREventQueue *queue = (PREventQueue *)lParam;
+
+ PR_ASSERT(queue == PL_GetMainEventQueue());
+ if (queue == PL_GetMainEventQueue())
+ {
+ PR_ProcessPendingEvents(queue);
+ return TRUE;
+ }
+ }
+ return DefWindowProc(hwnd, uMsg, wParam, lParam);
+}
+
+PR_IMPLEMENT(void)
+PL_InitializeEventsLib(char *name)
+{
+ WNDCLASS wc;
+
+ _pr_main_event_queue = PL_CreateEventQueue(name, PR_GetCurrentThread());
+
+ /* Register the windows message for NSPR Event notification */
+ _pr_PostEventMsgId = RegisterWindowMessage("NSPR_PostEvent");
+
+ /* Register the class for the event receiver window */
+ if (!GetClassInfo(_pr_hInstance, _pr_eventWindowClass, &wc)) {
+ wc.style = 0;
+ wc.lpfnWndProc = _md_EventReceiverProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = _pr_hInstance;
+ wc.hIcon = NULL;
+ wc.hCursor = NULL;
+ wc.hbrBackground = (HBRUSH) NULL;
+ wc.lpszMenuName = (LPCSTR) NULL;
+ wc.lpszClassName = _pr_eventWindowClass;
+ RegisterClass(&wc);
+ }
+
+ /* Create the event receiver window */
+ _pr_eventReceiverWindow = CreateWindow(_pr_eventWindowClass,
+ "NSPR:EventReceiver",
+ 0, 0, 0, 10, 10,
+ NULL, NULL, _pr_hInstance,
+ NULL);
+ PR_ASSERT(_pr_eventReceiverWindow);
+}
+#endif
+
+#if defined(_WIN32) || defined(WIN16)
+PR_IMPLEMENT(HWND)
+PR_GetEventReceiverWindow()
+{
+ if(_pr_eventReceiverWindow != NULL)
+ {
+ return _pr_eventReceiverWindow;
+ }
+
+ return NULL;
+
+}
+#endif
+
+/******************************************************************************/
diff --git a/lib/ds/plevent.h b/lib/ds/plevent.h
new file mode 100644
index 00000000..81c7340e
--- /dev/null
+++ b/lib/ds/plevent.h
@@ -0,0 +1,410 @@
+/* -*- 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.
+ */
+
+/**********************************************************************
+NSPL Events
+
+Defining Events
+---------------
+
+Events are essentially structures that represent argument lists for a
+function that will run on another thread. All event structures you
+define must include a PLEvent struct as their first field:
+
+ typedef struct MyEventType {
+ PLEvent e;
+ // arguments follow...
+ int x;
+ char* y;
+ } MyEventType;
+
+It is also essential that you establish a model of ownership for each
+argument passed in an event record, i.e. whether particular arguments
+will be deleted by the event destruction callback, or whether they
+only loaned to the event handler callback, and guaranteed to persist
+until the time at which the handler is called.
+
+Sending Events
+--------------
+
+Events are initialized by PL_InitEvent and can be sent via
+PL_PostEvent or PL_PostSynchronousEvent. Events can also have an
+owner. The owner of an event can revoke all the events in a given
+event-queue by calling PL_RevokeEvents. An owner might want
+to do this if, for instance, it is being destroyed, and handling the
+events after the owner's destruction would cause an error (e.g. an
+MWContext).
+
+Since the act of initializing and posting an event must be coordinated
+with it's possible revocation, it is essential that the event-queue's
+monitor be entered surrounding the code that constructs, initializes
+and posts the event:
+
+ void postMyEvent(MyOwner* owner, int x, char* y)
+ {
+ MyEventType* event;
+
+ PL_ENTER_EVENT_QUEUE_MONITOR(myQueue);
+
+ // construct
+ event = PR_NEW(MyEventType);
+ if (event == NULL) goto done;
+
+ // initialize
+ PL_InitEvent(event, owner,
+ (PLHandleEventProc)handleMyEvent,
+ (PLDestroyEventProc)destroyMyEvent);
+ event->x = x;
+ event->y = strdup(y);
+
+ // post
+ PL_PostEvent(myQueue, &event->e);
+
+ done:
+ PL_EXIT_EVENT_QUEUE_MONITOR(myQueue);
+ }
+
+If you don't call PL_InitEvent and PL_PostEvent within the
+event-queue's monitor, you'll get a big red assert.
+
+Handling Events
+---------------
+
+To handle an event you must write a callback that is passed the event
+record you defined containing the event's arguments:
+
+ void* handleMyEvent(MyEventType* event)
+ {
+ doit(event->x, event->y);
+ return NULL; // you could return a value for a sync event
+ }
+
+Similarly for the destruction callback:
+
+ void destroyMyEvent(MyEventType* event)
+ {
+ free(event->y); // created by strdup
+ free(event);
+ }
+
+Processing Events in Your Event Loop
+------------------------------------
+
+If your main loop only processes events delivered to the event queue,
+things are rather simple. You just get the next event (which may
+block), and then handle it:
+
+ while (1) {
+ event = PL_GetEvent(myQueue);
+ PL_HandleEvent(event);
+ }
+
+However, if other things must be waited on, you'll need to obtain a
+file-descriptor that represents your event queue, and hand it to select:
+
+ fd = PL_GetEventQueueSelectFD(myQueue);
+ ...add fd to select set...
+ while (select(...)) {
+ if (...fd...) {
+ PL_ProcessPendingEvents(myQueue);
+ }
+ ...
+ }
+
+Of course, with Motif and Windows it's more complicated than that, and
+on Mac it's completely different, but you get the picture.
+
+Revoking Events
+---------------
+If at any time an owner of events is about to be destroyed, you must
+take steps to ensure that no one tries to use the event queue after
+the owner is gone (or a crash may result). You can do this by either
+processing all the events in the queue before destroying the owner:
+
+ {
+ ...
+ PL_ENTER_EVENT_QUEUE_MONITOR(myQueue);
+ PL_ProcessPendingEvents(myQueue);
+ DestroyMyOwner(owner);
+ PL_EXIT_EVENT_QUEUE_MONITOR(myQueue);
+ ...
+ }
+
+or by revoking the events that are in the queue for that owner. This
+removes them from the queue and calls their destruction callback:
+
+ {
+ ...
+ PL_ENTER_EVENT_QUEUE_MONITOR(myQueue);
+ PL_RevokeEvents(myQueue, owner);
+ DestroyMyOwner(owner);
+ PL_EXIT_EVENT_QUEUE_MONITOR(myQueue);
+ ...
+ }
+
+In either case it is essential that you be in the event-queue's monitor
+to ensure that all events are removed from the queue for that owner,
+and to ensure that no more events will be delivered for that owner.
+**********************************************************************/
+
+#ifndef prevent_h___
+#define prevent_h___
+
+#include "prtypes.h"
+#include "prclist.h"
+#include "prthread.h"
+#include "prmon.h"
+
+/* For HWND */
+#ifdef _WIN32
+#include <windef.h>
+#elif defined(WIN16)
+#include <windows.h>
+#endif
+
+PR_BEGIN_EXTERN_C
+
+/* Typedefs */
+
+typedef struct PLEvent PLEvent;
+typedef struct PLEventQueue PLEventQueue;
+
+/*******************************************************************************
+ * Event Queue Operations
+ ******************************************************************************/
+
+/*
+** Creates a new event queue. Returns NULL on failure.
+*/
+PR_EXTERN(PLEventQueue*)
+PL_CreateEventQueue(char* name, PRThread* handlerThread);
+
+/*
+** Destroys an event queue.
+*/
+PR_EXTERN(void)
+PL_DestroyEventQueue(PLEventQueue* self);
+
+/*
+** Returns the monitor associated with an event queue. This monitor is
+** selectable. The monitor should be entered to protect against anyone
+** calling PL_RevokeEvents while the event is trying to be constructed
+** and delivered.
+*/
+PR_EXTERN(PRMonitor*)
+PL_GetEventQueueMonitor(PLEventQueue* self);
+
+#define PL_ENTER_EVENT_QUEUE_MONITOR(queue) \
+ PR_EnterMonitor(PL_GetEventQueueMonitor(queue))
+
+#define PL_EXIT_EVENT_QUEUE_MONITOR(queue) \
+ PR_ExitMonitor(PL_GetEventQueueMonitor(queue))
+
+/*
+** Posts an event to an event queue, waking up any threads waiting for an
+** event. If event is NULL, notification still occurs, but no event will
+** be available.
+**
+** Any events delivered by this routine will be destroyed by PL_HandleEvent
+** when it is called (by the event-handling thread).
+*/
+PR_EXTERN(PRStatus)
+PL_PostEvent(PLEventQueue* self, PLEvent* event);
+
+/*
+** Like PL_PostEvent, this routine posts an event to the event handling
+** thread, but does so synchronously, waiting for the result. The result
+** which is the value of the handler routine is returned.
+**
+** Any events delivered by this routine will be not be destroyed by
+** PL_HandleEvent, but instead will be destroyed just before the result is
+** returned (by the current thread).
+*/
+PR_EXTERN(void*)
+PL_PostSynchronousEvent(PLEventQueue* self, PLEvent* event);
+
+/*
+** Gets an event from an event queue. Returns NULL if no event is
+** available.
+*/
+PR_EXTERN(PLEvent*)
+PL_GetEvent(PLEventQueue* self);
+
+/*
+** Returns true if there is an event available for PL_GetEvent.
+*/
+PR_EXTERN(PRBool)
+PL_EventAvailable(PLEventQueue* self);
+
+/*
+** This is the type of the function that must be passed to PL_MapEvents
+** (see description below).
+*/
+typedef void
+(PR_CALLBACK *PLEventFunProc)(PLEvent* event, void* data, PLEventQueue* queue);
+
+/*
+** Applies a function to every event in the event queue. This can be used
+** to selectively handle, filter, or remove events. The data pointer is
+** passed to each invocation of the function fun.
+*/
+PR_EXTERN(void)
+PL_MapEvents(PLEventQueue* self, PLEventFunProc fun, void* data);
+
+/*
+** This routine walks an event queue and destroys any event whose owner is
+** the owner specified. The == operation is used to compare owners.
+*/
+PR_EXTERN(void)
+PL_RevokeEvents(PLEventQueue* self, void* owner);
+
+/*
+** This routine processes all pending events in the event queue. It can be
+** called from the thread's main event-processing loop whenever the event
+** queue's selectFD is ready (returned by PL_GetEventQueueSelectFD).
+*/
+PR_EXTERN(void)
+PL_ProcessPendingEvents(PLEventQueue* self);
+
+/*******************************************************************************
+ * Pure Event Queues
+ *
+ * For when you're only processing PLEvents and there is no native
+ * select, thread messages, or AppleEvents.
+ ******************************************************************************/
+
+/*
+** Blocks until an event can be returned from the event queue. This routine
+** may return NULL if the current thread is interrupted.
+*/
+PR_EXTERN(PLEvent*)
+PL_WaitForEvent(PLEventQueue* self);
+
+/*
+** One stop shopping if all you're going to do is process PLEvents. Just
+** call this and it loops forever processing events as they arrive. It will
+** terminate when your thread is interrupted or dies.
+*/
+PR_EXTERN(void)
+PL_EventLoop(PLEventQueue* self);
+
+/*******************************************************************************
+ * Native Event Queues
+ *
+ * For when you need to call select, or WaitNextEvent, and yet also want
+ * to handle PLEvents.
+ ******************************************************************************/
+
+/*
+** This routine allows you to grab the file descriptor associated with an
+** event queue and use it in the readFD set of select. Useful for platforms
+** that support select, and must wait on other things besides just PLEvents.
+*/
+PR_EXTERN(PRInt32)
+PL_GetEventQueueSelectFD(PLEventQueue* self);
+
+/*******************************************************************************
+ * Event Operations
+ ******************************************************************************/
+
+/*
+** The type of an event handler function. This function is passed as an
+** initialization argument to PL_InitEvent, and called by
+** PL_HandleEvent. If the event is called synchronously, a void* result
+** may be returned (otherwise any result will be ignored).
+*/
+typedef void*
+(PR_CALLBACK *PLHandleEventProc)(PLEvent* self);
+
+/*
+** The type of an event destructor function. This function is passed as
+** an initialization argument to PL_InitEvent, and called by
+** PL_DestroyEvent.
+*/
+typedef void
+(PR_CALLBACK *PLDestroyEventProc)(PLEvent* self);
+
+/*
+** Initializes an event. Usually events are embedded in a larger event
+** structure which holds event-specific data, so this is an initializer
+** for that embedded part of the structure.
+*/
+PR_EXTERN(void)
+PL_InitEvent(PLEvent* self, void* owner,
+ PLHandleEventProc handler,
+ PLDestroyEventProc destructor);
+
+/*
+** Returns the owner of an event.
+*/
+PR_EXTERN(void*)
+PL_GetEventOwner(PLEvent* self);
+
+/*
+** Handles an event, calling the event's handler routine.
+*/
+PR_EXTERN(void)
+PL_HandleEvent(PLEvent* self);
+
+/*
+** Destroys an event, calling the event's destructor.
+*/
+PR_EXTERN(void)
+PL_DestroyEvent(PLEvent* self);
+
+/*
+** Removes an event from an event queue.
+*/
+PR_EXTERN(void)
+PL_DequeueEvent(PLEvent* self, PLEventQueue* queue);
+
+#if defined(_WIN32) || defined(WIN16)
+PR_EXTERN(HWND)
+PR_GetEventReceiverWindow();
+#endif
+
+
+/*******************************************************************************
+ * Private Stuff
+ ******************************************************************************/
+
+struct PLEvent {
+ PRCList link;
+ PLHandleEventProc handler;
+ PLDestroyEventProc destructor;
+ void* owner;
+ void* synchronousResult;
+ /* other fields follow... */
+};
+
+/******************************************************************************/
+
+/*
+** Returns the event queue associated with the main thread. Note that
+** the event queue is now created by NSPR.
+*/
+#ifdef XP_PC
+PR_EXTERN(PLEventQueue *)
+PL_GetMainEventQueue(void);
+PR_EXTERN(void)
+PL_InitializeEventsLib(char *name);
+#endif /* XP_PC */
+
+PR_END_EXTERN_C
+
+#endif /* prevent_h___ */
diff --git a/lib/ds/plhash.c b/lib/ds/plhash.c
new file mode 100644
index 00000000..fb4d5c1f
--- /dev/null
+++ b/lib/ds/plhash.c
@@ -0,0 +1,477 @@
+/* -*- 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.
+ */
+
+/*
+ * PL hash table package.
+ */
+#include "plhash.h"
+#include "prbit.h"
+#include "prlog.h"
+#include "prmem.h"
+#include "prtypes.h"
+#include <stdlib.h>
+#include <string.h>
+
+/* Compute the number of buckets in ht */
+#define NBUCKETS(ht) (1 << (PL_HASH_BITS - (ht)->shift))
+
+/* The smallest table has 16 buckets */
+#define MINBUCKETSLOG2 4
+#define MINBUCKETS (1 << MINBUCKETSLOG2)
+
+/* Compute the maximum entries given n buckets that we will tolerate, ~90% */
+#define OVERLOADED(n) ((n) - ((n) >> 3))
+
+/* Compute the number of entries below which we shrink the table by half */
+#define UNDERLOADED(n) (((n) > MINBUCKETS) ? ((n) >> 2) : 0)
+
+/*
+** Stubs for default hash allocator ops.
+*/
+static void * PR_CALLBACK
+DefaultAllocTable(void *pool, PRSize size)
+{
+#if defined(XP_MAC)
+#pragma unused (pool)
+#endif
+
+ return PR_MALLOC(size);
+}
+
+static void PR_CALLBACK
+DefaultFreeTable(void *pool, void *item)
+{
+#if defined(XP_MAC)
+#pragma unused (pool)
+#endif
+
+ PR_Free(item);
+}
+
+static PLHashEntry * PR_CALLBACK
+DefaultAllocEntry(void *pool, const void *key)
+{
+#if defined(XP_MAC)
+#pragma unused (pool,key)
+#endif
+
+ return PR_NEW(PLHashEntry);
+}
+
+static void PR_CALLBACK
+DefaultFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
+{
+#if defined(XP_MAC)
+#pragma unused (pool)
+#endif
+
+ if (flag == HT_FREE_ENTRY)
+ PR_Free(he);
+}
+
+static PLHashAllocOps defaultHashAllocOps = {
+ DefaultAllocTable, DefaultFreeTable,
+ DefaultAllocEntry, DefaultFreeEntry
+};
+
+PR_IMPLEMENT(PLHashTable *)
+PL_NewHashTable(PRUint32 n, PLHashFunction keyHash,
+ PLHashComparator keyCompare, PLHashComparator valueCompare,
+ const PLHashAllocOps *allocOps, void *allocPriv)
+{
+ PLHashTable *ht;
+ PRSize nb;
+
+ if (n <= MINBUCKETS) {
+ n = MINBUCKETSLOG2;
+ } else {
+ n = PR_CeilingLog2(n);
+ if ((PRInt32)n < 0)
+ return 0;
+ }
+
+ if (!allocOps) allocOps = &defaultHashAllocOps;
+
+ ht = (PLHashTable*)((*allocOps->allocTable)(allocPriv, sizeof *ht));
+ if (!ht)
+ return 0;
+ memset(ht, 0, sizeof *ht);
+ ht->shift = PL_HASH_BITS - n;
+ n = 1 << n;
+#if defined(WIN16)
+ if (n > 16000) {
+ (*allocOps->freeTable)(allocPriv, ht);
+ return 0;
+ }
+#endif /* WIN16 */
+ nb = n * sizeof(PLHashEntry *);
+ ht->buckets = (PLHashEntry**)((*allocOps->allocTable)(allocPriv, nb));
+ if (!ht->buckets) {
+ (*allocOps->freeTable)(allocPriv, ht);
+ return 0;
+ }
+ memset(ht->buckets, 0, nb);
+
+ ht->keyHash = keyHash;
+ ht->keyCompare = keyCompare;
+ ht->valueCompare = valueCompare;
+ ht->allocOps = allocOps;
+ ht->allocPriv = allocPriv;
+ return ht;
+}
+
+PR_IMPLEMENT(void)
+PL_HashTableDestroy(PLHashTable *ht)
+{
+ PRUint32 i, n;
+ PLHashEntry *he, *next;
+ const PLHashAllocOps *allocOps = ht->allocOps;
+ void *allocPriv = ht->allocPriv;
+
+ n = NBUCKETS(ht);
+ for (i = 0; i < n; i++) {
+ for (he = ht->buckets[i]; he; he = next) {
+ next = he->next;
+ (*allocOps->freeEntry)(allocPriv, he, HT_FREE_ENTRY);
+ }
+ }
+#ifdef DEBUG
+ memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]);
+#endif
+ (*allocOps->freeTable)(allocPriv, ht->buckets);
+#ifdef DEBUG
+ memset(ht, 0xDB, sizeof *ht);
+#endif
+ (*allocOps->freeTable)(allocPriv, ht);
+}
+
+/*
+** Multiplicative hash, from Knuth 6.4.
+*/
+#define GOLDEN_RATIO 0x9E3779B9U
+
+PR_IMPLEMENT(PLHashEntry **)
+PL_HashTableRawLookup(PLHashTable *ht, PLHashNumber keyHash, const void *key)
+{
+ PLHashEntry *he, **hep, **hep0;
+ PLHashNumber h;
+
+#ifdef HASHMETER
+ ht->nlookups++;
+#endif
+ h = keyHash * GOLDEN_RATIO;
+ h >>= ht->shift;
+ hep = hep0 = &ht->buckets[h];
+ while ((he = *hep) != 0) {
+ if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) {
+ /* Move to front of chain if not already there */
+ if (hep != hep0) {
+ *hep = he->next;
+ he->next = *hep0;
+ *hep0 = he;
+ }
+ return hep0;
+ }
+ hep = &he->next;
+#ifdef HASHMETER
+ ht->nsteps++;
+#endif
+ }
+ return hep;
+}
+
+PR_IMPLEMENT(PLHashEntry *)
+PL_HashTableRawAdd(PLHashTable *ht, PLHashEntry **hep,
+ PLHashNumber keyHash, const void *key, void *value)
+{
+ PRUint32 i, n;
+ PLHashEntry *he, *next, **oldbuckets;
+ PRSize nb;
+
+ /* Grow the table if it is overloaded */
+ n = NBUCKETS(ht);
+ if (ht->nentries >= OVERLOADED(n)) {
+#ifdef HASHMETER
+ ht->ngrows++;
+#endif
+ ht->shift--;
+ oldbuckets = ht->buckets;
+#if defined(WIN16)
+ if (2 * n > 16000)
+ return 0;
+#endif /* WIN16 */
+ nb = 2 * n * sizeof(PLHashEntry *);
+ ht->buckets = (PLHashEntry**)
+ ((*ht->allocOps->allocTable)(ht->allocPriv, nb));
+ if (!ht->buckets) {
+ ht->buckets = oldbuckets;
+ return 0;
+ }
+ memset(ht->buckets, 0, nb);
+
+ for (i = 0; i < n; i++) {
+ for (he = oldbuckets[i]; he; he = next) {
+ next = he->next;
+ hep = PL_HashTableRawLookup(ht, he->keyHash, he->key);
+ PR_ASSERT(*hep == 0);
+ he->next = 0;
+ *hep = he;
+ }
+ }
+#ifdef DEBUG
+ memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
+#endif
+ (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
+ hep = PL_HashTableRawLookup(ht, keyHash, key);
+ }
+
+ /* Make a new key value entry */
+ he = (*ht->allocOps->allocEntry)(ht->allocPriv, key);
+ if (!he)
+ return 0;
+ he->keyHash = keyHash;
+ he->key = key;
+ he->value = value;
+ he->next = *hep;
+ *hep = he;
+ ht->nentries++;
+ return he;
+}
+
+PR_IMPLEMENT(PLHashEntry *)
+PL_HashTableAdd(PLHashTable *ht, const void *key, void *value)
+{
+ PLHashNumber keyHash;
+ PLHashEntry *he, **hep;
+
+ keyHash = (*ht->keyHash)(key);
+ hep = PL_HashTableRawLookup(ht, keyHash, key);
+ if ((he = *hep) != 0) {
+ /* Hit; see if values match */
+ if ((*ht->valueCompare)(he->value, value)) {
+ /* key,value pair is already present in table */
+ return he;
+ }
+ if (he->value)
+ (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_VALUE);
+ he->value = value;
+ return he;
+ }
+ return PL_HashTableRawAdd(ht, hep, keyHash, key, value);
+}
+
+PR_IMPLEMENT(void)
+PL_HashTableRawRemove(PLHashTable *ht, PLHashEntry **hep, PLHashEntry *he)
+{
+ PRUint32 i, n;
+ PLHashEntry *next, **oldbuckets;
+ PRSize nb;
+
+ *hep = he->next;
+ (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_ENTRY);
+
+ /* Shrink table if it's underloaded */
+ n = NBUCKETS(ht);
+ if (--ht->nentries < UNDERLOADED(n)) {
+#ifdef HASHMETER
+ ht->nshrinks++;
+#endif
+ ht->shift++;
+ oldbuckets = ht->buckets;
+ nb = n * sizeof(PLHashEntry*) / 2;
+ ht->buckets = (PLHashEntry**)(
+ (*ht->allocOps->allocTable)(ht->allocPriv, nb));
+ if (!ht->buckets) {
+ ht->buckets = oldbuckets;
+ return;
+ }
+ memset(ht->buckets, 0, nb);
+
+ for (i = 0; i < n; i++) {
+ for (he = oldbuckets[i]; he; he = next) {
+ next = he->next;
+ hep = PL_HashTableRawLookup(ht, he->keyHash, he->key);
+ PR_ASSERT(*hep == 0);
+ he->next = 0;
+ *hep = he;
+ }
+ }
+#ifdef DEBUG
+ memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
+#endif
+ (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
+ }
+}
+
+PR_IMPLEMENT(PRBool)
+PL_HashTableRemove(PLHashTable *ht, const void *key)
+{
+ PLHashNumber keyHash;
+ PLHashEntry *he, **hep;
+
+ keyHash = (*ht->keyHash)(key);
+ hep = PL_HashTableRawLookup(ht, keyHash, key);
+ if ((he = *hep) == 0)
+ return PR_FALSE;
+
+ /* Hit; remove element */
+ PL_HashTableRawRemove(ht, hep, he);
+ return PR_TRUE;
+}
+
+PR_IMPLEMENT(void *)
+PL_HashTableLookup(PLHashTable *ht, const void *key)
+{
+ PLHashNumber keyHash;
+ PLHashEntry *he, **hep;
+
+ keyHash = (*ht->keyHash)(key);
+ hep = PL_HashTableRawLookup(ht, keyHash, key);
+ if ((he = *hep) != 0) {
+ return he->value;
+ }
+ return 0;
+}
+
+/*
+** Iterate over the entries in the hash table calling func for each
+** entry found. Stop if "f" says to (return value & PR_ENUMERATE_STOP).
+** Return a count of the number of elements scanned.
+*/
+PR_IMPLEMENT(int)
+PL_HashTableEnumerateEntries(PLHashTable *ht, PLHashEnumerator f, void *arg)
+{
+ PLHashEntry *he, **hep;
+ PRUint32 i, nbuckets;
+ int rv, n = 0;
+ PLHashEntry *todo = 0;
+
+ nbuckets = NBUCKETS(ht);
+ for (i = 0; i < nbuckets; i++) {
+ hep = &ht->buckets[i];
+ while ((he = *hep) != 0) {
+ rv = (*f)(he, n, arg);
+ n++;
+ if (rv & (HT_ENUMERATE_REMOVE | HT_ENUMERATE_UNHASH)) {
+ *hep = he->next;
+ if (rv & HT_ENUMERATE_REMOVE) {
+ he->next = todo;
+ todo = he;
+ }
+ } else {
+ hep = &he->next;
+ }
+ if (rv & HT_ENUMERATE_STOP) {
+ goto out;
+ }
+ }
+ }
+
+out:
+ hep = &todo;
+ while ((he = *hep) != 0) {
+ PL_HashTableRawRemove(ht, hep, he);
+ }
+ return n;
+}
+
+#ifdef HASHMETER
+#include <math.h>
+#include <stdio.h>
+
+PR_IMPLEMENT(void)
+PL_HashTableDumpMeter(PLHashTable *ht, PLHashEnumerator dump, FILE *fp)
+{
+ double mean, variance;
+ PRUint32 nchains, nbuckets;
+ PRUint32 i, n, maxChain, maxChainLen;
+ PLHashEntry *he;
+
+ variance = 0;
+ nchains = 0;
+ maxChainLen = 0;
+ nbuckets = NBUCKETS(ht);
+ for (i = 0; i < nbuckets; i++) {
+ he = ht->buckets[i];
+ if (!he)
+ continue;
+ nchains++;
+ for (n = 0; he; he = he->next)
+ n++;
+ variance += n * n;
+ if (n > maxChainLen) {
+ maxChainLen = n;
+ maxChain = i;
+ }
+ }
+ mean = (double)ht->nentries / nchains;
+ variance = fabs(variance / nchains - mean * mean);
+
+ fprintf(fp, "\nHash table statistics:\n");
+ fprintf(fp, " number of lookups: %u\n", ht->nlookups);
+ fprintf(fp, " number of entries: %u\n", ht->nentries);
+ fprintf(fp, " number of grows: %u\n", ht->ngrows);
+ fprintf(fp, " number of shrinks: %u\n", ht->nshrinks);
+ fprintf(fp, " mean steps per hash: %g\n", (double)ht->nsteps
+ / ht->nlookups);
+ fprintf(fp, "mean hash chain length: %g\n", mean);
+ fprintf(fp, " standard deviation: %g\n", sqrt(variance));
+ fprintf(fp, " max hash chain length: %u\n", maxChainLen);
+ fprintf(fp, " max hash chain: [%u]\n", maxChain);
+
+ for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++)
+ if ((*dump)(he, i, fp) != HT_ENUMERATE_NEXT)
+ break;
+}
+#endif /* HASHMETER */
+
+PR_IMPLEMENT(int)
+PL_HashTableDump(PLHashTable *ht, PLHashEnumerator dump, FILE *fp)
+{
+ int count;
+
+ count = PL_HashTableEnumerateEntries(ht, dump, fp);
+#ifdef HASHMETER
+ PL_HashTableDumpMeter(ht, dump, fp);
+#endif
+ return count;
+}
+
+PR_IMPLEMENT(PLHashNumber)
+PL_HashString(const void *key)
+{
+ PLHashNumber h;
+ const PRUint8 *s;
+
+ h = 0;
+ for (s = (const PRUint8*)key; *s; s++)
+ h = (h >> 28) ^ (h << 4) ^ *s;
+ return h;
+}
+
+PR_IMPLEMENT(int)
+PL_CompareStrings(const void *v1, const void *v2)
+{
+ return strcmp((const char*)v1, (const char*)v2) == 0;
+}
+
+PR_IMPLEMENT(int)
+PL_CompareValues(const void *v1, const void *v2)
+{
+ return v1 == v2;
+}
diff --git a/lib/ds/plhash.h b/lib/ds/plhash.h
new file mode 100644
index 00000000..24898d2c
--- /dev/null
+++ b/lib/ds/plhash.h
@@ -0,0 +1,131 @@
+/* -*- 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 plhash_h___
+#define plhash_h___
+/*
+ * API to portable hash table code.
+ */
+#include <stdio.h>
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+typedef struct PLHashEntry PLHashEntry;
+typedef struct PLHashTable PLHashTable;
+typedef PRUint32 PLHashNumber;
+#define PL_HASH_BITS 32 /* Number of bits in PLHashNumber */
+typedef PLHashNumber (PR_CALLBACK *PLHashFunction)(const void *key);
+typedef PRIntn (PR_CALLBACK *PLHashComparator)(const void *v1, const void *v2);
+typedef PRIntn (PR_CALLBACK *PLHashEnumerator)(PLHashEntry *he, PRIntn index, void *arg);
+
+/* Flag bits in PLHashEnumerator's return value */
+#define HT_ENUMERATE_NEXT 0 /* continue enumerating entries */
+#define HT_ENUMERATE_STOP 1 /* stop enumerating entries */
+#define HT_ENUMERATE_REMOVE 2 /* remove and free the current entry */
+#define HT_ENUMERATE_UNHASH 4 /* just unhash the current entry */
+
+typedef struct PLHashAllocOps {
+ void * (PR_CALLBACK *allocTable)(void *pool, PRSize size);
+ void (PR_CALLBACK *freeTable)(void *pool, void *item);
+ PLHashEntry * (PR_CALLBACK *allocEntry)(void *pool, const void *key);
+ void (PR_CALLBACK *freeEntry)(void *pool, PLHashEntry *he, PRUintn flag);
+} PLHashAllocOps;
+
+#define HT_FREE_VALUE 0 /* just free the entry's value */
+#define HT_FREE_ENTRY 1 /* free value and entire entry */
+
+struct PLHashEntry {
+ PLHashEntry *next; /* hash chain linkage */
+ PLHashNumber keyHash; /* key hash function result */
+ const void *key; /* ptr to opaque key */
+ void *value; /* ptr to opaque value */
+};
+
+struct PLHashTable {
+ PLHashEntry **buckets; /* vector of hash buckets */
+ PRUint32 nentries; /* number of entries in table */
+ PRUint32 shift; /* multiplicative hash shift */
+ PLHashFunction keyHash; /* key hash function */
+ PLHashComparator keyCompare; /* key comparison function */
+ PLHashComparator valueCompare; /* value comparison function */
+ const PLHashAllocOps *allocOps; /* allocation operations */
+ void *allocPriv; /* allocation private data */
+#ifdef HASHMETER
+ PRUint32 nlookups; /* total number of lookups */
+ PRUint32 nsteps; /* number of hash chains traversed */
+ PRUint32 ngrows; /* number of table expansions */
+ PRUint32 nshrinks; /* number of table contractions */
+#endif
+};
+
+/*
+ * Create a new hash table.
+ * If allocOps is null, use default allocator ops built on top of malloc().
+ */
+PR_EXTERN(PLHashTable *)
+PL_NewHashTable(PRUint32 numBuckets, PLHashFunction keyHash,
+ PLHashComparator keyCompare, PLHashComparator valueCompare,
+ const PLHashAllocOps *allocOps, void *allocPriv);
+
+PR_EXTERN(void)
+PL_HashTableDestroy(PLHashTable *ht);
+
+/* Higher level access methods */
+PR_EXTERN(PLHashEntry *)
+PL_HashTableAdd(PLHashTable *ht, const void *key, void *value);
+
+PR_EXTERN(PRBool)
+PL_HashTableRemove(PLHashTable *ht, const void *key);
+
+PR_EXTERN(void *)
+PL_HashTableLookup(PLHashTable *ht, const void *key);
+
+PR_EXTERN(PRIntn)
+PL_HashTableEnumerateEntries(PLHashTable *ht, PLHashEnumerator f, void *arg);
+
+/* General-purpose C string hash function. */
+PR_EXTERN(PLHashNumber)
+PL_HashString(const void *key);
+
+/* Compare strings using strcmp(), return true if equal. */
+PR_EXTERN(PRIntn)
+PL_CompareStrings(const void *v1, const void *v2);
+
+/* Stub function just returns v1 == v2 */
+PR_EXTERN(PRIntn)
+PL_CompareValues(const void *v1, const void *v2);
+
+/* Low level access methods */
+PR_EXTERN(PLHashEntry **)
+PL_HashTableRawLookup(PLHashTable *ht, PLHashNumber keyHash, const void *key);
+
+PR_EXTERN(PLHashEntry *)
+PL_HashTableRawAdd(PLHashTable *ht, PLHashEntry **hep, PLHashNumber keyHash,
+ const void *key, void *value);
+
+PR_EXTERN(void)
+PL_HashTableRawRemove(PLHashTable *ht, PLHashEntry **hep, PLHashEntry *he);
+
+/* This can be trivially implemented using PL_HashTableEnumerateEntries. */
+PR_EXTERN(PRIntn)
+PL_HashTableDump(PLHashTable *ht, PLHashEnumerator dump, FILE *fp);
+
+PR_END_EXTERN_C
+
+#endif /* plhash_h___ */
diff --git a/lib/ds/plvector.c b/lib/ds/plvector.c
new file mode 100644
index 00000000..7cde6f35
--- /dev/null
+++ b/lib/ds/plvector.c
@@ -0,0 +1,318 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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 "plvector.h"
+#include "prmem.h"
+#include <string.h>
+
+#ifdef XP_WIN16
+#define SIZE_T_MAX 0xFF80 /* a little less than 64K, the max alloc size on win16. */
+#define MAX_ARR_ELEMS SIZE_T_MAX/sizeof(void*)
+#endif
+
+PR_IMPLEMENT(PLVector*)
+PL_NewVector(PRUint32 initialSize, PRInt32 initialGrowBy)
+{
+ PLVector* v = (PLVector*)PR_Malloc(sizeof(PLVector*));
+ if (v == NULL)
+ return NULL;
+ PL_VectorInitialize(v, initialSize, initialGrowBy);
+ return v;
+}
+
+PR_IMPLEMENT(void)
+PL_VectorDestroy(PLVector* v)
+{
+ PL_VectorFinalize(v);
+ PR_Free(v);
+}
+
+/* Initializes an existing vector */
+PR_IMPLEMENT(void)
+PL_VectorInitialize(PLVector* v, PRUint32 initialSize, PRInt32 initialGrowBy)
+{
+ v->data = NULL;
+ v->size = v->maxSize = v->growBy = 0;
+ if (initialSize > 0 || initialGrowBy > 0)
+ PL_VectorSetSize(v, initialSize, initialGrowBy);
+}
+
+/* Destroys the elements, but doesn't free the vector */
+PR_IMPLEMENT(void)
+PL_VectorFinalize(PLVector* v)
+{
+ /* This implementation doesn't do anything to delete the elements
+ in the vector -- that's up to the caller. (Don't shoot me,
+ I just copied the code from libmsg.) */
+ PR_Free(v->data);
+}
+
+PR_IMPLEMENT(PRBool)
+PL_VectorSetSize(PLVector* v, PRUint32 newSize, PRInt32 growBy)
+{
+ if (growBy != -1)
+ v->growBy = growBy; /* set new size */
+
+ if (newSize == 0) {
+ /* shrink to nothing */
+ PR_Free(v->data);
+ v->data = NULL;
+ v->size = v->maxSize = 0;
+ }
+ else if (v->data == NULL) {
+ /* create one with exact size */
+#ifdef SIZE_T_MAX
+ PR_ASSERT(newSize <= SIZE_T_MAX/sizeof(void*)); /* no overflow */
+#endif
+ v->data = (void**)PR_Malloc(newSize * sizeof(void *));
+ if (v->data == NULL) {
+ v->size = 0;
+ return PR_FALSE;
+ }
+
+ memset(v->data, 0, newSize * sizeof(void *)); /* zero fill */
+
+ v->size = v->maxSize = newSize;
+ }
+ else if (newSize <= v->maxSize) {
+ /* it fits */
+ if (newSize > v->size) {
+ /* initialize the new elements */
+
+ memset(&v->data[v->size], 0, (newSize-v->size) * sizeof(void *));
+ }
+ v->size = newSize;
+ }
+ else {
+ /* otherwise, grow array */
+ PRUint32 newMax;
+ void** newData;
+ PRInt32 growBy = v->growBy;
+ if (growBy == 0) {
+ /* heuristically determine growth when growBy == 0
+ (this avoids heap fragmentation in many situations) */
+ growBy = PR_MIN(1024, PR_MAX(4, v->size / 8));
+ }
+#ifdef MAX_ARR_ELEMS
+ if (v->size + growBy > MAX_ARR_ELEMS)
+ growBy = MAX_ARR_ELEMS - v->size;
+#endif
+ if (newSize < v->maxSize + growBy)
+ newMax = v->maxSize + growBy; /* granularity */
+ else
+ newMax = newSize; /* no slush */
+
+#ifdef SIZE_T_MAX
+ if (newMax >= SIZE_T_MAX/sizeof(void*))
+ return PR_FALSE;
+ PR_ASSERT(newMax <= SIZE_T_MAX/sizeof(void*)); /* no overflow */
+#endif
+ PR_ASSERT(newMax >= v->maxSize); /* no wrap around */
+ newData = (void**)PR_Malloc(newMax * sizeof(void*));
+
+ if (newData != NULL) {
+ /* copy new data from old */
+ memcpy(newData, v->data, v->size * sizeof(void*));
+
+ /* construct remaining elements */
+ PR_ASSERT(newSize > v->size);
+
+ memset(&newData[v->size], 0, (newSize-v->size) * sizeof(void*));
+
+ /* get rid of old stuff (note: no destructors called) */
+ PR_Free(v->data);
+ v->data = newData;
+ v->size = newSize;
+ v->maxSize = newMax;
+ }
+ else {
+ return PR_FALSE;
+ }
+ }
+ return PR_TRUE;
+}
+
+PR_IMPLEMENT(PRBool)
+PL_VectorIsValidIndex(PLVector* v, PRUint32 index)
+{
+ return (index < v->size) ? PR_TRUE : PR_FALSE;
+}
+
+
+PR_IMPLEMENT(void)
+PL_VectorCompact(PLVector* v)
+{
+ if (v->size != v->maxSize) {
+ /* shrink to desired size */
+#ifdef SIZE_T_MAX
+ PR_ASSERT(v->size <= SIZE_T_MAX/sizeof(void *)); /* no overflow */
+#endif
+ void ** newData = NULL;
+ if (v->size != 0) {
+ newData = (void **)PR_Malloc(v->size * sizeof(void *));
+ /* copy new data from old */
+ memcpy(newData, v->data, v->size * sizeof(void *));
+ }
+
+ /* get rid of old stuff (note: no destructors called) */
+ PR_Free(v->data);
+ v->data = newData;
+ v->maxSize = v->size;
+ }
+}
+
+#if 0 /* becomes Copy */
+PR_IMPLEMENT(void)
+PL_VectorSplice(PLVector* v, PRUint32 startIndex, PLVector* newVector)
+{
+ PRUint32 i;
+ PR_ASSERT(newVector != NULL);
+
+ if (PL_VectorGetSize(newVector) > 0) {
+ PL_VectorInsert(v, startIndex, PL_VectorGet(newVector, 0),
+ PL_VectorGetSize(newVector));
+ for (i = 0; i < PL_VectorGetSize(newVector); i++)
+ PL_VectorSet(v, startIndex + i, PL_VectorGet(newVector, i));
+ }
+}
+#endif
+
+PR_IMPLEMENT(void)
+PL_VectorCopy(PLVector* dstVector, PRUint32 dstPosition,
+ PLVector* srcVector, PRUint32 srcPosition, PRUint32 length)
+{
+ PR_ASSERT(0); /* XXX not implemented yet */
+#if 0
+ PL_VectorSetSize(dstVector, PR_MAX(PL_VectorGetSize(dstVector),
+ PL_VectorGetSize(srcVector)),
+ PL_VECTOR_GROW_DEFAULT);
+
+ if (v->data)
+ PR_Free(v->data);
+ v->size = oldA->v->size;
+ v->maxSize = oldA->v->maxSize;
+ v->growBy = oldA->v->growBy;
+ v->data = (void**)PR_Malloc(v->size * sizeof(void *));
+ if (v->data == NULL) {
+ v->size = 0;
+ }
+ else {
+ memcpy(v->data, oldA->v->data, v->size * sizeof(void *));
+ }
+#endif
+}
+
+PR_IMPLEMENT(PLVector*)
+PL_VectorClone(PLVector* v)
+{
+ PLVector* newVec = PL_NewVector(v->size, v->growBy);
+ PL_VectorCopy(newVec, 0, v, 0, v->size);
+ return newVec;
+}
+
+/* Accessing elements */
+
+PR_IMPLEMENT(void)
+PL_VectorSet(PLVector* v, PRUint32 index, void* newElement)
+{
+ if (index >= v->size) {
+ if (!PL_VectorSetSize(v, index+1, PL_VECTOR_GROW_DEFAULT))
+ return;
+ }
+ v->data[index] = newElement;
+}
+
+/* Adds at the end */
+PR_IMPLEMENT(PRUint32)
+PL_VectorAdd(PLVector* v, void* newElement)
+{
+ PRUint32 index = v->size;
+#ifdef XP_WIN16
+ if (index >= SIZE_T_MAX / 4L) {
+ return -1;
+ }
+#endif
+ PL_VectorSet(v, index, newElement);
+ return index;
+}
+
+/* Inserts new element count times at index */
+PR_IMPLEMENT(void)
+PL_VectorInsert(PLVector* v, PRUint32 index, void* newElement, PRUint32 count)
+{
+ PR_ASSERT(count > 0); /* zero or negative size not allowed */
+
+ if (index >= v->size) {
+ /* adding after the end of the array */
+ if (!PL_VectorSetSize(v, index + count, PL_VECTOR_GROW_DEFAULT))
+ return; /* grow so index is valid */
+ }
+ else {
+ /* inserting in the middle of the array */
+ PRUint32 nOldSize = v->size;
+ if (!PL_VectorSetSize(v, v->size + count, PL_VECTOR_GROW_DEFAULT))
+ return; /* grow it to new size */
+ /* shift old data up to fill gap */
+ memmove(&v->data[index+count], &v->data[index],
+ (nOldSize-index) * sizeof(void *));
+
+ /* re-init slots we copied from */
+ memset(&v->data[index], 0, count * sizeof(void *));
+ }
+
+ /* insert new value in the gap */
+ PR_ASSERT(index + count <= v->size);
+ while (count--)
+ v->data[index++] = newElement;
+}
+
+/* Removes count elements at index */
+PR_IMPLEMENT(void)
+PL_VectorRemove(PLVector* v, PRUint32 index, PRUint32 count)
+{
+ PRUint32 moveCount;
+ PR_ASSERT(count >= 0);
+ PR_ASSERT(index + count <= v->size);
+
+ /* just remove a range */
+ moveCount = v->size - (index + count);
+
+ if (moveCount)
+ memmove(&v->data[index], &v->data[index + count],
+ moveCount * sizeof(void *));
+ v->size -= count;
+}
+
+#ifdef DEBUG
+
+PR_IMPLEMENT(void)
+PL_VectorAssertValid(PLVector* v)
+{
+ if (v->data == NULL) {
+ PR_ASSERT(v->size == 0);
+ PR_ASSERT(v->maxSize == 0);
+ }
+ else {
+ PR_ASSERT(v->size >= 0);
+ PR_ASSERT(v->maxSize >= 0);
+ PR_ASSERT(v->size <= v->maxSize);
+ }
+}
+
+#endif
+
diff --git a/lib/ds/plvector.h b/lib/ds/plvector.h
new file mode 100644
index 00000000..a82e849d
--- /dev/null
+++ b/lib/ds/plvector.h
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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 plvector_h__
+#define plvector_h__
+
+#include "prtypes.h"
+#include "prlog.h"
+
+PR_BEGIN_EXTERN_C
+
+/* Vectors are extensible arrays */
+
+typedef struct PLVector {
+ void** data; /* the actual array of data */
+ PRUint32 size; /* # of elements (upperBound - 1) */
+ PRUint32 maxSize; /* max allocated */
+ PRInt32 growBy; /* grow amount */
+} PLVector;
+
+PR_EXTERN(PLVector*)
+PL_NewVector(PRUint32 initialSize, PRInt32 initialGrowBy);
+
+PR_EXTERN(void)
+PL_VectorDestroy(PLVector* v);
+
+/* Initializes an existing vector */
+PR_EXTERN(void)
+PL_VectorInitialize(PLVector* v, PRUint32 initialSize, PRInt32 initialGrowBy);
+
+/* Destroys the elements, but doesn't free the vector */
+PR_EXTERN(void)
+PL_VectorFinalize(PLVector* v);
+
+#define PL_VectorGetSize(v) ((v)->size)
+
+#define PL_VECTOR_GROW_DEFAULT (-1)
+
+PR_EXTERN(PRBool)
+PL_VectorSetSize(PLVector* v, PRUint32 newSize, PRInt32 growBy);
+
+PR_EXTERN(PRBool)
+PL_VectorIsValidIndex(PLVector* v, PRUint32 index);
+
+PR_EXTERN(void)
+PL_VectorCompact(PLVector* v);
+
+PR_EXTERN(void)
+PL_VectorCopy(PLVector* dstVector, PRUint32 dstPosition,
+ PLVector* srcVector, PRUint32 srcPosition, PRUint32 length);
+
+PR_EXTERN(PLVector*)
+PL_VectorClone(PLVector* v);
+
+/* Accessing elements */
+
+#define PL_VectorGetAddr(v, index) (PR_ASSERT((index) < (v)->size), &(v)->data[index])
+
+#define PL_VectorGet(v, index) (*PL_VectorGetAddr(v, index))
+
+PR_EXTERN(void)
+PL_VectorSet(PLVector* v, PRUint32 index, void* newElement);
+
+/* Adds at the end */
+PR_EXTERN(PRUint32)
+PL_VectorAdd(PLVector* v, void* newElement);
+
+/* Inserts new element count times at index */
+PR_EXTERN(void)
+PL_VectorInsert(PLVector* v, PRUint32 index, void* newElement, PRUint32 count);
+
+/* Removes count elements at index */
+PR_EXTERN(void)
+PL_VectorRemove(PLVector* v, PRUint32 index, PRUint32 count);
+
+#ifdef DEBUG
+
+PR_EXTERN(void)
+PL_VectorAssertValid(PLVector* v);
+
+#endif
+
+PR_END_EXTERN_C
+
+#endif /* plvector_h__ */
diff --git a/lib/libc/Makefile b/lib/libc/Makefile
new file mode 100644
index 00000000..55ea8b62
--- /dev/null
+++ b/lib/libc/Makefile
@@ -0,0 +1,32 @@
+#
+# 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 = ../..
+
+export NSPR20=1
+
+include $(MOD_DEPTH)/config/config.mk
+
+DIRS = include src
+
+include $(MOD_DEPTH)/config/rules.mk
+
+export:: $(TARGETS)
+
+install:: export
diff --git a/lib/libc/README b/lib/libc/README
new file mode 100644
index 00000000..74f24690
--- /dev/null
+++ b/lib/libc/README
@@ -0,0 +1,20 @@
+NSPR 2.0 libc functions
+-----------------------
+
+Last edited: AOF 04 March 1997
+
+This directory contains various libc-types of functions. All functions in
+this directory are platform independent, thread friendly (both safe and
+efficient). They are contributed from various sources, though the contri-
+butions are monitored by the NSPR group (mailto:freier).
+
+All API items exported by these functions will contain the same three
+character prefix, "PL_" (Portable Library). Internal function names
+that are not exported (static) are of little concern, though some caution
+must be used on those elements that are 'extern' but not really intended
+to be part of the API. Those should all have a prefix of "_PL_" (is that
+legal?).
+
+The responsibility for contributions in this area are distributed among
+all interested parties.
+
diff --git a/lib/libc/include/MANIFEST b/lib/libc/include/MANIFEST
new file mode 100644
index 00000000..63e8861d
--- /dev/null
+++ b/lib/libc/include/MANIFEST
@@ -0,0 +1,9 @@
+#
+# This is a list of local files which get copied to the mozilla:dist directory
+#
+
+plbase64.h
+plerror.h
+plgetopt.h
+plresolv.h
+plstr.h
diff --git a/lib/libc/include/Makefile b/lib/libc/include/Makefile
new file mode 100644
index 00000000..fc5392f1
--- /dev/null
+++ b/lib/libc/include/Makefile
@@ -0,0 +1,37 @@
+#
+# 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
+
+HEADERS = $(wildcard *.h)
+
+RELEASE_HEADERS = $(HEADERS)
+RELEASE_HEADERS_DEST = $(RELEASE_INCLUDE_DIR)
+
+include $(MOD_DEPTH)/config/rules.mk
+
+export:: $(HEADERS)
+ $(INSTALL) -m 444 $(HEADERS) $(DIST)/include
+ifeq ($(MOZ_BITS),16)
+ $(INSTALL) -m 444 $(HEADERS) $(MOZ_INCL)
+endif
+
+install:: export
+
diff --git a/lib/libc/include/README b/lib/libc/include/README
new file mode 100644
index 00000000..2b852189
--- /dev/null
+++ b/lib/libc/include/README
@@ -0,0 +1,7 @@
+NSPR 2.0 libc functions
+-----------------------
+
+Last edited: AOF 04 March 1997
+
+This directory contains the API for various libc-types of functions.
+
diff --git a/lib/libc/include/plbase64.h b/lib/libc/include/plbase64.h
new file mode 100644
index 00000000..86a8f3b3
--- /dev/null
+++ b/lib/libc/include/plbase64.h
@@ -0,0 +1,79 @@
+/* -*- 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 _plbase64_h
+#define _plbase64_h
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+/*
+ * PL_Base64Encode
+ *
+ * This routine encodes the data pointed to by the "src" parameter using the
+ * base64 algorithm, and returns a pointer to the result. If the "srclen"
+ * parameter is not zero, it specifies the length of the source data. If it
+ * is zero, the source data is assumed to be null-terminated, and PL_strlen
+ * is used to determine the source length. If the "dest" parameter is not
+ * null, it is assumed to point to a buffer of sufficient size (which may be
+ * calculated: ((srclen + 2)/3)*4) into which the encoded data is placed
+ * (without any termination). If the "dest" parameter is null, a buffer is
+ * allocated from the heap to hold the encoded data, and the result *will*
+ * be terminated with an extra null character. It is the caller's
+ * responsibility to free the result when it is allocated. A null is returned
+ * if the allocation fails.
+ */
+
+PR_EXTERN(char *)
+PL_Base64Encode
+(
+ const char *src,
+ PRUint32 srclen,
+ char *dest
+);
+
+/*
+ * PL_Base64Decode
+ *
+ * This routine decodes the data pointed to by the "src" parameter using
+ * the base64 algorithm, and returns a pointer to the result. The source
+ * may either include or exclude any trailing '=' characters. If the
+ * "srclen" parameter is not zero, it specifies the length of the source
+ * data. If it is zero, PL_strlen will be used to determine the source
+ * length. If the "dest" parameter is not null, it is assumed to point to
+ * a buffer of sufficient size (which may be calculated: (srclen * 3)/4
+ * when srclen includes the '=' characters) into which the decoded data
+ * is placed (without any termination). If the "dest" parameter is null,
+ * a buffer is allocated from the heap to hold the decoded data, and the
+ * result *will* be terminated with an extra null character. It is the
+ * caller's responsibility to free the result when it is allocated. A null
+ * is retuned if the allocation fails, or if the source is not well-coded.
+ */
+
+PR_EXTERN(char *)
+PL_Base64Decode
+(
+ const char *src,
+ PRUint32 srclen,
+ char *dest
+);
+
+PR_END_EXTERN_C
+
+#endif /* _plbase64_h */
diff --git a/lib/libc/include/plerror.h b/lib/libc/include/plerror.h
new file mode 100644
index 00000000..9e19ba14
--- /dev/null
+++ b/lib/libc/include/plerror.h
@@ -0,0 +1,47 @@
+/* -*- 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: plerror.h
+** Description: Simple routine to print translate the calling thread's
+** error numbers and print them.
+*/
+
+#if defined(PLERROR_H)
+#else
+#define PLERROR_H
+
+#include "prio.h"
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+/*
+** Print the messages to "syserr" prepending 'msg' if not NULL.
+*/
+PR_EXTERN(void) PL_PrintError(const char *msg);
+
+/*
+** Print the messages to specified output file prepending 'msg' if not NULL.
+*/
+PR_EXTERN(void) PL_FPrintError(PRFileDesc *output, const char *msg);
+
+PR_END_EXTERN_C
+
+#endif /* defined(PLERROR_H) */
+
+/* plerror.h */
diff --git a/lib/libc/include/plgetopt.h b/lib/libc/include/plgetopt.h
new file mode 100644
index 00000000..59f4e8e7
--- /dev/null
+++ b/lib/libc/include/plgetopt.h
@@ -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.
+ */
+
+/*
+** File: plgetopt.h
+** Description: utilities to parse argc/argv
+*/
+
+#if defined(PLGETOPT_H_)
+#else
+#define PLGETOPT_H_
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+typedef struct PLOptionInternal PLOptionInternal;
+
+typedef enum
+{
+ PL_OPT_OK, /* all's well with the option */
+ PL_OPT_EOL, /* end of options list */
+ PL_OPT_BAD /* invalid option (and value) */
+} PLOptStatus;
+
+typedef struct PLOptState
+{
+ char option; /* the name of the option */
+ const char *value; /* the value of that option | NULL */
+
+ PLOptionInternal *internal; /* private processing state */
+
+} PLOptState;
+
+PR_EXTERN(PLOptState*) PL_CreateOptState(
+ PRIntn argc, char **argv, const char *options);
+
+PR_EXTERN(void) PL_DestroyOptState(PLOptState *opt);
+
+PR_EXTERN(PLOptStatus) PL_GetNextOpt(PLOptState *opt);
+
+PR_END_EXTERN_C
+
+#endif /* defined(PLGETOPT_H_) */
+
+/* plgetopt.h */
+
diff --git a/lib/libc/include/plresolv.h b/lib/libc/include/plresolv.h
new file mode 100644
index 00000000..2cb9a3eb
--- /dev/null
+++ b/lib/libc/include/plresolv.h
@@ -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.
+ */
+
+/*
+ * plresolv.h - asynchronous name resolution using DNS
+ */
+
+#ifndef _PLRESOLV_H_
+#define _PLRESOLV_H_
+
+/*
+** THIS IS WORK IN PROGRESS. DO NOT ATTEMPT TO USE ANY PORTION OF THIS
+** API UNTIL THIS MESSAGE NO LONGER EXISTS. IF YOU DO, THEN YOU SURRENDER
+** THE RIGHT TO COMPLAIN ABOUT ANY CONTENT.
+*/
+
+#if defined(XP_UNIX)
+
+#include <prtypes.h>
+#include <prnetdb.h>
+
+NSPR_BEGIN_EXTERN_C
+
+#define PL_RESOLVE_MAXHOSTENTBUF 1024
+#define PL_RESOLVE_DEFAULT_TIMEOUT 0
+
+/* Error return codes */
+#define PL_RESOLVE_OK 0
+#define PL_RESOLVE_EWINIT 1 /* Failed to initialize window */
+#define PL_RESOLVE_EMAKE 2 /* Failed to create request */
+#define PL_RESOLVE_ELAUNCH 3 /* Error launching Async request */
+#define PL_RESOLVE_ETIMEDOUT 4 /* Request timed-out */
+#define PL_RESOLVE_EINVAL 5 /* Invalid argument */
+#define PL_RESOLVE_EOVERFLOW 6 /* Buffer Overflow */
+#define PL_RESOLVE_EUNKNOWN 7 /* berzerk error */
+
+/* ----------- Function Prototypes ----------------*/
+
+PR_EXTERN(PRStatus) PL_ResolveName(
+ const char *name, unsigned char *buf,
+ PRIntn bufsize, PRIntervalTime timeout,
+ PRHostEnt *hostentry, PRIntervalTime *ttl);
+
+PR_EXTERN(PRStatus) PL_ResolveAddr(
+ const PRNetAddr *address, unsigned char *buf,
+ PRIntn bufsize, PRIntervalTime timeout,
+ PRHostEnt *hostentry, PRIntervalTime *ttl);
+
+typedef struct PLResolveStats {
+ int re_errors;
+ int re_nu_look;
+ int re_na_look;
+ int re_replies;
+ int re_requests;
+ int re_resends;
+ int re_sent;
+ int re_timeouts;
+} PLResolveStats;
+
+typedef struct PLResoveInfo {
+ PRBool enabled;
+ PRUint32 numNameLookups;
+ PRUint32 numAddrLookups;
+ PRUint32 numLookupsInProgress;
+ PLResolveStats stats;
+} PLResoveInfo;
+
+PR_EXTERN(void) PL_ResolveInfo(PLResoveInfo *info);
+
+NSPR_END_EXTERN_C
+
+#endif /* defined(XP_UNIX) */
+
+#endif /* _PLRESOLV_H_ */
diff --git a/lib/libc/include/plstr.h b/lib/libc/include/plstr.h
new file mode 100644
index 00000000..4199faeb
--- /dev/null
+++ b/lib/libc/include/plstr.h
@@ -0,0 +1,428 @@
+/* -*- 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 _plstr_h
+#define _plstr_h
+
+/*
+ * plstr.h
+ *
+ * This header file exports the API to the NSPR portable library or string-
+ * handling functions.
+ *
+ * This API was not designed as an "optimal" or "ideal" string library; it
+ * was based on the good ol' unix string.3 functions, and was written to
+ *
+ * 1) replace the libc functions, for cross-platform consistancy,
+ * 2) complete the API on platforms lacking common functions (e.g.,
+ * strcase*), and
+ * 3) to implement some obvious "closure" functions that I've seen
+ * people hacking around in our code.
+ *
+ * Point number three largely means that most functions have an "strn"
+ * limited-length version, and all comparison routines have a non-case-
+ * sensitive version available.
+ */
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+/*
+ * PL_strlen
+ *
+ * Returns the length of the provided string, not including the trailing '\0'.
+ */
+
+PR_EXTERN(PRUint32)
+PL_strlen(const char *str);
+
+/*
+ * PL_strnlen
+ *
+ * Returns the length of the provided string, not including the trailing '\0',
+ * up to the indicated maximum. The string will not be examined beyond the
+ * maximum; if no terminating '\0' is found, the maximum will be returned.
+ */
+
+PR_EXTERN(PRUint32)
+PL_strnlen(const char *str, PRUint32 max);
+
+/*
+ * PL_strcpy
+ *
+ * Copies the source string, up to and including the trailing '\0', into the
+ * destination buffer. It does not (can not) verify that the destination
+ * buffer is large enough. It returns the "dest" argument.
+ */
+
+PR_EXTERN(char *)
+PL_strcpy(char *dest, const char *src);
+
+/*
+ * PL_strncpy
+ *
+ * Copies the source string into the destination buffer, up to and including
+ * the trailing '\0' or up to and including the max'th character, whichever
+ * comes first. It does not (can not) verify that the destination buffer is
+ * large enough. If the source string is longer than the maximum length,
+ * the result will *not* be null-terminated (JLRU).
+ */
+
+PR_EXTERN(char *)
+PL_strncpy(char *dest, const char *src, PRUint32 max);
+
+/*
+ * PL_strncpyz
+ *
+ * Copies the source string into the destination buffer, up to and including
+ * the trailing '\0' or up but not including the max'th character, whichever
+ * comes first. It does not (can not) verify that the destination buffer is
+ * large enough. The destination string is always terminated with a '\0',
+ * unlike the traditional libc implementation. It returns the "dest" argument.
+ *
+ * NOTE: If you call this with a source "abcdefg" and a max of 5, the
+ * destination will end up with "abcd\0" (i.e., it's strlen length will be 4)!
+ *
+ * This means you can do this:
+ *
+ * char buffer[ SOME_SIZE ];
+ * PL_strncpyz(buffer, src, sizeof(buffer));
+ *
+ * and the result will be properly terminated.
+ */
+
+PR_EXTERN(char *)
+PL_strncpyz(char *dest, const char *src, PRUint32 max);
+
+/*
+ * PL_strdup
+ *
+ * Returns a pointer to a malloc'd extent of memory containing a duplicate
+ * of the argument string. The size of the allocated extent is one greater
+ * than the length of the argument string, because of the terminator. A
+ * null argument, like a zero-length argument, will result in a pointer to
+ * a one-byte extent containing the null value. This routine returns null
+ * upon malloc failure.
+ */
+
+PR_EXTERN(char *)
+PL_strdup(const char *s);
+
+/*
+ * PL_strfree
+ *
+ * Free memory allocated by PL_strdup
+ */
+
+PR_EXTERN(void)
+PL_strfree(char *s);
+
+/*
+ * PL_strndup
+ *
+ * Returns a pointer to a malloc'd extent of memory containing a duplicate
+ * of the argument string, up to the maximum specified. If the argument
+ * string has a length greater than the value of the specified maximum, the
+ * return value will be a pointer to an extent of memory of length one
+ * greater than the maximum specified. A null string, a zero-length string,
+ * or a zero maximum will all result in a pointer to a one-byte extent
+ * containing the null value. This routine returns null upon malloc failure.
+ */
+
+PR_EXTERN(char *)
+PL_strndup(const char *s, PRUint32 max);
+
+/*
+ * PL_strcat
+ *
+ * Appends a copy of the string pointed to by the second argument to the
+ * end of the string pointed to by the first. The destination buffer is
+ * not (can not be) checked for sufficient size. A null destination
+ * argument returns null; otherwise, the first argument is returned.
+ */
+
+PR_EXTERN(char *)
+PL_strcat(char *dst, const char *src);
+
+/*
+ * PL_strncat
+ *
+ * Appends a copy of the string pointed to by the second argument, up to
+ * the maximum size specified, to the end of the string pointed to by the
+ * first. The destination buffer is not (can not be) checked for sufficient
+ * size. A null destination argument returns null; otherwise, the first
+ * argument is returned. If the maximum size limits the copy, then the
+ * result will *not* be null-terminated (JLRU). A null destination
+ * returns null; otherwise, the destination argument is returned.
+ */
+
+PR_EXTERN(char *)
+PL_strncat(char *dst, const char *src, PRUint32 max);
+
+/*
+ * PL_strcatn
+ *
+ * Appends a copy of the string pointed to by the third argument, to the
+ * end of the string pointed to by the first. The second argument specifies
+ * the maximum size of the destination buffer, including the null termination.
+ * If the existing string in dst is longer than the max, no action is taken.
+ * The resulting string will be null-terminated. A null destination returns
+ * null; otherwise, the destination argument is returned.
+ */
+
+PR_EXTERN(char *)
+PL_strcatn(char *dst, PRUint32 max, const char *src);
+
+/*
+ * PL_strcmp
+ *
+ * Returns an integer, the sign of which -- positive, zero, or negative --
+ * reflects the lexical sorting order of the two strings indicated. The
+ * result is positive if the first string comes after the second. The
+ * NSPR implementation is not i18n.
+ */
+
+PR_EXTERN(PRIntn)
+PL_strcmp(const char *a, const char *b);
+
+/*
+ * PL_strncmp
+ *
+ * Returns an integer, the sign of which -- positive, zero, or negative --
+ * reflects the lexical sorting order of the two strings indicated, up to
+ * the maximum specified. The result is positive if the first string comes
+ * after the second. The NSPR implementation is not i18n. If the maximum
+ * is zero, only the existance or non-existance (pointer is null) of the
+ * strings is compared.
+ */
+
+PR_EXTERN(PRIntn)
+PL_strncmp(const char *a, const char *b, PRUint32 max);
+
+/*
+ * PL_strcasecmp
+ *
+ * Returns an integer, the sign of which -- positive, zero or negative --
+ * reflects the case-insensitive lexical sorting order of the two strings
+ * indicated. The result is positive if the first string comes after the
+ * second. The NSPR implementation is not i18n.
+ */
+
+PR_EXTERN(PRIntn)
+PL_strcasecmp(const char *a, const char *b);
+
+/*
+ * PL_strncasecmp
+ *
+ * Returns an integer, the sign of which -- positive, zero or negative --
+ * reflects the case-insensitive lexical sorting order of the first n characters
+ * of the two strings indicated. The result is positive if the first string comes
+ * after the second. The NSPR implementation is not i18n.
+ */
+
+PR_EXTERN(PRIntn)
+PL_strncasecmp(const char *a, const char *b, PRUint32 max);
+
+/*
+ * PL_strchr
+ *
+ * Returns a pointer to the first instance of the specified character in the
+ * provided string. It returns null if the character is not found, or if the
+ * provided string is null. The character may be the null character.
+ */
+
+PR_EXTERN(char *)
+PL_strchr(const char *s, char c);
+
+/*
+ * PL_strrchr
+ *
+ * Returns a pointer to the last instance of the specified character in the
+ * provided string. It returns null if the character is not found, or if the
+ * provided string is null. The character may be the null character.
+ */
+
+PR_EXTERN(char *)
+PL_strrchr(const char *s, char c);
+
+/*
+ * PL_strnchr
+ *
+ * Returns a pointer to the first instance of the specified character within the
+ * first n characters of the provided string. It returns null if the character
+ * is not found, or if the provided string is null. The character may be the
+ * null character.
+ */
+
+PR_EXTERN(char *)
+PL_strnchr(const char *s, char c, PRUint32 n);
+
+/*
+ * PL_strnrchr
+ *
+ * Returns a pointer to the last instance of the specified character within the
+ * first n characters of the provided string. It returns null if the character is
+ * not found, or if the provided string is null. The character may be the null
+ * character.
+ */
+
+PR_EXTERN(char *)
+PL_strnrchr(const char *s, char c, PRUint32 n);
+
+/*
+ * NOTE: Looking for strcasechr, strcaserchr, strncasechr, or strncaserchr?
+ * Use strpbrk, strprbrk, strnpbrk or strnprbrk.
+ */
+
+/*
+ * PL_strpbrk
+ *
+ * Returns a pointer to the first instance in the first string of any character
+ * (not including the terminating null character) of the second string. It returns
+ * null if either string is null.
+ */
+
+PR_EXTERN(char *)
+PL_strpbrk(const char *s, const char *list);
+
+/*
+ * PL_strprbrk
+ *
+ * Returns a pointer to the last instance in the first string of any character
+ * (not including the terminating null character) of the second string. It returns
+ * null if either string is null.
+ */
+
+PR_EXTERN(char *)
+PL_strprbrk(const char *s, const char *list);
+
+/*
+ * PL_strnpbrk
+ *
+ * Returns a pointer to the first instance (within the first n characters) of any
+ * character (not including the terminating null character) of the second string.
+ * It returns null if either string is null.
+ */
+
+PR_EXTERN(char *)
+PL_strnpbrk(const char *s, const char *list, PRUint32 n);
+
+/*
+ * PL_strnprbrk
+ *
+ * Returns a pointer to the last instance (within the first n characters) of any
+ * character (not including the terminating null character) of the second string.
+ * It returns null if either string is null.
+ */
+
+PR_EXTERN(char *)
+PL_strnprbrk(const char *s, const char *list, PRUint32 n);
+
+/*
+ * PL_strstr
+ *
+ * Returns a pointer to the first instance of the little string within the
+ * big one. It returns null if either string is null.
+ */
+
+PR_EXTERN(char *)
+PL_strstr(const char *big, const char *little);
+
+/*
+ * PL_strrstr
+ *
+ * Returns a pointer to the last instance of the little string within the big one.
+ * It returns null if either string is null.
+ */
+
+PR_EXTERN(char *)
+PL_strrstr(const char *big, const char *little);
+
+/*
+ * PL_strnstr
+ *
+ * Returns a pointer to the first instance of the little string within the first
+ * n characters of the big one. It returns null if either string is null. It
+ * returns null if the length of the little string is greater than n.
+ */
+
+PR_EXTERN(char *)
+PL_strnstr(const char *big, const char *little, PRUint32 n);
+
+/*
+ * PL_strnrstr
+ *
+ * Returns a pointer to the last instance of the little string within the first
+ * n characters of the big one. It returns null if either string is null. It
+ * returns null if the length of the little string is greater than n.
+ */
+
+PR_EXTERN(char *)
+PL_strnrstr(const char *big, const char *little, PRUint32 max);
+
+/*
+ * PL_strcasestr
+ *
+ * Returns a pointer to the first instance of the little string within the big one,
+ * ignoring case. It returns null if either string is null.
+ */
+
+PR_EXTERN(char *)
+PL_strcasestr(const char *big, const char *little);
+
+/*
+ * PL_strcaserstr
+ *
+ * Returns a pointer to the last instance of the little string within the big one,
+ * ignoring case. It returns null if either string is null.
+ */
+
+PR_EXTERN(char *)
+PL_strcaserstr(const char *big, const char *little);
+
+/*
+ * PL_strncasestr
+ *
+ * Returns a pointer to the first instance of the listtle string within the first
+ * n characters of the big one, ignoring case. It returns null if either string is
+ * null. It returns null if the length of the little string is greater than n.
+ */
+
+PR_EXTERN(char *)
+PL_strncasestr(const char *big, const char *little, PRUint32 max);
+
+/*
+ * PL_strncaserstr
+ *
+ * Returns a pointer to the last instance of the little string within the first
+ * n characters of the big one, ignoring case. It returns null if either string is
+ * null. It returns null if the length of the little string is greater than n.
+ */
+
+PR_EXTERN(char *)
+PL_strncaserstr(const char *big, const char *little, PRUint32 max);
+
+/*
+ * Things not (yet?) included: strspn/strcspn, strtok/strtok_r, strsep.
+ * memchr, memcmp, memcpy, memccpy, index, rindex, bcmp, bcopy, bzero.
+ * Any and all i18n/l10n stuff.
+ */
+
+PR_END_EXTERN_C
+
+#endif /* _plstr_h */
diff --git a/lib/libc/src/Makefile b/lib/libc/src/Makefile
new file mode 100644
index 00000000..9c7fe4ea
--- /dev/null
+++ b/lib/libc/src/Makefile
@@ -0,0 +1,150 @@
+#
+# 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
+
+INCLUDES = -I$(DIST)/include
+
+CSRCS =\
+ plvrsion.c \
+ strlen.c \
+ strcpy.c \
+ strdup.c \
+ strcat.c \
+ strcmp.c \
+ strccmp.c \
+ strchr.c \
+ strpbrk.c \
+ strstr.c \
+ strcstr.c \
+ base64.c \
+ plerror.c \
+ plgetopt.c \
+ $(NULL)
+
+LIBRARY_NAME = plc
+LIBRARY_VERSION = $(MOD_VERSION)
+
+RELEASE_LIBS = $(TARGETS)
+
+ifeq ($(OS_ARCH),WINNT)
+ifeq (,$(filter-out WIN16 OS2,$(OS_TARGET)))
+EXTRA_LIBS = $(DIST)/lib/nspr$(MOD_VERSION).lib
+else
+DLLBASE=/BASE:0x30000000
+RES=$(OBJDIR)/plc.res
+RESNAME=$(MOD_DEPTH)/pr/src/nspr.rc
+EXTRA_LIBS = $(DIST)/lib/libnspr$(MOD_VERSION).lib
+
+ifdef MOZ_DEBUG
+ifdef GLOWCODE
+EXTRA_LIBS += $(GLOWDIR)/glowcode.lib
+endif
+endif
+
+endif
+else
+ifeq ($(OS_ARCH), AIX)
+ifeq ($(CLASSIC_NSPR),1)
+OS_LIBS += -lc
+else
+OS_LIBS += -lc_r
+endif
+endif
+ifeq ($(OS_ARCH)$(OS_RELEASE), AIX4.1)
+EXTRA_LIBS = -L$(DIST)/lib -lnspr$(MOD_VERSION)_shr
+else
+EXTRA_LIBS = -L$(DIST)/lib -lnspr$(MOD_VERSION)
+endif
+endif
+
+# On NCR and SCOOS, we can't link with extra libraries when
+# we build a shared library. If we do so, the linker doesn't
+# complain, but we would run into weird problems at run-time.
+# Therefore on these platforms, we link just the .o files.
+ifeq ($(OS_ARCH),NCR)
+EXTRA_LIBS =
+endif
+ifeq ($(OS_ARCH),SCOOS)
+EXTRA_LIBS =
+endif
+
+include $(MOD_DEPTH)/config/rules.mk
+
+#
+# Version information generation (begin)
+#
+ECHO = echo
+TINC = $(OBJDIR)/_pl_bld.h
+PROD = libplc$(MOD_VERSION).$(DLL_SUFFIX)
+NOW = $(MOD_DEPTH)/config/$(OBJDIR)/now
+SH_DATE = $(shell date)
+SH_NOW = $(shell $(NOW))
+
+ifeq ($(OS_ARCH), WINNT)
+ SUF = i64
+else
+ SUF = LL
+endif
+
+$(TINC):
+ @$(MAKE_OBJDIR)
+ @$(ECHO) '#define _BUILD_STRING "$(SH_DATE)"' > $(TINC)
+ @if test ! -z "$(SH_NOW)"; then \
+ $(ECHO) '#define _BUILD_TIME $(SH_NOW)$(SUF)' >> $(TINC); \
+ fi
+ @$(ECHO) '#define _PRODUCTION "$(PROD)"' >> $(TINC)
+
+
+$(OBJDIR)/plvrsion.$(OBJ_SUFFIX): $(TINC)
+ifeq ($(OS_ARCH), WINNT)
+ $(CC) -Fo$@ -c $(CFLAGS) -I$(OBJDIR) plvrsion.c
+else
+ $(CC) -o $@ -c $(CFLAGS) -I$(OBJDIR) plvrsion.c
+endif
+#
+# Version information generation (end)
+#
+
+#
+# The Client build wants the shared libraries in $(DIST)/bin,
+# so we also install them there.
+#
+
+export:: $(TARGETS)
+ $(INSTALL) -m 444 $(TARGETS) $(DIST)/lib
+ifdef SHARED_LIBRARY
+ $(INSTALL) -m 444 $(SHARED_LIBRARY) $(DIST)/bin
+endif
+ifeq ($(MOZ_BITS),16)
+ $(INSTALL) -m 444 $(TARGETS) $(MOZ_DIST)/lib
+ $(INSTALL) -m 444 $(TARGETS) $(MOZ_DIST)/bin
+endif
+
+install:: export
+
diff --git a/lib/libc/src/README b/lib/libc/src/README
new file mode 100644
index 00000000..74f24690
--- /dev/null
+++ b/lib/libc/src/README
@@ -0,0 +1,20 @@
+NSPR 2.0 libc functions
+-----------------------
+
+Last edited: AOF 04 March 1997
+
+This directory contains various libc-types of functions. All functions in
+this directory are platform independent, thread friendly (both safe and
+efficient). They are contributed from various sources, though the contri-
+butions are monitored by the NSPR group (mailto:freier).
+
+All API items exported by these functions will contain the same three
+character prefix, "PL_" (Portable Library). Internal function names
+that are not exported (static) are of little concern, though some caution
+must be used on those elements that are 'extern' but not really intended
+to be part of the API. Those should all have a prefix of "_PL_" (is that
+legal?).
+
+The responsibility for contributions in this area are distributed among
+all interested parties.
+
diff --git a/lib/libc/src/base64.c b/lib/libc/src/base64.c
new file mode 100644
index 00000000..4f22350f
--- /dev/null
+++ b/lib/libc/src/base64.c
@@ -0,0 +1,403 @@
+/* -*- 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 "plbase64.h"
+#include "prlog.h" /* For PR_NOT_REACHED */
+#include "prmem.h" /* for malloc / PR_MALLOC */
+#include "plstr.h" /* for PL_strlen */
+
+static unsigned char *base = (unsigned char *)"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static void
+encode3to4
+(
+ const unsigned char *src,
+ unsigned char *dest
+)
+{
+ PRUint32 b32 = (PRUint32)0;
+ PRIntn i, j = 18;
+
+ for( i = 0; i < 3; i++ )
+ {
+ b32 <<= 8;
+ b32 |= (PRUint32)src[i];
+ }
+
+ for( i = 0; i < 4; i++ )
+ {
+ dest[i] = base[ (PRUint32)((b32>>j) & 0x3F) ];
+ j -= 6;
+ }
+
+ return;
+}
+
+static void
+encode2to4
+(
+ const unsigned char *src,
+ unsigned char *dest
+)
+{
+ dest[0] = base[ (PRUint32)((src[0]>>2) & 0x3F) ];
+ dest[1] = base[ (PRUint32)(((src[0] & 0x03) << 4) | ((src[1] >> 4) & 0x0F)) ];
+ dest[2] = base[ (PRUint32)((src[1] & 0x0F) << 2) ];
+ dest[3] = (unsigned char)'=';
+ return;
+}
+
+static void
+encode1to4
+(
+ const unsigned char *src,
+ unsigned char *dest
+)
+{
+ dest[0] = base[ (PRUint32)((src[0]>>2) & 0x3F) ];
+ dest[1] = base[ (PRUint32)((src[0] & 0x03) << 4) ];
+ dest[2] = (unsigned char)'=';
+ dest[3] = (unsigned char)'=';
+ return;
+}
+
+static void
+encode
+(
+ const unsigned char *src,
+ PRUint32 srclen,
+ unsigned char *dest
+)
+{
+ while( srclen >= 3 )
+ {
+ encode3to4(src, dest);
+ src += 3;
+ dest += 4;
+ srclen -= 3;
+ }
+
+ switch( srclen )
+ {
+ case 2:
+ encode2to4(src, dest);
+ break;
+ case 1:
+ encode1to4(src, dest);
+ break;
+ case 0:
+ break;
+ default:
+ PR_NOT_REACHED("coding error");
+ }
+
+ return;
+}
+
+/*
+ * PL_Base64Encode
+ *
+ * If the destination argument is NULL, a return buffer is
+ * allocated, and the data therein will be null-terminated.
+ * If the destination argument is not NULL, it is assumed to
+ * be of sufficient size, and the contents will not be null-
+ * terminated by this routine.
+ *
+ * Returns null if the allocation fails.
+ */
+
+PR_IMPLEMENT(char *)
+PL_Base64Encode
+(
+ const char *src,
+ PRUint32 srclen,
+ char *dest
+)
+{
+ if( 0 == srclen )
+ {
+ srclen = PL_strlen(src);
+ }
+
+ if( (char *)0 == dest )
+ {
+ PRUint32 destlen = ((srclen + 2)/3) * 4;
+ dest = (char *)PR_MALLOC(destlen + 1);
+ if( (char *)0 == dest )
+ {
+ return (char *)0;
+ }
+ dest[ destlen ] = (char)0; /* null terminate */
+ }
+
+ encode((const unsigned char *)src, srclen, (unsigned char *)dest);
+ return dest;
+}
+
+static PRInt32
+codetovalue
+(
+ unsigned char c
+)
+{
+ if( (c >= (unsigned char)'A') && (c <= (unsigned char)'Z') )
+ {
+ return (PRInt32)(c - (unsigned char)'A');
+ }
+ else if( (c >= (unsigned char)'a') && (c <= (unsigned char)'z') )
+ {
+ return ((PRInt32)(c - (unsigned char)'a') +26);
+ }
+ else if( (c >= (unsigned char)'0') && (c <= (unsigned char)'9') )
+ {
+ return ((PRInt32)(c - (unsigned char)'0') +52);
+ }
+ else if( (unsigned char)'+' == c )
+ {
+ return (PRInt32)62;
+ }
+ else if( (unsigned char)'/' == c )
+ {
+ return (PRInt32)63;
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+static PRStatus
+decode4to3
+(
+ const unsigned char *src,
+ unsigned char *dest
+)
+{
+ PRUint32 b32 = (PRUint32)0;
+ PRInt32 bits;
+ PRIntn i;
+
+ for( i = 0; i < 4; i++ )
+ {
+ bits = codetovalue(src[i]);
+ if( bits < 0 )
+ {
+ return PR_FAILURE;
+ }
+
+ b32 <<= 6;
+ b32 |= bits;
+ }
+
+ dest[0] = (unsigned char)((b32 >> 16) & 0xFF);
+ dest[1] = (unsigned char)((b32 >> 8) & 0xFF);
+ dest[2] = (unsigned char)((b32 ) & 0xFF);
+
+ return PR_SUCCESS;
+}
+
+static PRStatus
+decode3to2
+(
+ const unsigned char *src,
+ unsigned char *dest
+)
+{
+ PRUint32 b32 = (PRUint32)0;
+ PRInt32 bits;
+ PRUint32 ubits;
+
+ bits = codetovalue(src[0]);
+ if( bits < 0 )
+ {
+ return PR_FAILURE;
+ }
+
+ b32 = (PRUint32)bits;
+ b32 <<= 6;
+
+ bits = codetovalue(src[1]);
+ if( bits < 0 )
+ {
+ return PR_FAILURE;
+ }
+
+ b32 |= (PRUint32)bits;
+ b32 <<= 4;
+
+ bits = codetovalue(src[2]);
+ if( bits < 0 )
+ {
+ return PR_FAILURE;
+ }
+
+ ubits = (PRUint32)bits;
+ b32 |= (ubits >> 2);
+
+ dest[0] = (unsigned char)((b32 >> 8) & 0xFF);
+ dest[1] = (unsigned char)((b32 ) & 0xFF);
+
+ return PR_SUCCESS;
+}
+
+static PRStatus
+decode2to1
+(
+ const unsigned char *src,
+ unsigned char *dest
+)
+{
+ PRUint32 b32;
+ PRUint32 ubits;
+ PRInt32 bits;
+
+ bits = codetovalue(src[0]);
+ if( bits < 0 )
+ {
+ return PR_FAILURE;
+ }
+
+ ubits = (PRUint32)bits;
+ b32 = (ubits << 2);
+
+ bits = codetovalue(src[1]);
+ if( bits < 0 )
+ {
+ return PR_FAILURE;
+ }
+
+ ubits = (PRUint32)bits;
+ b32 |= (ubits >> 4);
+
+ dest[0] = (unsigned char)b32;
+
+ return PR_SUCCESS;
+}
+
+static PRStatus
+decode
+(
+ const unsigned char *src,
+ PRUint32 srclen,
+ unsigned char *dest
+)
+{
+ PRStatus rv;
+
+ while( srclen >= 4 )
+ {
+ rv = decode4to3(src, dest);
+ if( PR_SUCCESS != rv )
+ {
+ return PR_FAILURE;
+ }
+
+ src += 4;
+ dest += 3;
+ srclen -= 4;
+ }
+
+ switch( srclen )
+ {
+ case 3:
+ rv = decode3to2(src, dest);
+ break;
+ case 2:
+ rv = decode2to1(src, dest);
+ break;
+ case 1:
+ rv = PR_FAILURE;
+ break;
+ case 0:
+ break;
+ default:
+ PR_NOT_REACHED("coding error");
+ }
+
+ return rv;
+}
+
+/*
+ * PL_Base64Decode
+ *
+ * If the destination argument is NULL, a return buffer is
+ * allocated and the data therein will be null-terminated.
+ * If the destination argument is not null, it is assumed
+ * to be of sufficient size, and the data will not be null-
+ * terminated by this routine.
+ *
+ * Returns null if the allocation fails, or if the source string is
+ * not well-formed.
+ */
+
+PR_IMPLEMENT(char *)
+PL_Base64Decode
+(
+ const char *src,
+ PRUint32 srclen,
+ char *dest
+)
+{
+ PRStatus status;
+ PRBool allocated = PR_FALSE;
+
+ if( 0 == srclen )
+ {
+ srclen = PL_strlen(src);
+ }
+
+ if( 0 == (srclen & 3) )
+ {
+ if( (char)'=' == src[ srclen-1 ] )
+ {
+ if( (char)'=' == src[ srclen-2 ] )
+ {
+ srclen -= 2;
+ }
+ else
+ {
+ srclen -= 1;
+ }
+ }
+ }
+
+ if( (char *)0 == dest )
+ {
+ PRUint32 destlen = ((srclen * 3) / 4);
+ dest = (char *)PR_MALLOC(destlen + 1);
+ if( (char *)0 == dest )
+ {
+ return (char *)0;
+ }
+ dest[ destlen ] = (char)0; /* null terminate */
+ allocated = PR_TRUE;
+ }
+
+ status = decode((const unsigned char *)src, srclen, (unsigned char *)dest);
+ if( PR_SUCCESS != status )
+ {
+ if( PR_TRUE == allocated )
+ {
+ PR_DELETE(dest);
+ }
+
+ return (char *)0;
+ }
+
+ return dest;
+}
diff --git a/lib/libc/src/plerror.c b/lib/libc/src/plerror.c
new file mode 100644
index 00000000..ce834675
--- /dev/null
+++ b/lib/libc/src/plerror.c
@@ -0,0 +1,149 @@
+/* -*- 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:plerror.c
+** Description: Simple routine to print translate the calling thread's
+** error numbers and print them to "syserr".
+*/
+
+#include "plerror.h"
+
+#include "prprf.h"
+#include "prerror.h"
+
+PR_IMPLEMENT(void) PL_FPrintError(PRFileDesc *fd, const char *msg)
+{
+static const char *tags[] =
+{
+ "PR_OUT_OF_MEMORY_ERROR",
+ "PR_BAD_DESCRIPTOR_ERROR",
+ "PR_WOULD_BLOCK_ERROR",
+ "PR_ACCESS_FAULT_ERROR",
+ "PR_INVALID_METHOD_ERROR",
+ "PR_ILLEGAL_ACCESS_ERROR",
+ "PR_UNKNOWN_ERROR",
+ "PR_PENDING_INTERRUPT_ERROR",
+ "PR_NOT_IMPLEMENTED_ERROR",
+ "PR_IO_ERROR",
+ "PR_IO_TIMEOUT_ERROR",
+ "PR_IO_PENDING_ERROR",
+ "PR_DIRECTORY_OPEN_ERROR",
+ "PR_INVALID_ARGUMENT_ERROR",
+ "PR_ADDRESS_NOT_AVAILABLE_ERROR",
+ "PR_ADDRESS_NOT_SUPPORTED_ERROR",
+ "PR_IS_CONNECTED_ERROR",
+ "PR_BAD_ADDRESS_ERROR",
+ "PR_ADDRESS_IN_USE_ERROR",
+ "PR_CONNECT_REFUSED_ERROR",
+ "PR_NETWORK_UNREACHABLE_ERROR",
+ "PR_CONNECT_TIMEOUT_ERROR",
+ "PR_NOT_CONNECTED_ERROR",
+ "PR_LOAD_LIBRARY_ERROR",
+ "PR_UNLOAD_LIBRARY_ERROR",
+ "PR_FIND_SYMBOL_ERROR",
+ "PR_INSUFFICIENT_RESOURCES_ERROR",
+ "PR_DIRECTORY_LOOKUP_ERROR",
+ "PR_TPD_RANGE_ERROR",
+ "PR_PROC_DESC_TABLE_FULL_ERROR",
+ "PR_SYS_DESC_TABLE_FULL_ERROR",
+ "PR_NOT_SOCKET_ERROR",
+ "PR_NOT_TCP_SOCKET_ERROR",
+ "PR_SOCKET_ADDRESS_IS_BOUND_ERROR",
+ "PR_NO_ACCESS_RIGHTS_ERROR",
+ "PR_OPERATION_NOT_SUPPORTED_ERROR",
+ "PR_PROTOCOL_NOT_SUPPORTED_ERROR",
+ "PR_REMOTE_FILE_ERROR",
+ "PR_BUFFER_OVERFLOW_ERROR",
+ "PR_CONNECT_RESET_ERROR",
+ "PR_RANGE_ERROR",
+ "PR_DEADLOCK_ERROR",
+ "PR_FILE_IS_LOCKED_ERROR",
+ "PR_FILE_TOO_BIG_ERROR",
+ "PR_NO_DEVICE_SPACE_ERROR",
+ "PR_PIPE_ERROR",
+ "PR_NO_SEEK_DEVICE_ERROR",
+ "PR_IS_DIRECTORY_ERROR",
+ "PR_LOOP_ERROR",
+ "PR_NAME_TOO_LONG_ERROR",
+ "PR_FILE_NOT_FOUND_ERROR",
+ "PR_NOT_DIRECTORY_ERROR",
+ "PR_READ_ONLY_FILESYSTEM_ERROR",
+ "PR_DIRECTORY_NOT_EMPTY_ERROR",
+ "PR_FILESYSTEM_MOUNTED_ERROR",
+ "PR_NOT_SAME_DEVICE_ERROR",
+ "PR_DIRECTORY_CORRUPTED_ERROR",
+ "PR_FILE_EXISTS_ERROR",
+ "PR_MAX_DIRECTORY_ENTRIES_ERROR",
+ "PR_INVALID_DEVICE_STATE_ERROR",
+ "PR_DEVICE_IS_LOCKED_ERROR",
+ "PR_NO_MORE_FILES_ERROR",
+ "PR_END_OF_FILE_ERROR",
+ "PR_FILE_SEEK_ERROR",
+ "PR_FILE_IS_BUSY_ERROR",
+ "<unused error code>",
+ "PR_IN_PROGRESS_ERROR",
+ "PR_ALREADY_INITIATED_ERROR",
+ "PR_GROUP_EMPTY_ERROR",
+ "PR_INVALID_STATE_ERROR",
+ "PR_NETWORK_DOWN_ERROR",
+ "PR_SOCKET_SHUTDOWN_ERROR",
+ "PR_CONNECT_ABORTED_ERROR",
+ "PR_HOST_UNREACHABLE_ERROR",
+ "PR_MAX_ERROR"
+};
+
+PRErrorCode error = PR_GetError();
+PRInt32 oserror = PR_GetOSError();
+PRIntn thoseIKnowAbout = sizeof(tags) / sizeof(char*);
+PRIntn lastError = PR_NSPR_ERROR_BASE + thoseIKnowAbout;
+
+ if (NULL != msg) PR_fprintf(fd, "%s: ", msg);
+ if ((error < PR_NSPR_ERROR_BASE) || (error > lastError))
+ PR_fprintf(
+ fd, " (%d)OUT OF RANGE, oserror = %d\n", error, oserror);
+ else
+ PR_fprintf(
+ fd, "%s(%d), oserror = %d\n",
+ tags[error - PR_NSPR_ERROR_BASE], error, oserror);
+} /* PL_FPrintError */
+
+PR_IMPLEMENT(void) PL_PrintError(const char *msg)
+{
+ static PRFileDesc *fd = NULL;
+ if (NULL == fd) fd = PR_GetSpecialFD(PR_StandardError);
+ PL_FPrintError(fd, msg);
+} /* PL_PrintError */
+
+#if defined(WIN16)
+/*
+** libmain() is a required function for win16
+**
+*/
+int CALLBACK LibMain( HINSTANCE hInst, WORD wDataSeg,
+ WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+return TRUE;
+}
+#endif /* WIN16 */
+
+
+
+
+
+/* plerror.c */
diff --git a/lib/libc/src/plgetopt.c b/lib/libc/src/plgetopt.c
new file mode 100644
index 00000000..c12387fc
--- /dev/null
+++ b/lib/libc/src/plgetopt.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.
+ */
+
+/*
+** File: plgetopt.c
+** Description: utilities to parse argc/argv
+*/
+
+#include "prmem.h"
+#include "prlog.h"
+#include "prerror.h"
+#include "plstr.h"
+#include "plgetopt.h"
+
+#include <string.h>
+
+static char static_Nul = 0;
+
+struct PLOptionInternal
+{
+ const char *options; /* client options list specification */
+ PRIntn argc; /* original number of arguments */
+ char **argv; /* vector of pointers to arguments */
+ PRIntn xargc; /* which one we're processing now */
+ const char *xargv; /* where within *argv[xargc] */
+ PRBool minus; /* do we already have the '-'? */
+};
+
+/*
+** Create the state in which to parse the tokens.
+**
+** argc the sum of the number of options and their values
+** argv the options and their values
+** options vector of single character options w/ | w/o ':
+*/
+PR_IMPLEMENT(PLOptState*) PL_CreateOptState(
+ PRIntn argc, char **argv, const char *options)
+{
+ PLOptState *opt = NULL;
+ if (NULL == options)
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ else
+ {
+ opt = PR_NEWZAP(PLOptState);
+ if (NULL == opt)
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ else
+ {
+ PLOptionInternal *internal = PR_NEW(PLOptionInternal);
+ if (NULL == internal)
+ {
+ PR_DELETE(opt);
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ }
+ else
+ {
+ opt->option = 0;
+ opt->value = NULL;
+ opt->internal = internal;
+
+ internal->argc = argc;
+ internal->argv = argv;
+ internal->xargc = 0;
+ internal->xargv = &static_Nul;
+ internal->minus = PR_FALSE;
+ internal->options = options;
+ }
+ }
+ }
+ return opt;
+} /* PL_CreateOptState */
+
+/*
+** Destroy object created by CreateOptState()
+*/
+PR_IMPLEMENT(void) PL_DestroyOptState(PLOptState *opt)
+{
+ PR_DELETE(opt->internal);
+ PR_DELETE(opt);
+} /* PL_DestroyOptState */
+
+PR_IMPLEMENT(PLOptStatus) PL_GetNextOpt(PLOptState *opt)
+{
+ PLOptionInternal *internal = opt->internal;
+ PRIntn cop, eoo = PL_strlen(internal->options);
+
+ /*
+ ** If the current xarg points to nul, advance to the next
+ ** element of the argv vector. If the vector index is equal
+ ** to argc, we're out of arguments, so return an EOL.
+ ** Note whether the first character of the new argument is
+ ** a '-' and skip by it if it is.
+ */
+ while (0 == *internal->xargv)
+ {
+ internal->xargc += 1;
+ if (internal->xargc >= internal->argc)
+ {
+ opt->option = 0;
+ opt->value = NULL;
+ return PL_OPT_EOL;
+ }
+ internal->xargv = internal->argv[internal->xargc];
+ internal->minus = ('-' == *internal->xargv ? PR_TRUE : PR_FALSE); /* not it */
+ if (internal->minus) internal->xargv += 1; /* and consume */
+ }
+
+ /*
+ ** If we already have a '-' in hand, xargv points to the next
+ ** option. See if we can find a match in the list of possible
+ ** options supplied.
+ */
+
+ if (internal->minus)
+ {
+ for (cop = 0; cop < eoo; ++cop)
+ {
+ if (internal->options[cop] == *internal->xargv)
+ {
+ opt->option = *internal->xargv;
+ internal->xargv += 1;
+ /*
+ ** if options indicates that there's an associated
+ ** value, this argv is finished and the next is the
+ ** option's value.
+ */
+ if (':' == internal->options[cop + 1])
+ {
+ PR_ASSERT(0 == *internal->xargv);
+ opt->value = internal->argv[++(internal->xargc)];
+ internal->xargv = &static_Nul;
+ internal->minus = PR_FALSE;
+ }
+ else opt->value = NULL;
+ return PL_OPT_OK;
+ }
+ }
+ internal->xargv += 1; /* consume that option */
+ return PL_OPT_BAD;
+ }
+ /*
+ ** No '-', so it must be a standalone value. The option is nul.
+ */
+ opt->value = internal->argv[(internal->xargc)++];
+ internal->xargv = &static_Nul;
+ opt->option = 0;
+ return PL_OPT_OK;
+} /* PL_GetNextOpt */
+
+/* plgetopt.c */
diff --git a/lib/libc/src/plvrsion.c b/lib/libc/src/plvrsion.c
new file mode 100644
index 00000000..2f803dcf
--- /dev/null
+++ b/lib/libc/src/plvrsion.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.
+ */
+
+#include "prinit.h"
+#include "prvrsion.h"
+
+/************************************************************************/
+/**************************IDENTITY AND VERSIONING***********************/
+/************************************************************************/
+#include "_pl_bld.h"
+#if !defined(_BUILD_TIME)
+#ifdef HAVE_LONG_LONG
+#define _BUILD_TIME 0
+#else
+#define _BUILD_TIME {0, 0}
+#endif
+#endif
+#if !defined(_BUILD_STRING)
+#define _BUILD_STRING ""
+#endif
+#if !defined(_PRODUCTION)
+#define _PRODUCTION ""
+#endif
+
+static PRVersionDescription prVersionDescription_libplc21 =
+{
+ /* version */ 2, /* this is the only one supported */
+ /* buildTime */ _BUILD_TIME, /* usecs since midnight 1/1/1970 GMT */
+ /* buildTimeString */ _BUILD_STRING, /* ditto, but human readable */
+ /* vMajor */ PR_VMAJOR, /* NSPR's version number */
+ /* vMinor */ PR_VMINOR, /* and minor version */
+ /* vPatch */ PR_VPATCH, /* and patch */
+ /* beta */ PR_BETA, /* beta build boolean */
+#if defined(DEBUG)
+ /* debug */ PR_TRUE, /* a debug build */
+#else
+ /* debug */ PR_FALSE, /* an optomized build */
+#endif
+ /* special */ PR_FALSE, /* they're all special, but ... */
+ /* filename */ _PRODUCTION, /* the produced library name */
+ /* description */ "Portable runtime", /* what we are */
+ /* security */ "N/A", /* not applicable here */
+ /* copywrite */ "Copyright (c) 1998 Netscape Communications Corporation. All Rights Reserved",
+ /* comment */ "http://www.mozilla.org/NPL/",
+ /* specialString */ ""
+};
+
+PR_IMPLEMENT(const PRVersionDescription*) libVersionPoint()
+{
+ return &prVersionDescription_libplc21;
+} /* versionEntryPointType */
+
+/* prvrsion.c */
+
diff --git a/lib/libc/src/strcat.c b/lib/libc/src/strcat.c
new file mode 100644
index 00000000..8cb0e7e0
--- /dev/null
+++ b/lib/libc/src/strcat.c
@@ -0,0 +1,70 @@
+/* -*- 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 "plstr.h"
+
+PR_IMPLEMENT(char *)
+PL_strcat(char *dest, const char *src)
+{
+ char *rv;
+
+ if( (char *)0 == dest ) return (char *)0;
+ if( (const char *)0 == src ) return dest;
+
+ for( rv = dest; *dest; dest++ )
+ ;
+
+ for( ; ((*dest = *src) != 0); dest++, src++ )
+ ;
+
+ return rv;
+}
+
+PR_IMPLEMENT(char *)
+PL_strncat(char *dest, const char *src, PRUint32 max)
+{
+ char *rv;
+
+ if( (char *)0 == dest ) return (char *)0;
+ if( (const char *)0 == src ) return dest;
+ if( 0 == max ) return dest;
+
+ for( rv = dest; *dest; dest++ )
+ ;
+
+ (void)PL_strncpy(dest, src, max);
+ return rv;
+}
+
+PR_IMPLEMENT(char *)
+PL_strcatn(char *dest, PRUint32 max, const char *src)
+{
+ char *rv;
+ PRUint32 dl;
+
+ if( (char *)0 == dest ) return (char *)0;
+ if( (const char *)0 == src ) return dest;
+
+ for( rv = dest, dl = 0; *dest; dest++, dl++ )
+ ;
+
+ if( max <= dl ) return rv;
+ (void)PL_strncpyz(dest, src, max-dl);
+
+ return rv;
+}
diff --git a/lib/libc/src/strccmp.c b/lib/libc/src/strccmp.c
new file mode 100644
index 00000000..a58a8c4d
--- /dev/null
+++ b/lib/libc/src/strccmp.c
@@ -0,0 +1,80 @@
+/* -*- 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 "plstr.h"
+
+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'
+};
+
+PR_IMPLEMENT(PRIntn)
+PL_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]);
+}
+
+PR_IMPLEMENT(PRIntn)
+PL_strncasecmp(const char *a, const char *b, PRUint32 max)
+{
+ 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( max && (uc[*ua] == uc[*ub]) && ('\0' != *a) )
+ {
+ a++;
+ ua++;
+ ub++;
+ max--;
+ }
+
+ if( 0 == max ) return (PRIntn)0;
+
+ return (PRIntn)(uc[*ua] - uc[*ub]);
+}
diff --git a/lib/libc/src/strchr.c b/lib/libc/src/strchr.c
new file mode 100644
index 00000000..0d31a793
--- /dev/null
+++ b/lib/libc/src/strchr.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 "plstr.h"
+
+PR_IMPLEMENT(char *)
+PL_strchr(const char *s, char c)
+{
+ if( (const char *)0 == s ) return (char *)0;
+
+ for( ; *s; s++ )
+ if( *s == c )
+ return (char *)s;
+
+ if( (char)0 == c ) return (char *)s;
+
+ return (char *)0;
+}
+
+PR_IMPLEMENT(char *)
+PL_strrchr(const char *s, char c)
+{
+ const char *p;
+
+ if( (const char *)0 == s ) return (char *)0;
+
+ for( p = s; *p; p++ )
+ ;
+
+ for( ; p >= s; p-- )
+ if( *p == c )
+ return (char *)p;
+
+ return (char *)0;
+}
+
+PR_IMPLEMENT(char *)
+PL_strnchr(const char *s, char c, PRUint32 n)
+{
+ if( (const char *)0 == s ) return (char *)0;
+
+ for( ; *s && n; s++, n-- )
+ if( *s == c )
+ return (char *)s;
+
+ if( ((char)0 == c) && ((char)0 == *s) && (n > 0)) return (char *)s;
+
+ return (char *)0;
+}
+
+PR_IMPLEMENT(char *)
+PL_strnrchr(const char *s, char c, PRUint32 n)
+{
+ const char *p;
+
+ if( (const char *)0 == s ) return (char *)0;
+
+ for( p = s; *p && n; p++, n-- )
+ ;
+
+ if( ((char)0 == c) && ((char)0 == *p) && (n > 0) ) return (char *)p;
+
+ for( p--; p >= s; p-- )
+ if( *p == c )
+ return (char *)p;
+
+ return (char *)0;
+}
diff --git a/lib/libc/src/strcmp.c b/lib/libc/src/strcmp.c
new file mode 100644
index 00000000..f0192497
--- /dev/null
+++ b/lib/libc/src/strcmp.c
@@ -0,0 +1,52 @@
+/* -*- 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 "plstr.h"
+
+PR_IMPLEMENT(PRIntn)
+PL_strcmp(const char *a, const char *b)
+{
+ if( ((const char *)0 == a) || (const char *)0 == b )
+ return (PRIntn)(a-b);
+
+ while( (*a == *b) && ('\0' != *a) )
+ {
+ a++;
+ b++;
+ }
+
+ return (PRIntn)(*((const unsigned char *)a) - *((const unsigned char *)b));
+}
+
+PR_IMPLEMENT(PRIntn)
+PL_strncmp(const char *a, const char *b, PRUint32 max)
+{
+ if( ((const char *)0 == a) || (const char *)0 == b )
+ return (PRIntn)(a-b);
+
+ while( max && (*a == *b) && ('\0' != *a) )
+ {
+ a++;
+ b++;
+ max--;
+ }
+
+ if( 0 == max ) return (PRIntn)0;
+
+ return (PRIntn)(*((const unsigned char *)a) - *((const unsigned char *)b));
+}
diff --git a/lib/libc/src/strcpy.c b/lib/libc/src/strcpy.c
new file mode 100644
index 00000000..f7f46d61
--- /dev/null
+++ b/lib/libc/src/strcpy.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.
+ */
+
+#include "plstr.h"
+
+PR_IMPLEMENT(char *)
+PL_strcpy(char *dest, const char *src)
+{
+ char *rv;
+
+ if( (char *)0 == dest ) return (char *)0;
+ if( (const char *)0 == src ) return (char *)0;
+
+ for( rv = dest; ((*dest = *src) != 0); dest++, src++ )
+ ;
+
+ return rv;
+}
+
+PR_IMPLEMENT(char *)
+PL_strncpy(char *dest, const char *src, PRUint32 max)
+{
+ char *rv;
+
+ if( (char *)0 == dest ) return (char *)0;
+ if( (const char *)0 == src ) return (char *)0;
+
+ for( rv = dest; max && ((*dest = *src) != 0); dest++, src++, max-- )
+ ;
+
+#ifdef JLRU
+ while( --max )
+ *++dest = '\0';
+#endif /* JLRU */
+
+ return rv;
+}
+
+PR_IMPLEMENT(char *)
+PL_strncpyz(char *dest, const char *src, PRUint32 max)
+{
+ char *rv;
+
+ if( (char *)0 == dest ) return (char *)0;
+ if( (const char *)0 == src ) return (char *)0;
+ if( 0 == max ) return (char *)0;
+
+ for( rv = dest, max--; max && ((*dest = *src) != 0); dest++, src++, max-- )
+ ;
+
+ *dest = '\0';
+
+ return rv;
+}
diff --git a/lib/libc/src/strcstr.c b/lib/libc/src/strcstr.c
new file mode 100644
index 00000000..0ac50aa1
--- /dev/null
+++ b/lib/libc/src/strcstr.c
@@ -0,0 +1,104 @@
+/* -*- 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 "plstr.h"
+
+PR_IMPLEMENT(char *)
+PL_strcasestr(const char *big, const char *little)
+{
+ PRUint32 ll;
+
+ if( ((const char *)0 == big) || ((const char *)0 == little) ) return (char *)0;
+ if( ((char)0 == *big) || ((char)0 == *little) ) return (char *)0;
+
+ ll = PL_strlen(little);
+
+ for( ; *big; big++ )
+ /* obvious improvement available here */
+ if( 0 == PL_strncasecmp(big, little, ll) )
+ return (char *)big;
+
+ return (char *)0;
+}
+
+PR_IMPLEMENT(char *)
+PL_strcaserstr(const char *big, const char *little)
+{
+ const char *p;
+ PRUint32 ll;
+
+ if( ((const char *)0 == big) || ((const char *)0 == little) ) return (char *)0;
+ if( ((char)0 == *big) || ((char)0 == *little) ) return (char *)0;
+
+ ll = PL_strlen(little);
+ p = &big[ PL_strlen(big) - ll ];
+ if( p < big ) return (char *)0;
+
+ for( ; p >= big; p-- )
+ /* obvious improvement available here */
+ if( 0 == PL_strncasecmp(p, little, ll) )
+ return (char *)p;
+
+ return (char *)0;
+}
+
+PR_IMPLEMENT(char *)
+PL_strncasestr(const char *big, const char *little, PRUint32 max)
+{
+ PRUint32 ll;
+
+ if( ((const char *)0 == big) || ((const char *)0 == little) ) return (char *)0;
+ if( ((char)0 == *big) || ((char)0 == *little) ) return (char *)0;
+
+ ll = PL_strlen(little);
+ if( ll > max ) return (char *)0;
+ max -= ll;
+ max++;
+
+ for( ; *big && max; big++, max-- )
+ /* obvious improvement available here */
+ if( 0 == PL_strncasecmp(big, little, ll) )
+ return (char *)big;
+
+ return (char *)0;
+}
+
+PR_IMPLEMENT(char *)
+PL_strncaserstr(const char *big, const char *little, PRUint32 max)
+{
+ const char *p;
+ PRUint32 ll;
+
+ if( ((const char *)0 == big) || ((const char *)0 == little) ) return (char *)0;
+ if( ((char)0 == *big) || ((char)0 == *little) ) return (char *)0;
+
+ ll = PL_strlen(little);
+
+ for( p = big; *p && max; p++, max-- )
+ ;
+
+ p -= ll;
+ if( p < big ) return (char *)0;
+
+ for( ; p >= big; p-- )
+ /* obvious improvement available here */
+ if( 0 == PL_strncasecmp(p, little, ll) )
+ return (char *)p;
+
+ return (char *)0;
+}
diff --git a/lib/libc/src/strdup.c b/lib/libc/src/strdup.c
new file mode 100644
index 00000000..a9941d51
--- /dev/null
+++ b/lib/libc/src/strdup.c
@@ -0,0 +1,64 @@
+/* -*- 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 "plstr.h"
+#include "prmem.h"
+
+PR_IMPLEMENT(char *)
+PL_strdup(const char *s)
+{
+ char *rv;
+ PRUint32 l;
+
+ l = PL_strlen(s);
+
+ rv = (char *)malloc(l+1);
+ if( (char *)0 == rv ) return rv;
+
+ if( (const char *)0 == s )
+ *rv = '\0';
+ else
+ (void)PL_strcpy(rv, s);
+
+ return rv;
+}
+
+PR_IMPLEMENT(void)
+PL_strfree(char *s)
+{
+ free(s);
+}
+
+PR_IMPLEMENT(char *)
+PL_strndup(const char *s, PRUint32 max)
+{
+ char *rv;
+ PRUint32 l;
+
+ l = PL_strnlen(s, max);
+
+ rv = (char *)malloc(l+1);
+ if( (char *)0 == rv ) return rv;
+
+ if( (const char *)0 == s )
+ *rv = '\0';
+ else
+ (void)PL_strncpyz(rv, s, l+1);
+
+ return rv;
+}
diff --git a/lib/libc/src/strlen.c b/lib/libc/src/strlen.c
new file mode 100644
index 00000000..0ee9e5cf
--- /dev/null
+++ b/lib/libc/src/strlen.c
@@ -0,0 +1,50 @@
+/* -*- 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 "plstr.h"
+#include "prtypes.h"
+#include "prlog.h"
+
+PR_IMPLEMENT(PRUint32)
+PL_strlen(const char *str)
+{
+ register const char *s;
+
+ if( (const char *)0 == str ) return 0;
+ for( s = str; *s; s++ )
+ ;
+/* error checking in case we have a 64-bit platform -- make sure we dont
+ * have ultra long strings that overflow a int32
+ */
+ if (sizeof(PRUint32) < sizeof(PRUptrdiff))
+ PR_ASSERT((s-str) < 2147483647);
+
+ return (PRUint32)(s - str);
+}
+
+PR_IMPLEMENT(PRUint32)
+PL_strnlen(const char *str, PRUint32 max)
+{
+ register const char *s;
+
+ if( (const char *)0 == str ) return 0;
+ for( s = str; *s && max; s++, max-- )
+ ;
+
+ return (PRUint32)(s - str);
+}
diff --git a/lib/libc/src/strpbrk.c b/lib/libc/src/strpbrk.c
new file mode 100644
index 00000000..0b4f35a5
--- /dev/null
+++ b/lib/libc/src/strpbrk.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 "plstr.h"
+
+PR_IMPLEMENT(char *)
+PL_strpbrk(const char *s, const char *list)
+{
+ const char *p;
+
+ if( ((const char *)0 == s) || ((const char *)0 == list) ) return (char *)0;
+
+ for( ; *s; s++ )
+ for( p = list; *p; p++ )
+ if( *s == *p )
+ return (char *)s;
+
+ return (char *)0;
+}
+
+PR_IMPLEMENT(char *)
+PL_strprbrk(const char *s, const char *list)
+{
+ const char *p;
+ const char *r;
+
+ if( ((const char *)0 == s) || ((const char *)0 == list) ) return (char *)0;
+
+ for( r = s; *r; r++ )
+ ;
+
+ for( r--; r >= s; r-- )
+ for( p = list; *p; p++ )
+ if( *r == *p )
+ return (char *)r;
+
+ return (char *)0;
+}
+
+PR_IMPLEMENT(char *)
+PL_strnpbrk(const char *s, const char *list, PRUint32 max)
+{
+ const char *p;
+
+ if( ((const char *)0 == s) || ((const char *)0 == list) ) return (char *)0;
+
+ for( ; *s && max; s++, max-- )
+ for( p = list; *p; p++ )
+ if( *s == *p )
+ return (char *)s;
+
+ return (char *)0;
+}
+
+PR_IMPLEMENT(char *)
+PL_strnprbrk(const char *s, const char *list, PRUint32 max)
+{
+ const char *p;
+ const char *r;
+
+ if( ((const char *)0 == s) || ((const char *)0 == list) ) return (char *)0;
+
+ for( r = s; *r && max; r++, max-- )
+ ;
+
+ for( r--; r >= s; r-- )
+ for( p = list; *p; p++ )
+ if( *r == *p )
+ return (char *)r;
+
+ return (char *)0;
+}
diff --git a/lib/libc/src/strstr.c b/lib/libc/src/strstr.c
new file mode 100644
index 00000000..6a9f0c5b
--- /dev/null
+++ b/lib/libc/src/strstr.c
@@ -0,0 +1,104 @@
+/* -*- 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 "plstr.h"
+
+PR_IMPLEMENT(char *)
+PL_strstr(const char *big, const char *little)
+{
+ PRUint32 ll;
+
+ if( ((const char *)0 == big) || ((const char *)0 == little) ) return (char *)0;
+ if( ((char)0 == *big) || ((char)0 == *little) ) return (char *)0;
+
+ ll = PL_strlen(little);
+
+ for( ; *big; big++ )
+ if( *little == *big )
+ if( 0 == PL_strncmp(big, little, ll) )
+ return (char *)big;
+
+ return (char *)0;
+}
+
+PR_IMPLEMENT(char *)
+PL_strrstr(const char *big, const char *little)
+{
+ const char *p;
+ PRUint32 ll;
+
+ if( ((const char *)0 == big) || ((const char *)0 == little) ) return (char *)0;
+ if( ((char)0 == *big) || ((char)0 == *little) ) return (char *)0;
+
+ ll = PL_strlen(little);
+ p = &big[ PL_strlen(big) - ll ];
+ if( p < big ) return (char *)0;
+
+ for( ; p >= big; p-- )
+ if( *little == *p )
+ if( 0 == PL_strncmp(p, little, ll) )
+ return (char *)p;
+
+ return (char *)0;
+}
+
+PR_IMPLEMENT(char *)
+PL_strnstr(const char *big, const char *little, PRUint32 max)
+{
+ PRUint32 ll;
+
+ if( ((const char *)0 == big) || ((const char *)0 == little) ) return (char *)0;
+ if( ((char)0 == *big) || ((char)0 == *little) ) return (char *)0;
+
+ ll = PL_strlen(little);
+ if( ll > max ) return (char *)0;
+ max -= ll;
+ max++;
+
+ for( ; *big && max; big++, max-- )
+ if( *little == *big )
+ if( 0 == PL_strncmp(big, little, ll) )
+ return (char *)big;
+
+ return (char *)0;
+}
+
+PR_IMPLEMENT(char *)
+PL_strnrstr(const char *big, const char *little, PRUint32 max)
+{
+ const char *p;
+ PRUint32 ll;
+
+ if( ((const char *)0 == big) || ((const char *)0 == little) ) return (char *)0;
+ if( ((char)0 == *big) || ((char)0 == *little) ) return (char *)0;
+
+ ll = PL_strlen(little);
+
+ for( p = big; *p && max; p++, max-- )
+ ;
+
+ p -= ll;
+ if( p < big ) return (char *)0;
+
+ for( ; p >= big; p-- )
+ if( *little == *p )
+ if( 0 == PL_strncmp(p, little, ll) )
+ return (char *)p;
+
+ return (char *)0;
+}
diff --git a/lib/msgc/Makefile b/lib/msgc/Makefile
new file mode 100644
index 00000000..7de30cd2
--- /dev/null
+++ b/lib/msgc/Makefile
@@ -0,0 +1,28 @@
+#
+# 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
+
+DIRS = include src tests
+
+include $(MOD_DEPTH)/config/rules.mk
+
+export:: $(TARGETS)
+
+install:: export
diff --git a/lib/msgc/include/MANIFEST b/lib/msgc/include/MANIFEST
new file mode 100644
index 00000000..a45ec20e
--- /dev/null
+++ b/lib/msgc/include/MANIFEST
@@ -0,0 +1,5 @@
+#
+# This is a list of local files which get copied to the mozilla:dist directory
+#
+
+prgc.h
diff --git a/lib/msgc/include/Makefile b/lib/msgc/include/Makefile
new file mode 100644
index 00000000..a9e781e8
--- /dev/null
+++ b/lib/msgc/include/Makefile
@@ -0,0 +1,37 @@
+#
+# 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
+
+EXPORT_HEADERS = prgc.h
+HEADERS = $(EXPORT_HEADERS) gcint.h
+
+RELEASE_HEADERS = $(EXPORT_HEADERS)
+RELEASE_HEADERS_DEST = $(RELEASE_INCLUDE_DIR)
+
+include $(MOD_DEPTH)/config/rules.mk
+
+export:: $(EXPORT_HEADERS)
+ $(INSTALL) -m 444 $(EXPORT_HEADERS) $(DIST)/include
+ifeq ($(MOZ_BITS),16)
+ $(INSTALL) -m 444 $(EXPORT_HEADERS) $(MOZ_INCL)
+endif
+
+install:: export
diff --git a/lib/msgc/include/gcint.h b/lib/msgc/include/gcint.h
new file mode 100644
index 00000000..3da3e443
--- /dev/null
+++ b/lib/msgc/include/gcint.h
@@ -0,0 +1,110 @@
+/* -*- 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 gcint_h___
+#define gcint_h___
+
+#include "prmon.h"
+#include "prgc.h"
+
+extern PRLogModuleInfo *_pr_msgc_lm;
+extern GCInfo _pr_gcData;
+
+#if defined(_WIN32) && !defined(DEBUG)
+#undef INLINE_LOCK
+#endif
+
+#ifdef INLINE_LOCK
+#define LOCK_GC() EnterCriticalSection(&_pr_gcData.lock->mutexHandle)
+#define UNLOCK_GC() LeaveCriticalSection(&_pr_gcData.lock->mutexHandle)
+#else
+#define LOCK_GC() PR_EnterMonitor(_pr_gcData.lock)
+#define UNLOCK_GC() PR_ExitMonitor (_pr_gcData.lock)
+#define GC_IS_LOCKED() (PR_GetMonitorEntryCount(_pr_gcData.lock)!=0)
+#endif
+
+#ifdef DEBUG
+#define _GCTRACE(x, y) if (_pr_gcData.flags & x) GCTrace y
+#else
+#define _GCTRACE(x, y)
+#endif
+
+extern GCBeginGCHook *_pr_beginGCHook;
+extern void *_pr_beginGCHookArg;
+extern GCBeginGCHook *_pr_endGCHook;
+extern void *_pr_endGCHookArg;
+
+extern GCBeginFinalizeHook *_pr_beginFinalizeHook;
+extern void *_pr_beginFinalizeHookArg;
+extern GCBeginFinalizeHook *_pr_endFinalizeHook;
+extern void *_pr_endFinalizeHookArg;
+
+extern int _pr_do_a_dump;
+extern FILE *_pr_dump_file;
+
+extern PRLogModuleInfo *_pr_gc_lm;
+
+/*
+** Root finders. Root finders are used by the GC to find pointers into
+** the GC heap that are not contained in the GC heap.
+*/
+typedef struct RootFinderStr RootFinder;
+
+struct RootFinderStr {
+ RootFinder *next;
+ GCRootFinder *func;
+ char *name;
+ void *arg;
+};
+extern RootFinder *_pr_rootFinders;
+
+typedef struct CollectorTypeStr {
+ GCType gctype;
+ PRUint32 flags;
+} CollectorType;
+
+#define GC_MAX_TYPES 256
+extern CollectorType *_pr_collectorTypes;
+
+#define _GC_TYPE_BUSY 0x1
+#define _GC_TYPE_FINAL 0x2
+#define _GC_TYPE_WEAK 0x4
+
+/* Slot in _pr_gcTypes used for free memory */
+#define FREE_MEMORY_TYPEIX 255
+
+extern void _PR_InitGC(PRWord flags);
+extern void _MD_InitGC(void);
+extern void PR_CALLBACK _PR_ScanFinalQueue(void *notused);
+
+/*
+** Grow the GC Heap.
+*/
+extern void *_MD_GrowGCHeap(PRUint32 *sizep);
+
+/*
+** Extend the GC Heap.
+*/
+extern PRBool _MD_ExtendGCHeap(char *base, PRInt32 oldSize, PRInt32 newSize);
+
+/*
+** Free a GC segment.
+*/
+extern void _MD_FreeGCSegment(void *base, PRInt32 len);
+
+#endif /* gcint_h___ */
diff --git a/lib/msgc/include/prgc.h b/lib/msgc/include/prgc.h
new file mode 100644
index 00000000..5c471a99
--- /dev/null
+++ b/lib/msgc/include/prgc.h
@@ -0,0 +1,400 @@
+/* -*- 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 prgc_h___
+#define prgc_h___
+
+/*
+** API to NSPR gc memory system.
+*/
+#include "prtypes.h"
+#include "prmon.h"
+#include "prthread.h"
+#include <stdio.h>
+
+#if defined(WIN16)
+#define GCPTR __far
+#else
+#define GCPTR
+#endif
+
+
+PR_BEGIN_EXTERN_C
+
+/*
+** Initialize the garbage collector.
+** "flags" is the trace flags (see below).
+** "initialHeapSize" is the initial size of the heap and may be zero
+** if the default is desired.
+** "segmentSize" is the size of each segment of memory added to the
+** heap when the heap is grown.
+*/
+PR_EXTERN(void) PR_InitGC(
+ PRWord flags, PRInt32 initialHeapSize, PRInt32 segmentSize, PRThreadScope scope);
+
+/*
+** Shuts down gc and frees up all memory associated with it.
+*/
+PR_EXTERN(void) PR_ShutdownGC(PRBool finalizeOnExit);
+
+/*
+** This walk function will be called for every gc object in the
+** heap as it is walked. If it returns non-zero, the walk is terminated.
+*/
+typedef PRInt32 (*PRWalkFun)(void GCPTR* obj, void* data);
+
+/*
+** GC Type record. This defines all of the GC operations used on a
+** particular object type. These structures are passed to
+** PR_RegisterType.
+*/
+typedef struct GCType {
+ /*
+ ** Scan an object that is in the GC heap and call GCInfo.livePointer
+ ** on all of the pointers in it. If this slot is null then the object
+ ** won't be scanned (i.e. it has no embedded pointers).
+ */
+ void (PR_CALLBACK *scan)(void GCPTR *obj);
+
+ /*
+ ** Finalize an object that has no references. This is called by the
+ ** GC after it has determined where the object debris is but before
+ ** it has moved the debris to the logical "free list". The object is
+ ** marked alive for this call and removed from the list of objects
+ ** that need finalization (finalization only happens once for an
+ ** object). If this slot is null then the object doesn't need
+ ** finalization.
+ */
+ void (PR_CALLBACK *finalize)(void GCPTR *obj);
+
+ /*
+ ** Dump out an object during a PR_DumpGCHeap(). This is used as a
+ ** debugging tool.
+ */
+ void (PR_CALLBACK *dump)(FILE *out, void GCPTR *obj, PRBool detailed, PRIntn indentLevel);
+
+ /*
+ ** Add object to summary table.
+ */
+ void (PR_CALLBACK *summarize)(void GCPTR *obj, PRUint32 bytes);
+
+ /*
+ ** Free hook called by GC when the object is being freed.
+ */
+ void (PR_CALLBACK *free)(void *obj);
+
+ /* Weak pointer support: If the object has a weak pointer (Note:
+ at most one), this function is used to get the weak link's
+ offset from the start of the body of a gc object */
+ PRUint32 (PR_CALLBACK *getWeakLinkOffset)(void *obj);
+
+ /* Descriptive character for dumping this GCType */
+ char kindChar;
+
+ /*
+ ** Walker routine. This routine should apply fun(obj->ptr, data)
+ ** for every gc pointer within the object.
+ */
+ PRInt32 (PR_CALLBACK *walk)(void GCPTR *obj, PRWalkFun fun, void* data);
+} GCType;
+
+/*
+** This data structure must be added as the hash table passed to
+** the summarize method of GCType.
+*/
+typedef struct PRSummaryEntry {
+ void* clazz;
+ PRInt32 instancesCount;
+ PRInt32 totalSize;
+} PRSummaryEntry;
+
+/*
+** This function pointer must be registered by users of nspr
+** to produce the finally summary after all object in the
+** heap have been visited.
+*/
+typedef void (PR_CALLBACK *PRSummaryPrinter)(FILE *out, void* closure);
+
+PR_EXTERN(void) PR_CALLBACK PR_RegisterSummaryPrinter(PRSummaryPrinter fun, void* closure);
+
+typedef void PR_CALLBACK GCRootFinder(void *arg);
+typedef void PR_CALLBACK GCBeginFinalizeHook(void *arg);
+typedef void PR_CALLBACK GCEndFinalizeHook(void *arg);
+typedef void PR_CALLBACK GCBeginGCHook(void *arg);
+typedef void PR_CALLBACK GCEndGCHook(void *arg);
+
+typedef enum { PR_GCBEGIN, PR_GCEND } GCLockHookArg;
+
+typedef void PR_CALLBACK GCLockHookFunc(GCLockHookArg arg1, void *arg2);
+
+typedef struct GCLockHook GCLockHook;
+
+struct GCLockHook {
+ GCLockHookFunc* func;
+ void* arg;
+ GCLockHook* next;
+ GCLockHook* prev;
+};
+
+
+/*
+** Hooks which are called at the beginning and end of the GC process.
+** The begin hooks are called before the root finding step. The hooks are
+** called with threading disabled, so it is now allowed to re-enter the
+** kernel. The end hooks are called after the gc has finished but before
+** the finalizer has run.
+*/
+PR_EXTERN(void) PR_CALLBACK PR_SetBeginGCHook(GCBeginGCHook *hook, void *arg);
+PR_EXTERN(void) PR_CALLBACK PR_GetBeginGCHook(GCBeginGCHook **hook, void **arg);
+PR_EXTERN(void) PR_CALLBACK PR_SetEndGCHook(GCBeginGCHook *hook, void *arg);
+PR_EXTERN(void) PR_CALLBACK PR_GetEndGCHook(GCEndGCHook **hook, void **arg);
+
+/*
+** Called before SuspendAll is called by dogc, so that GC thread can hold
+** all the locks before hand to avoid any deadlocks
+*/
+
+/*
+PR_EXTERN(void) PR_SetGCLockHook(GCLockHook *hook, void *arg);
+PR_EXTERN(void) PR_GetGCLockHook(GCLockHook **hook, void **arg);
+*/
+
+PR_EXTERN(int) PR_RegisterGCLockHook(GCLockHookFunc *hook, void *arg);
+
+/*
+** Hooks which are called at the beginning and end of the GC finalization
+** process. After the GC has identified all of the dead objects in the
+** heap, it looks for objects that need finalization. Before it calls the
+** first finalization proc (see the GCType structure above) it calls the
+** begin hook. When it has finalized the last object it calls the end
+** hook.
+*/
+PR_EXTERN(void) PR_SetBeginFinalizeHook(GCBeginFinalizeHook *hook, void *arg);
+PR_EXTERN(void) PR_GetBeginFinalizeHook(GCBeginFinalizeHook **hook, void **arg);
+PR_EXTERN(void) PR_SetEndFinalizeHook(GCBeginFinalizeHook *hook, void *arg);
+PR_EXTERN(void) PR_GetEndFinalizeHook(GCEndFinalizeHook **hook, void **arg);
+
+/*
+** Register a GC type. Return's the index into the GC internal type
+** table. The returned value is passed to PR_AllocMemory. After the call,
+** the "type" memory belongs to the GC (the caller must not free it or
+** change it).
+*/
+PR_EXTERN(PRInt32) PR_RegisterType(GCType *type);
+
+/*
+** Register a root finder with the collector. The collector will call
+** these functions to identify all of the roots before collection
+** proceeds. "arg" is passed to the function when it is called.
+*/
+PR_EXTERN(PRStatus) PR_RegisterRootFinder(GCRootFinder func, char *name, void *arg);
+
+/*
+** Allocate some GC'able memory. The object must be at least bytes in
+** size. The type index function for the object is specified. "flags"
+** specifies some control flags. If PR_ALLOC_CLEAN is set then the memory
+** is zero'd before being returned. If PR_ALLOC_DOUBLE is set then the
+** allocated memory is double aligned.
+**
+** Any memory cell that you store a pointer to something allocated by
+** this call must be findable by the GC. Use the PR_RegisterRootFinder to
+** register new places where the GC will look for pointers into the heap.
+** The GC already knows how to scan any NSPR threads or monitors.
+*/
+PR_EXTERN(PRWord GCPTR *)PR_AllocMemory(
+ PRWord bytes, PRInt32 typeIndex, PRWord flags);
+PR_EXTERN(PRWord GCPTR *)PR_AllocSimpleMemory(
+ PRWord bytes, PRInt32 typeIndex);
+
+/*
+** This function can be used to cause PR_AllocMemory to always return
+** NULL. This may be useful in low memory situations when we're trying to
+** shutdown applets.
+*/
+PR_EXTERN(void) PR_EnableAllocation(PRBool yesOrNo);
+
+/* flags bits */
+#define PR_ALLOC_CLEAN 0x1
+#define PR_ALLOC_DOUBLE 0x2
+#define PR_ALLOC_ZERO_HANDLE 0x4 /* XXX yes, it's a hack */
+
+/*
+** Force a garbage collection right now. Return when it completes.
+*/
+PR_EXTERN(void) PR_GC(void);
+
+/*
+** Force a finalization right now. Return when finalization has
+** completed. Finalization completes when there are no more objects
+** pending finalization. This does not mean there are no objects in the
+** gc heap that will need finalization should a collection be done after
+** this call.
+*/
+PR_EXTERN(void) PR_ForceFinalize(void);
+
+/*
+** Dump the GC heap out to the given file. This will stop the system dead
+** in its tracks while it is occuring.
+*/
+PR_EXTERN(void) PR_DumpGCHeap(FILE *out, PRBool detailed);
+
+/*
+** Wrapper for PR_DumpGCHeap
+*/
+PR_EXTERN(void) PR_DumpMemory(PRBool detailed);
+
+/*
+** Dump summary of objects allocated.
+*/
+PR_EXTERN(void) PR_DumpMemorySummary(void);
+
+/*
+** Dump the application heaps.
+*/
+PR_EXTERN(void) PR_DumpApplicationHeaps(void);
+
+/*
+** Helper function used by dump routines to do the indentation in a
+** consistent fashion.
+*/
+PR_EXTERN(void) PR_DumpIndent(FILE *out, PRIntn indent);
+
+/*
+** The GCInfo structure contains all of the GC state...
+**
+** busyMemory:
+** The amount of GC heap memory that is busy at this instant. Busy
+** doesn't mean alive, it just means that it has been
+** allocated. Immediately after a collection busy means how much is
+** alive.
+**
+** freeMemory:
+** The amount of GC heap memory that is as yet unallocated.
+**
+** allocMemory:
+** The sum of free and busy memory in the GC heap.
+**
+** maxMemory:
+** The maximum size that the GC heap is allowed to grow.
+**
+** lowSeg:
+** The lowest segment currently used in the GC heap.
+**
+** highSeg:
+** The highest segment currently used in the GC heap.
+** The lowSeg and highSeg members are used for a "quick test" of whether
+** a pointer falls within the GC heap. [ see GC_IN_HEAP(...) ]
+**
+** lock:
+** Monitor used for syncronization within the GC.
+**
+** finalizer:
+** Thread in which the GC finalizer is running.
+**
+** liveBlock:
+** Object scanning functions call through this function pointer to
+** register a potential block of pointers with the collector. (This is
+** currently not at all different than processRoot.)
+**
+** livePointer:
+** Object scanning functions call through this function pointer to
+** register a single pointer with the collector.
+**
+** processRootBlock:
+** When a root finder identifies a root it should call through this
+** function pointer so that the GC can process the root. The call takes
+** a base address and count which the gc will examine for valid heap
+** pointers.
+**
+** processRootPointer:
+** When a root finder identifies a root it should call through this
+** function pointer so that the GC can process the root. The call takes
+** a single pointer value.
+*/
+typedef struct GCInfoStr {
+ PRWord flags; /* trace flags (see below) */
+ PRWord busyMemory; /* memory in use right now */
+ PRWord freeMemory; /* memory free right now */
+ PRWord allocMemory; /* sum of busy & free memory */
+ PRWord maxMemory; /* max memory we are allowed to allocate */
+ PRWord *lowSeg; /* lowest segment in the GC heap */
+ PRWord *highSeg; /* higest segment in the GC heap */
+
+ PRMonitor *lock;
+ PRThread *finalizer;
+
+ void (PR_CALLBACK *liveBlock)(void **base, PRInt32 count);
+ void (PR_CALLBACK *livePointer)(void *ptr);
+ void (PR_CALLBACK *processRootBlock)(void **base, PRInt32 count);
+ void (PR_CALLBACK *processRootPointer)(void *ptr);
+ FILE* dumpOutput;
+#ifdef GCTIMINGHOOK
+ void (*gcTimingHook)(int32 gcTime);
+#endif
+} GCInfo;
+
+PR_EXTERN(GCInfo *) PR_GetGCInfo(void);
+PR_EXTERN(PRBool) PR_GC_In_Heap(void GCPTR *object);
+
+/*
+** Simple bounds check to see if a pointer is anywhere near the GC heap.
+** Used to avoid calls to PR_ProcessRoot and GCInfo.livePointer by object
+** scanning code.
+*/
+#if !defined(XP_PC) || defined(_WIN32)
+#define GC_IN_HEAP(_info, _p) (((PRWord*)(_p) >= (_info)->lowSeg) && \
+ ((PRWord*)(_p) < (_info)->highSeg))
+#else
+/*
+** The simple bounds check, above, doesn't work in Win16, because we don't
+** maintain: lowSeg == MIN(all segments) and highSeg == MAX(all segments).
+** So we have to do a little better.
+*/
+#define GC_IN_HEAP(_info, _p) PR_GC_In_Heap(_p)
+#endif
+
+PR_EXTERN(PRWord) PR_GetObjectHeader(void *ptr);
+
+PR_EXTERN(PRWord) PR_SetObjectHeader(void *ptr, PRWord newUserBits);
+
+/************************************************************************/
+
+/* Trace flags (passed to PR_InitGC or in environment GCLOG) */
+#define GC_TRACE 0x0001
+#define GC_ROOTS 0x0002
+#define GC_LIVE 0x0004
+#define GC_ALLOC 0x0008
+#define GC_MARK 0x0010
+#define GC_SWEEP 0x0020
+#define GC_DEBUG 0x0040
+#define GC_FINAL 0x0080
+
+#if defined(DEBUG_kipp) || defined(DEBUG_warren)
+#define GC_CHECK 0x0100
+#endif
+
+#ifdef DEBUG
+#define GCTRACE(x, y) if (PR_GetGCInfo()->flags & x) GCTrace y
+PR_EXTERN(void) GCTrace(char *fmt, ...);
+#else
+#define GCTRACE(x, y)
+#endif
+
+PR_END_EXTERN_C
+
+#endif /* prgc_h___ */
diff --git a/lib/msgc/src/Makefile b/lib/msgc/src/Makefile
new file mode 100644
index 00000000..6164d38d
--- /dev/null
+++ b/lib/msgc/src/Makefile
@@ -0,0 +1,116 @@
+#
+# 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
+
+INCLUDES = -I$(DIST)/include -I../include
+
+CSRCS = prgcapi.c prmsgc.c
+
+ifeq ($(OS_ARCH),WINNT)
+ifeq ($(OS_TARGET),WIN16)
+CSRCS += win16gc.c
+else
+ifeq ($(OS_TARGET),OS2)
+CSRCS += os2gc.c
+else
+CSRCS += win32gc.c
+endif
+endif
+else
+CSRCS += unixgc.c
+endif
+
+NSPR_VERSION = $(MOD_VERSION)
+
+ifeq ($(OS_ARCH), WINNT)
+ifeq (,$(filter-out WIN16 OS2,$(OS_TARGET)))
+EXTRA_LIBS = $(DIST)/lib/nspr$(NSPR_VERSION).lib
+else
+DLLBASE=/BASE:0x30000000
+#RES=$(OBJDIR)/ds.res
+#RESNAME=$(MOD_DEPTH)/pr/src/nspr.rc
+#OS_LIBS = user32.lib
+EXTRA_LIBS = $(DIST)/lib/libnspr$(NSPR_VERSION).lib
+
+ifdef MOZ_DEBUG
+ifdef GLOWCODE
+EXTRA_LIBS += $(GLOWDIR)/glowcode.lib
+endif
+endif
+
+endif
+else
+ifeq ($(OS_ARCH), AIX)
+ifeq ($(CLASSIC_NSPR),1)
+OS_LIBS += -lc
+else
+OS_LIBS += -lc_r
+endif
+endif
+ifeq ($(OS_ARCH)$(OS_RELEASE), AIX4.1)
+EXTRA_LIBS = -L$(DIST)/lib -lnspr$(NSPR_VERSION)_shr
+else
+EXTRA_LIBS = -L$(DIST)/lib -lnspr$(NSPR_VERSION)
+endif
+endif
+
+# On NCR and SCOOS, we can't link with extra libraries when
+# we build a shared library. If we do so, the linker doesn't
+# complain, but we would run into weird problems at run-time.
+# Therefore on these platforms, we link just the .o files.
+ifeq ($(OS_ARCH),NCR)
+EXTRA_LIBS =
+endif
+ifeq ($(OS_ARCH),SCOOS)
+EXTRA_LIBS =
+endif
+
+LIBRARY_NAME = msgc
+LIBRARY_VERSION = $(MOD_VERSION)
+
+RELEASE_LIBS = $(TARGETS)
+
+include $(MOD_DEPTH)/config/rules.mk
+
+#
+# The Client build wants the shared libraries in $(DIST)/bin,
+# so we also install them there.
+#
+
+export:: $(TARGETS)
+ $(INSTALL) -m 444 $(TARGETS) $(DIST)/lib
+ifdef SHARED_LIBRARY
+ $(INSTALL) -m 444 $(SHARED_LIBRARY) $(DIST)/bin
+endif
+ifeq ($(MOZ_BITS),16)
+ $(INSTALL) -m 444 $(TARGETS) $(MOZ_DIST)/lib
+ $(INSTALL) -m 444 $(TARGETS) $(MOZ_DIST)/bin
+endif
+
+install:: export
diff --git a/lib/msgc/src/macgc.c b/lib/msgc/src/macgc.c
new file mode 100644
index 00000000..cba61256
--- /dev/null
+++ b/lib/msgc/src/macgc.c
@@ -0,0 +1,56 @@
+/* -*- 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 "MacMemAllocator.h"
+
+void _MD_InitGC() {}
+
+void *_MD_GrowGCHeap(size_t *sizep)
+{
+ void *heapPtr = NULL;
+ size_t heapSize = *sizep;
+
+ // In previous versions of this code we tried to allocate GC heaps from the application
+ // heap. In the 4.0 application, we try to keep our app heap allications to a minimum
+ // and instead go through our own memory allocation routines.
+ heapPtr = malloc(heapSize);
+
+ if (heapPtr == NULL) {
+ FreeMemoryStats stats;
+
+ memtotal(heapSize, &stats); // How much can we allcoate?
+
+ if (stats.maxBlockSize < heapSize)
+ heapSize = stats.maxBlockSize;
+
+ heapPtr = malloc(heapSize);
+
+ if (heapPtr == NULL) // Now we're hurting
+ heapSize = 0;
+ }
+
+ *sizep = heapSize;
+ return heapPtr;
+}
+
+
+void _MD_FreeGCSegment(void *base, int32 /* len */)
+{
+ free(base);
+}
diff --git a/lib/msgc/src/os2gc.c b/lib/msgc/src/os2gc.c
new file mode 100644
index 00000000..6a748e20
--- /dev/null
+++ b/lib/msgc/src/os2gc.c
@@ -0,0 +1,64 @@
+/* -*- 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 "prlog.h"
+
+#include <stdlib.h>
+
+/* Leave a bit of room for any malloc header bytes... */
+#define MAX_SEGMENT_SIZE (65536L - 4096L)
+
+/************************************************************************/
+/*
+** Machine dependent GC Heap management routines:
+** _MD_GrowGCHeap
+*/
+/************************************************************************/
+void _MD_InitGC() {}
+
+void *_MD_GrowGCHeap(PRUint32 *sizep)
+{
+ void *addr;
+
+ if ( *sizep > MAX_SEGMENT_SIZE )
+ {
+ *sizep = MAX_SEGMENT_SIZE;
+ }
+
+ addr = malloc((size_t)*sizep);
+ return addr;
+}
+
+
+PRBool _MD_ExtendGCHeap(char *base, PRInt32 oldSize, PRInt32 newSize) {
+ /* Not sure about this. Todd? */
+ return PR_FALSE;
+}
+
+
+void _MD_FreeGCSegment(void *base, PRInt32 len)
+{
+ if (base)
+ {
+ free(base);
+ }
+}
diff --git a/lib/msgc/src/prgcapi.c b/lib/msgc/src/prgcapi.c
new file mode 100644
index 00000000..ba7c5b18
--- /dev/null
+++ b/lib/msgc/src/prgcapi.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.
+ */
+
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include "prenv.h"
+#include "prmem.h"
+#include "prmon.h"
+#include "prlog.h"
+#include "prthread.h"
+#if defined(XP_MAC)
+#include "pprthred.h"
+#else
+#include "private/pprthred.h"
+#endif
+#include "gcint.h"
+
+/*
+** Generic GC implementation independent code for the NSPR GC
+*/
+
+RootFinder *_pr_rootFinders;
+
+CollectorType *_pr_collectorTypes;
+
+/* GC State information */
+GCInfo _pr_gcData;
+
+GCBeginGCHook *_pr_beginGCHook;
+void *_pr_beginGCHookArg;
+GCBeginGCHook *_pr_endGCHook;
+void *_pr_endGCHookArg;
+
+GCBeginFinalizeHook *_pr_beginFinalizeHook;
+void *_pr_beginFinalizeHookArg;
+GCBeginFinalizeHook *_pr_endFinalizeHook;
+void *_pr_endFinalizeHookArg;
+
+FILE *_pr_dump_file;
+int _pr_do_a_dump;
+GCLockHook *_pr_GCLockHook;
+
+extern PRLogModuleInfo *_pr_msgc_lm;
+
+/************************************************************************/
+
+static PRStatus PR_CALLBACK
+pr_ScanOneThread(PRThread* t, void** addr, PRUword count, void* closure)
+{
+#if defined(XP_MAC)
+#pragma unused (t, closure)
+#endif
+
+ _pr_gcData.processRootBlock(addr, count);
+ return PR_SUCCESS;
+}
+
+/*
+** Scan all of the threads C stack's and registers, looking for "root"
+** pointers into the GC heap. These are the objects that the GC cannot
+** move and are considered "live" by the GC. Caller has stopped all of
+** the threads from running.
+*/
+static void PR_CALLBACK ScanThreads(void *arg)
+{
+ PR_ScanStackPointers(pr_ScanOneThread, arg);
+}
+
+/************************************************************************/
+
+PR_IMPLEMENT(GCInfo *) PR_GetGCInfo(void)
+{
+ return &_pr_gcData;
+}
+
+
+PR_IMPLEMENT(PRInt32) PR_RegisterType(GCType *t)
+{
+ CollectorType *ct, *ect;
+ int rv = -1;
+
+ LOCK_GC();
+ ct = &_pr_collectorTypes[0];
+ ect = &_pr_collectorTypes[FREE_MEMORY_TYPEIX];
+ for (; ct < ect; ct++) {
+ if (ct->flags == 0) {
+ ct->gctype = *t;
+ ct->flags = _GC_TYPE_BUSY;
+ if (0 != ct->gctype.finalize) {
+ ct->flags |= _GC_TYPE_FINAL;
+ }
+ if (0 != ct->gctype.getWeakLinkOffset) {
+ ct->flags |= _GC_TYPE_WEAK;
+ }
+ rv = ct - &_pr_collectorTypes[0];
+ break;
+ }
+ }
+ UNLOCK_GC();
+ return rv;
+}
+
+PR_IMPLEMENT(PRStatus) PR_RegisterRootFinder(
+ GCRootFinder f, char *name, void *arg)
+{
+ RootFinder *rf = PR_NEWZAP(RootFinder);
+ if (rf) {
+ rf->func = f;
+ rf->name = name;
+ rf->arg = arg;
+
+ LOCK_GC();
+ rf->next = _pr_rootFinders;
+ _pr_rootFinders = rf;
+ UNLOCK_GC();
+ return PR_SUCCESS;
+ }
+ return PR_FAILURE;
+}
+
+
+PR_IMPLEMENT(int) PR_RegisterGCLockHook(GCLockHookFunc* f, void *arg)
+{
+
+ GCLockHook *rf = 0;
+
+ rf = (GCLockHook*) calloc(1, sizeof(GCLockHook));
+ if (rf) {
+ rf->func = f;
+ rf->arg = arg;
+
+ LOCK_GC();
+ /* first dummy node */
+ if (! _pr_GCLockHook) {
+ _pr_GCLockHook = (GCLockHook*) calloc(1, sizeof(GCLockHook));
+ _pr_GCLockHook->next = _pr_GCLockHook;
+ _pr_GCLockHook->prev = _pr_GCLockHook;
+ }
+
+ rf->next = _pr_GCLockHook;
+ rf->prev = _pr_GCLockHook->prev;
+ _pr_GCLockHook->prev->next = rf;
+ _pr_GCLockHook->prev = rf;
+ UNLOCK_GC();
+ return 0;
+ }
+ return -1;
+}
+
+/*
+PR_IMPLEMENT(void) PR_SetGCLockHook(GCLockHook *hook, void *arg)
+{
+ LOCK_GC();
+ _pr_GCLockHook = hook;
+ _pr_GCLockHookArg2 = arg;
+ UNLOCK_GC();
+}
+
+PR_IMPLEMENT(void) PR_GetGCLockHook(GCLockHook **hook, void **arg)
+{
+ LOCK_GC();
+ *hook = _pr_GCLockHook;
+ *arg = _pr_GCLockHookArg2;
+ UNLOCK_GC();
+}
+*/
+
+
+PR_IMPLEMENT(void) PR_SetBeginGCHook(GCBeginGCHook *hook, void *arg)
+{
+ LOCK_GC();
+ _pr_beginGCHook = hook;
+ _pr_beginGCHookArg = arg;
+ UNLOCK_GC();
+}
+
+PR_IMPLEMENT(void) PR_GetBeginGCHook(GCBeginGCHook **hook, void **arg)
+{
+ LOCK_GC();
+ *hook = _pr_beginGCHook;
+ *arg = _pr_beginGCHookArg;
+ UNLOCK_GC();
+}
+
+PR_IMPLEMENT(void) PR_SetEndGCHook(GCEndGCHook *hook, void *arg)
+{
+ LOCK_GC();
+ _pr_endGCHook = hook;
+ _pr_endGCHookArg = arg;
+ UNLOCK_GC();
+}
+
+PR_IMPLEMENT(void) PR_GetEndGCHook(GCEndGCHook **hook, void **arg)
+{
+ LOCK_GC();
+ *hook = _pr_endGCHook;
+ *arg = _pr_endGCHookArg;
+ UNLOCK_GC();
+}
+
+PR_IMPLEMENT(void) PR_SetBeginFinalizeHook(GCBeginFinalizeHook *hook, void *arg)
+{
+ LOCK_GC();
+ _pr_beginFinalizeHook = hook;
+ _pr_beginFinalizeHookArg = arg;
+ UNLOCK_GC();
+}
+
+PR_IMPLEMENT(void) PR_GetBeginFinalizeHook(GCBeginFinalizeHook **hook,
+ void **arg)
+{
+ LOCK_GC();
+ *hook = _pr_beginFinalizeHook;
+ *arg = _pr_beginFinalizeHookArg;
+ UNLOCK_GC();
+}
+
+PR_IMPLEMENT(void) PR_SetEndFinalizeHook(GCEndFinalizeHook *hook, void *arg)
+{
+ LOCK_GC();
+ _pr_endFinalizeHook = hook;
+ _pr_endFinalizeHookArg = arg;
+ UNLOCK_GC();
+}
+
+PR_IMPLEMENT(void) PR_GetEndFinalizeHook(GCEndFinalizeHook **hook, void **arg)
+{
+ LOCK_GC();
+ *hook = _pr_endFinalizeHook;
+ *arg = _pr_endFinalizeHookArg;
+ UNLOCK_GC();
+}
+
+#ifdef DEBUG
+#include "prprf.h"
+
+#if defined(WIN16)
+static FILE *tracefile = 0;
+#endif
+
+PR_IMPLEMENT(void) GCTrace(char *fmt, ...)
+{
+ va_list ap;
+ char buf[400];
+
+ va_start(ap, fmt);
+ PR_vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+#if defined(WIN16)
+ if ( tracefile == 0 )
+ {
+ tracefile = fopen( "xxxGCtr", "w" );
+ }
+ fprintf(tracefile, "%s\n", buf );
+ fflush(tracefile);
+#else
+ PR_LOG(_pr_msgc_lm, PR_LOG_ALWAYS, ("%s", buf));
+#endif
+}
+#endif
+
+void _PR_InitGC(PRWord flags)
+{
+ static char firstTime = 1;
+
+ if (!firstTime) return;
+ firstTime = 0;
+
+ _MD_InitGC();
+
+ if (flags == 0) {
+ char *ev = PR_GetEnv("GCLOG");
+ if (ev && ev[0]) {
+ flags = atoi(ev);
+ }
+ }
+ _pr_gcData.flags = flags;
+
+ _pr_gcData.lock = PR_NewMonitor();
+
+ _pr_collectorTypes = (CollectorType*) PR_CALLOC(256 * sizeof(CollectorType));
+
+ PR_RegisterRootFinder(ScanThreads, "scan threads", 0);
+ PR_RegisterRootFinder(_PR_ScanFinalQueue, "scan final queue", 0);
+}
+
+extern void pr_FinalizeOnExit(void);
+
+#ifdef DEBUG
+#ifdef GC_STATS
+PR_PUBLIC_API(void) PR_PrintGCAllocStats(void);
+#endif
+#endif
+
+PR_IMPLEMENT(void)
+PR_ShutdownGC(PRBool finalizeOnExit)
+{
+ /* first finalize all the objects in the heap */
+ if (finalizeOnExit) {
+ pr_FinalizeOnExit();
+ }
+
+#ifdef DEBUG
+#ifdef GC_STATS
+ PR_PrintGCAllocStats();
+#endif /* GC_STATS */
+#endif /* DEBUG */
+
+ /* then the chance for any future allocations */
+
+ /* finally delete the gc heap */
+
+ /* write me */
+}
+
+/******************************************************************************/
diff --git a/lib/msgc/src/prmsgc.c b/lib/msgc/src/prmsgc.c
new file mode 100644
index 00000000..a707092d
--- /dev/null
+++ b/lib/msgc/src/prmsgc.c
@@ -0,0 +1,3495 @@
+/* -*- 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 <stddef.h>
+#include <stdarg.h>
+#include <time.h>
+
+#ifdef WIN32
+#include <windef.h>
+#include <winbase.h>
+#endif
+
+#include "prclist.h"
+#include "prbit.h"
+
+#include "prtypes.h"
+#include "prenv.h"
+#include "prgc.h"
+#include "prthread.h"
+#include "prlog.h"
+#include "prlong.h"
+#include "prinrval.h"
+#include "prprf.h"
+#include "gcint.h"
+
+#if defined(XP_MAC)
+#include "pprthred.h"
+#else
+#include "private/pprthred.h"
+#endif
+
+typedef void (*PRFileDumper)(FILE *out, PRBool detailed);
+
+PR_EXTERN(void)
+PR_DumpToFile(char* filename, char* msg, PRFileDumper dump, PRBool detailed);
+
+/*
+** Mark&sweep garbage collector. Supports objects that require
+** finalization, objects that can have a single weak link, and special
+** objects that require care during sweeping.
+*/
+
+PRLogModuleInfo *_pr_msgc_lm;
+PRLogModuleInfo* GC;
+
+static PRInt32 _pr_pageShift;
+static PRInt32 _pr_pageSize;
+
+#ifdef DEBUG
+#define GCMETER
+#endif
+#ifdef DEBUG_jwz
+# undef GCMETER
+#endif /* 1 */
+
+#ifdef GCMETER
+#define METER(x) x
+#else
+#define METER(x)
+#endif
+
+/*
+** Make this constant bigger to reduce the amount of recursion during
+** garbage collection.
+*/
+#define MAX_SCAN_Q 100L
+
+#if defined(XP_PC) && !defined(WIN32)
+#define MAX_SEGS 400L
+#define MAX_SEGMENT_SIZE (65536L - 4096L)
+#define SEGMENT_SIZE (65536L - 4096L)
+#define MAX_ALLOC_SIZE (65536L - 4096L)
+#else
+#define MAX_SEGS 400L
+#define MAX_SEGMENT_SIZE (2L * 256L * 1024L)
+#define SEGMENT_SIZE (1L * 256L * 1024L)
+#define MAX_ALLOC_SIZE (4L * 1024L * 1024L)
+#endif
+
+/*
+ * The highest value that can fit into a signed integer. This
+ * is used to prevent overflow of allocation size in alloc routines.
+ */
+
+#define MAX_INT ((1UL << (PR_BITS_PER_INT - 1)) - 1)
+
+/*
+ * On 32-bit machines, only 22 bits are used in the cibx integer to
+ * store size since 8 bits of the integer are used to store type, and
+ * of the remainder, 2 are user defined. Max allocation size = 2^22 -1
+ */
+
+#define MAX_ALLOC ( (1L << (PR_BYTES_PER_WORD_LOG2 + WORDS_BITS )) -1)
+
+/* The minimum percentage of free heap space after a collection. If
+ the amount of free space doesn't meet this criteria then we will
+ attempt to grow the heap */
+#ifdef XP_MAC
+#define MIN_FREE_THRESHOLD_AFTER_GC 10L
+#else
+#define MIN_FREE_THRESHOLD_AFTER_GC 20L
+#endif
+
+static PRInt32 segmentSize = SEGMENT_SIZE;
+
+static PRInt32 collectorCleanupNeeded;
+
+#ifdef GCMETER
+PRUint32 _pr_gcMeter;
+
+#define _GC_METER_STATS 0x01L
+#define _GC_METER_GROWTH 0x02L
+#define _GC_METER_FREE_LIST 0x04L
+#endif
+
+/************************************************************************/
+
+#define LINEAR_BIN_EXPONENT 5
+#define NUM_LINEAR_BINS ((PRUint32)1 << LINEAR_BIN_EXPONENT)
+#define FIRST_LOG_BIN (NUM_LINEAR_BINS - LINEAR_BIN_EXPONENT)
+
+/* Each free list bin holds a chunk of memory sized from
+ 2^n to (2^(n+1))-1 inclusive. */
+#define NUM_BINS (FIRST_LOG_BIN + 32)
+
+/*
+ * Find the bin number for a given size (in bytes). This does not round up as
+ * values from 2^n to (2^(n+1))-1 share the same bin.
+ */
+#define InlineBinNumber(_bin,_bytes) \
+{ \
+ PRUint32 _t, _n = (PRUint32) _bytes / 4; \
+ if (_n < NUM_LINEAR_BINS) { \
+ _bin = _n; \
+ } else { \
+ _bin = FIRST_LOG_BIN; \
+ if ((_t = (_n >> 16)) != 0) { _bin += 16; _n = _t; } \
+ if ((_t = (_n >> 8)) != 0) { _bin += 8; _n = _t; } \
+ if ((_t = (_n >> 4)) != 0) { _bin += 4; _n = _t; } \
+ if ((_t = (_n >> 2)) != 0) { _bin += 2; _n = _t; } \
+ if ((_n >> 1) != 0) _bin++; \
+ } \
+}
+
+#define BIG_ALLOC 16384L
+
+#define MIN_FREE_CHUNK_BYTES ((PRInt32)sizeof(GCFreeChunk))
+
+/* Note: fix code in PR_AllocMemory if you change the size of GCFreeChunk
+ so that it zeros the right number of words */
+typedef struct GCFreeChunk {
+ struct GCFreeChunk *next;
+ struct GCSeg *segment;
+ PRInt32 chunkSize;
+} GCFreeChunk;
+
+typedef struct GCSegInfo {
+ struct GCSegInfo *next;
+ char *base;
+ char *limit;
+ PRWord *hbits;
+ int fromMalloc;
+} GCSegInfo;
+
+typedef struct GCSeg {
+ char *base;
+ char *limit;
+ PRWord *hbits;
+ GCSegInfo *info;
+} GCSeg;
+
+#ifdef GCMETER
+typedef struct GCMeter {
+ PRInt32 allocBytes;
+ PRInt32 wastedBytes;
+ PRInt32 numFreeChunks;
+ PRInt32 skippedFreeChunks;
+} GCMeter;
+static GCMeter meter;
+#endif
+
+/*
+** There is one of these for each segment of GC'able memory.
+*/
+static GCSeg segs[MAX_SEGS];
+static GCSegInfo *freeSegs;
+static GCSeg* lastInHeap;
+static int nsegs;
+
+static GCFreeChunk *bins[NUM_BINS];
+static PRInt32 minBin;
+static PRInt32 maxBin;
+
+/*
+** Scan Q used to avoid deep recursion when scanning live objects for
+** heap pointers
+*/
+typedef struct GCScanQStr {
+ PRWord *q[MAX_SCAN_Q];
+ int queued;
+} GCScanQ;
+
+static GCScanQ *pScanQ;
+
+#ifdef GCMETER
+PRInt32 _pr_maxScanDepth;
+PRInt32 _pr_scanDepth;
+#endif
+
+/*
+** Keeps track of the number of bytes allocated via the BigAlloc()
+** allocator. When the number of bytes allocated, exceeds the
+** BIG_ALLOC_GC_SIZE, then a GC will occur before the next allocation
+** is done...
+*/
+#define BIG_ALLOC_GC_SIZE (4*SEGMENT_SIZE)
+static PRWord bigAllocBytes = 0;
+
+/*
+** There is one GC header word in front of each GC allocated object. We
+** use it to contain information about the object (what TYPEIX to use for
+** scanning it, how big it is, it's mark status, and if it's a root).
+*/
+#define TYPEIX_BITS 8L
+#define WORDS_BITS 20L
+#define MAX_CBS (1L << GC_TYPEIX_BITS)
+#define MAX_WORDS (1L << GC_WORDS_BITS)
+#define TYPEIX_SHIFT 24L
+#define MAX_TYPEIX ((1L << TYPEIX_BITS) - 1L)
+#define TYPEIX_MASK PR_BITMASK(TYPEIX_BITS)
+#define WORDS_SHIFT 2L
+#define WORDS_MASK PR_BITMASK(WORDS_BITS)
+#define MARK_BIT 1L
+#define FINAL_BIT 2L
+
+/* Two bits per object header are reserved for the user of the memory
+ system to store information into. */
+#define GC_USER_BITS_SHIFT 22L
+#define GC_USER_BITS 0x00c00000L
+
+#define MAKE_HEADER(_cbix,_words) \
+ ((PRWord) (((unsigned long)(_cbix) << TYPEIX_SHIFT) \
+ | ((unsigned long)(_words) << WORDS_SHIFT)))
+
+#define GET_TYPEIX(_h) \
+ (((PRUword)(_h) >> TYPEIX_SHIFT) & 0xff)
+
+#define MARK(_sp,_p) \
+ (((PRWord *)(_p))[0] |= MARK_BIT)
+#define IS_MARKED(_sp,_p) \
+ (((PRWord *)(_p))[0] & MARK_BIT)
+#define OBJ_BYTES(_h) \
+ (((PRInt32) (_h) & 0x003ffffcL) << (PR_BYTES_PER_WORD_LOG2-2L))
+
+#define GC_GET_USER_BITS(_h) (((_h) & GC_USER_BITS) >> GC_USER_BITS_SHIFT)
+
+/************************************************************************/
+
+/*
+** Mark the start of an object in a segment. Note that we mark the header
+** word (which we always have), not the data word (which we may not have
+** for empty objects).
+** XXX tune: put subtract of _sp->base into _sp->hbits pointer?
+*/
+#if !defined(WIN16)
+#define SET_HBIT(_sp,_ph) \
+ SET_BIT((_sp)->hbits, (((PRWord*)(_ph)) - ((PRWord*) (_sp)->base)))
+
+#define CLEAR_HBIT(_sp,_ph) \
+ CLEAR_BIT((_sp)->hbits, (((PRWord*)(_ph)) - ((PRWord*) (_sp)->base)))
+
+#define IS_HBIT(_sp,_ph) \
+ TEST_BIT((_sp)->hbits, (((PRWord*)(_ph)) - ((PRWord*) (_sp)->base)))
+#else
+
+#define SET_HBIT(_sp,_ph) set_hbit(_sp,_ph)
+
+#define CLEAR_HBIT(_sp,_ph) clear_hbit(_sp,_ph)
+
+#define IS_HBIT(_sp,_ph) is_hbit(_sp,_ph)
+
+static void
+set_hbit(GCSeg *sp, PRWord *p)
+{
+ unsigned int distance;
+ unsigned int index;
+ PRWord mask;
+
+ PR_ASSERT( SELECTOROF(p) == SELECTOROF(sp->base) );
+ PR_ASSERT( OFFSETOF(p) >= OFFSETOF(sp->base) );
+
+ distance = (OFFSETOF(p) - OFFSETOF(sp->base)) >> 2;
+ index = distance >> PR_BITS_PER_WORD_LOG2;
+ mask = 1L << (distance&(PR_BITS_PER_WORD-1));
+
+ sp->hbits[index] |= mask;
+}
+
+static void
+clear_hbit(GCSeg *sp, PRWord *p)
+{
+ unsigned int distance;
+ unsigned int index;
+ PRWord mask;
+
+ PR_ASSERT( SELECTOROF(p) == SELECTOROF(sp->base) );
+ PR_ASSERT( OFFSETOF(p) >= OFFSETOF(sp->base) );
+
+ distance = (OFFSETOF(p) - OFFSETOF(sp->base)) >> 2;
+ index = distance >> PR_BITS_PER_WORD_LOG2;
+ mask = 1L << (distance&(PR_BITS_PER_WORD-1));
+
+ sp->hbits[index] &= ~mask;
+}
+
+static int
+is_hbit(GCSeg *sp, PRWord *p)
+{
+ unsigned int distance;
+ unsigned int index;
+ PRWord mask;
+
+ PR_ASSERT( SELECTOROF(p) == SELECTOROF(sp->base) );
+ PR_ASSERT( OFFSETOF(p) >= OFFSETOF(sp->base) );
+
+ distance = (OFFSETOF(p) - OFFSETOF(sp->base)) >> 2;
+ index = distance >> PR_BITS_PER_WORD_LOG2;
+ mask = 1L << (distance&(PR_BITS_PER_WORD-1));
+
+ return ((sp->hbits[index] & mask) != 0);
+}
+
+
+#endif /* WIN16 */
+
+/*
+** Given a pointer into this segment, back it up until we are at the
+** start of the object the pointer points into. Each heap segment has a
+** bitmap that has one bit for each word of the objects it contains. The
+** bit's are set for the firstword of an object, and clear for it's other
+** words.
+*/
+static PRWord *FindObject(GCSeg *sp, PRWord *p)
+{
+ PRWord *base;
+
+ /* Align p to it's proper boundary before we start fiddling with it */
+ p = (PRWord*) ((PRWord)p & ~(PR_BYTES_PER_WORD-1L));
+
+ base = (PRWord *) sp->base;
+#if defined(WIN16)
+ PR_ASSERT( SELECTOROF(p) == SELECTOROF(base));
+#endif
+ do {
+ if (IS_HBIT(sp, p)) {
+ return (p);
+ }
+ p--;
+ } while ( p >= base );
+
+ /* Heap is corrupted! */
+ _GCTRACE(GC_TRACE, ("ERROR: The heap is corrupted!!! aborting now!"));
+ abort();
+ return NULL;
+}
+
+/************************************************************************/
+#if !defined(XP_PC) || defined(XP_OS2)
+#define OutputDebugString(msg)
+#endif
+
+#if !defined(WIN16)
+#define IN_SEGMENT(_sp, _p) \
+ ((((char *)(_p)) >= (_sp)->base) && \
+ (((char *)(_p)) < (_sp)->limit))
+#else
+#define IN_SEGMENT(_sp, _p) \
+ ((((PRWord)(_p)) >= ((PRWord)(_sp)->base)) && \
+ (((PRWord)(_p)) < ((PRWord)(_sp)->limit)))
+#endif
+
+static GCSeg *InHeap(void *p)
+{
+ GCSeg *sp, *esp;
+
+ if (lastInHeap && IN_SEGMENT(lastInHeap, p)) {
+ return lastInHeap;
+ }
+
+ sp = segs;
+ esp = segs + nsegs;
+ for (; sp < esp; sp++) {
+ if (IN_SEGMENT(sp, p)) {
+ lastInHeap = sp;
+ return sp;
+ }
+ }
+ return 0;
+}
+
+/*
+** Grow the heap by allocating another segment. Fudge the requestedSize
+** value to try to pre-account for the HBITS.
+*/
+static GCSeg* DoGrowHeap(PRInt32 requestedSize, PRBool exactly)
+{
+ GCSeg *sp;
+ GCSegInfo *segInfo;
+ GCFreeChunk *cp;
+ char *base;
+ PRWord *hbits;
+ PRInt32 nhbytes, nhbits;
+ PRUint32 allocSize;
+
+ if (nsegs == MAX_SEGS) {
+ /* No room for more segments */
+ return 0;
+ }
+
+ segInfo = (GCSegInfo*) PR_MALLOC(sizeof(GCSegInfo));
+#ifdef DEBUG
+ {
+ char str[256];
+ sprintf(str, "[1] Allocated %ld bytes at %p\n",
+ (long) sizeof(GCSegInfo), segInfo);
+ OutputDebugString(str);
+ }
+#endif
+ if (!segInfo) {
+ return 0;
+ }
+
+#if defined(WIN16)
+ if (requestedSize > segmentSize) {
+ PR_DELETE(segInfo);
+ return 0;
+ }
+#endif
+
+ /* Get more memory from the OS */
+ if (exactly) {
+ allocSize = requestedSize;
+ base = (char *) PR_MALLOC(requestedSize);
+ } else {
+ allocSize = requestedSize;
+ allocSize = (allocSize + _pr_pageSize - 1L) >> _pr_pageShift;
+ allocSize <<= _pr_pageShift;
+ base = (char*)_MD_GrowGCHeap(&allocSize);
+ }
+ if (!base) {
+ PR_DELETE(segInfo);
+ return 0;
+ }
+
+ nhbits = (PRInt32)(
+ (allocSize + PR_BYTES_PER_WORD - 1L) >> PR_BYTES_PER_WORD_LOG2);
+ nhbytes = ((nhbits + PR_BITS_PER_WORD - 1L) >> PR_BITS_PER_WORD_LOG2)
+ * sizeof(PRWord);
+
+ /* Get bitmap memory from malloc heap */
+#if defined(WIN16)
+ PR_ASSERT( nhbytes < MAX_ALLOC_SIZE );
+#endif
+ hbits = (PRWord *) PR_CALLOC((PRUint32)nhbytes);
+ if (!hbits) {
+ /* Loser! */
+ PR_DELETE(segInfo);
+ if (exactly) {
+ PR_DELETE(base);
+ } else {
+ /* XXX do something about this */
+ /* _MD_FreeGCSegment(base, allocSize); */
+ }
+ return 0;
+ }
+
+ /*
+ ** Setup new segment.
+ */
+ sp = &segs[nsegs++];
+ segInfo->base = sp->base = base;
+ segInfo->limit = sp->limit = base + allocSize;
+ segInfo->hbits = sp->hbits = hbits;
+ sp->info = segInfo;
+ segInfo->fromMalloc = exactly;
+ memset(base, 0, allocSize);
+
+#ifdef GCMETER
+ if (_pr_gcMeter & _GC_METER_GROWTH) {
+ fprintf(stderr, "[GC: new segment base=%p size=%ld]\n",
+ sp->base, (long) allocSize);
+ }
+#endif
+
+ _pr_gcData.allocMemory += allocSize;
+ _pr_gcData.freeMemory += allocSize;
+
+ if (!exactly) {
+ PRInt32 bin;
+
+ /* Put free memory into a freelist bin */
+ cp = (GCFreeChunk *) base;
+ cp->segment = sp;
+ cp->chunkSize = allocSize;
+ InlineBinNumber(bin, allocSize)
+ cp->next = bins[bin];
+ bins[bin] = cp;
+ if (bin < minBin) minBin = bin;
+ if (bin > maxBin) maxBin = bin;
+ } else {
+ /*
+ ** When exactly allocating the entire segment is given over to a
+ ** single object to prevent fragmentation
+ */
+ }
+
+ if (!_pr_gcData.lowSeg) {
+ _pr_gcData.lowSeg = (PRWord*) sp->base;
+ _pr_gcData.highSeg = (PRWord*) sp->limit;
+ } else {
+ if ((PRWord*)sp->base < _pr_gcData.lowSeg) {
+ _pr_gcData.lowSeg = (PRWord*) sp->base;
+ }
+ if ((PRWord*)sp->limit > _pr_gcData.highSeg) {
+ _pr_gcData.highSeg = (PRWord*) sp->limit;
+ }
+ }
+
+ /*
+ ** Get rid of the GC pointer in case it shows up in some uninitialized
+ ** local stack variable later (while scanning the C stack looking for
+ ** roots).
+ */
+ memset(&base, 0, sizeof(base)); /* optimizers beware */
+
+ PR_LOG(_pr_msgc_lm, PR_LOG_WARNING, ("grow heap: total gc memory now %d",
+ _pr_gcData.allocMemory));
+
+ return sp;
+}
+
+#ifdef USE_EXTEND_HEAP
+static PRBool ExtendHeap(PRInt32 requestedSize) {
+ GCSeg* sp;
+ PRUint32 allocSize;
+ PRInt32 oldSize, newSize;
+ PRInt32 newHBits, newHBytes;
+ PRInt32 oldHBits, oldHBytes;
+ PRWord* hbits;
+ GCFreeChunk* cp;
+ PRInt32 bin;
+
+ /* Can't extend nothing */
+ if (nsegs == 0) return PR_FALSE;
+
+ /* Round up requested size to the size of a page */
+ allocSize = (PRUint32) requestedSize;
+ allocSize = (allocSize + _pr_pageSize - 1L) >> _pr_pageShift;
+ allocSize <<= _pr_pageShift;
+
+ /* Malloc some memory for the new hbits array */
+ sp = segs;
+ oldSize = sp->limit - sp->base;
+ newSize = oldSize + allocSize;
+ newHBits = (newSize + PR_BYTES_PER_WORD - 1L) >> PR_BYTES_PER_WORD_LOG2;
+ newHBytes = ((newHBits + PR_BITS_PER_WORD - 1L) >> PR_BITS_PER_WORD_LOG2)
+ * sizeof(PRWord);
+ hbits = (PRWord*) PR_MALLOC(newHBytes);
+ if (0 == hbits) return PR_FALSE;
+
+ /* Attempt to extend the last segment by the desired amount */
+ if (_MD_ExtendGCHeap(sp->base, oldSize, newSize)) {
+ oldHBits = (oldSize + PR_BYTES_PER_WORD - 1L) >> PR_BYTES_PER_WORD_LOG2;
+ oldHBytes = ((oldHBits + PR_BITS_PER_WORD - 1L) >> PR_BITS_PER_WORD_LOG2)
+ * sizeof(PRWord);
+
+ /* Copy hbits from old memory into new memory */
+ memset(hbits, 0, newHBytes);
+ memcpy(hbits, sp->hbits, oldHBytes);
+ PR_DELETE(sp->hbits);
+ memset(sp->base + oldSize, 0, allocSize);
+
+ /* Adjust segment state */
+ sp->limit += allocSize;
+ sp->hbits = hbits;
+ sp->info->limit = sp->limit;
+ sp->info->hbits = hbits;
+
+ /* Put free memory into a freelist bin */
+ cp = (GCFreeChunk *) (sp->base + oldSize);
+ cp->segment = sp;
+ cp->chunkSize = allocSize;
+ InlineBinNumber(bin, allocSize)
+ cp->next = bins[bin];
+ bins[bin] = cp;
+ if (bin < minBin) minBin = bin;
+ if (bin > maxBin) maxBin = bin;
+
+ /* Prevent a pointer that points to the free memory from showing
+ up on the call stack later on */
+ memset(&cp, 0, sizeof(cp));
+
+ /* Update heap brackets and counters */
+ if ((PRWord*)sp->limit > _pr_gcData.highSeg) {
+ _pr_gcData.highSeg = (PRWord*) sp->limit;
+ }
+ _pr_gcData.allocMemory += allocSize;
+ _pr_gcData.freeMemory += allocSize;
+
+ return PR_TRUE;
+ }
+ PR_DELETE(hbits);
+ return PR_FALSE;
+}
+#endif /* USE_EXTEND_HEAP */
+
+static GCSeg *GrowHeapExactly(PRInt32 requestedSize)
+{
+ GCSeg *sp = DoGrowHeap(requestedSize, PR_TRUE);
+ return sp;
+}
+
+static PRBool GrowHeap(PRInt32 requestedSize)
+{
+ void *p;
+#ifdef USE_EXTEND_HEAP
+ if (ExtendHeap(requestedSize)) {
+ return PR_TRUE;
+ }
+#endif
+ p = DoGrowHeap(requestedSize, PR_FALSE);
+ return (p != NULL ? PR_TRUE : PR_FALSE);
+}
+
+/*
+** Release a segment when it is entirely free.
+*/
+static void ShrinkGCHeap(GCSeg *sp)
+{
+#ifdef GCMETER
+ if (_pr_gcMeter & _GC_METER_GROWTH) {
+ fprintf(stderr, "[GC: free segment base=%p size=%ld]\n",
+ sp->base, (long) (sp->limit - sp->base));
+ }
+#endif
+
+ /*
+ * Put segment onto free seginfo list (we can't call free right now
+ * because we have the GC lock and all of the other threads are
+ * suspended; if one of them has the malloc lock we would deadlock)
+ */
+ sp->info->next = freeSegs;
+ freeSegs = sp->info;
+ collectorCleanupNeeded = 1;
+ _pr_gcData.allocMemory -= sp->limit - sp->base;
+ if (sp == lastInHeap) lastInHeap = 0;
+
+ /* Squish out disappearing segment from segment table */
+ --nsegs;
+ if ((sp - segs) != nsegs) {
+ *sp = segs[nsegs];
+ } else {
+ sp->base = 0;
+ sp->limit = 0;
+ sp->hbits = 0;
+ sp->info = 0;
+ }
+
+ /* Recalculate the lowSeg and highSeg values */
+ _pr_gcData.lowSeg = (PRWord*) segs[0].base;
+ _pr_gcData.highSeg = (PRWord*) segs[0].limit;
+ for (sp = segs; sp < &segs[nsegs]; sp++) {
+ if ((PRWord*)sp->base < _pr_gcData.lowSeg) {
+ _pr_gcData.lowSeg = (PRWord*) sp->base;
+ }
+ if ((PRWord*)sp->limit > _pr_gcData.highSeg) {
+ _pr_gcData.highSeg = (PRWord*) sp->limit;
+ }
+ }
+}
+
+static void FreeSegments(void)
+{
+ GCSegInfo *si;
+
+ while (0 != freeSegs) {
+ LOCK_GC();
+ si = freeSegs;
+ if (si) {
+ freeSegs = si->next;
+ }
+ UNLOCK_GC();
+
+ if (!si) {
+ break;
+ }
+ PR_DELETE(si->base);
+ PR_DELETE(si->hbits);
+ PR_DELETE(si);
+ }
+}
+
+/************************************************************************/
+
+void ScanScanQ(GCScanQ *iscan)
+{
+ PRWord *p;
+ PRWord **pp;
+ PRWord **epp;
+ GCScanQ nextQ, *scan, *next, *temp;
+ CollectorType *ct;
+
+ if (!iscan->queued) return;
+
+ _GCTRACE(GC_MARK, ("begin scanQ @ 0x%x (%d)", iscan, iscan->queued));
+ scan = iscan;
+ next = &nextQ;
+ while (scan->queued) {
+ _GCTRACE(GC_MARK, ("continue scanQ @ 0x%x (%d)", scan, scan->queued));
+ /*
+ * Set pointer to current scanQ so that _pr_gcData.livePointer
+ * can find it.
+ */
+ pScanQ = next;
+ next->queued = 0;
+
+ /* Now scan the scan Q */
+ pp = scan->q;
+ epp = &scan->q[scan->queued];
+ scan->queued = 0;
+ while (pp < epp) {
+ p = *pp++;
+ ct = &_pr_collectorTypes[GET_TYPEIX(p[0])];
+ PR_ASSERT(0 != ct->gctype.scan);
+ /* Scan object ... */
+ (*ct->gctype.scan)(p + 1);
+ }
+
+ /* Exchange pointers so that we scan next */
+ temp = scan;
+ scan = next;
+ next = temp;
+ }
+
+ pScanQ = iscan;
+ PR_ASSERT(nextQ.queued == 0);
+ PR_ASSERT(iscan->queued == 0);
+}
+
+/*
+** Called during root finding step to identify "root" pointers into the
+** GC heap. First validate if it is a real heap pointer and then mark the
+** object being pointed to and add it to the scan Q for eventual
+** scanning.
+*/
+static void PR_CALLBACK ProcessRootBlock(void **base, PRInt32 count)
+{
+ GCSeg *sp;
+ PRWord *p0, *p, h, tix, *low, *high, *segBase;
+ CollectorType *ct;
+#ifdef DEBUG
+ void **base0 = base;
+#endif
+
+ low = _pr_gcData.lowSeg;
+ high = _pr_gcData.highSeg;
+ while (--count >= 0) {
+ p0 = (PRWord*) *base++;
+ /*
+ ** XXX:
+ ** Until Win16 maintains lowSeg and highSeg correctly,
+ ** (ie. lowSeg=MIN(all segs) and highSeg = MAX(all segs))
+ ** Allways scan through the segment list
+ */
+#if !defined(WIN16)
+ if (p0 < low) continue; /* below gc heap */
+ if (p0 >= high) continue; /* above gc heap */
+#endif
+ /* NOTE: inline expansion of InHeap */
+ /* Find segment */
+ sp = lastInHeap;
+ if (!sp || !IN_SEGMENT(sp,p0)) {
+ GCSeg *esp;
+ sp = segs;
+ esp = segs + nsegs;
+ for (; sp < esp; sp++) {
+ if (IN_SEGMENT(sp, p0)) {
+ lastInHeap = sp;
+ goto find_object;
+ }
+ }
+ continue;
+ }
+
+ find_object:
+ /* NOTE: Inline expansion of FindObject */
+ /* Align p to it's proper boundary before we start fiddling with it */
+ p = (PRWord*) ((PRWord)p0 & ~(PR_BYTES_PER_WORD-1L));
+ segBase = (PRWord *) sp->base;
+ do {
+ if (IS_HBIT(sp, p)) {
+ goto winner;
+ }
+ p--;
+ } while (p >= segBase);
+
+ /*
+ ** We have a pointer into the heap, but it has no header
+ ** bit. This means that somehow the very first object in the heap
+ ** doesn't have a header. This is impossible so when debugging
+ ** lets abort.
+ */
+#ifdef DEBUG
+ PR_Abort();
+#endif
+
+ winner:
+ h = p[0];
+ if ((h & MARK_BIT) == 0) {
+#ifdef DEBUG
+ _GCTRACE(GC_ROOTS,
+ ("root 0x%p (%d) base0=%p off=%d",
+ p, OBJ_BYTES(h), base0, (base-1) - base0));
+#endif
+
+ /* Mark the root we just found */
+ p[0] = h | MARK_BIT;
+
+ /*
+ * See if object we just found needs scanning. It must
+ * have a scan function to be placed on the scanQ.
+ */
+ tix = (PRWord)GET_TYPEIX(h);
+ ct = &_pr_collectorTypes[tix];
+ if (0 == ct->gctype.scan) {
+ continue;
+ }
+
+ /*
+ ** Put a pointer onto the scan Q. We use the scan Q to avoid
+ ** deep recursion on the C call stack. Objects are added to
+ ** the scan Q until the scan Q fills up. At that point we
+ ** make a call to ScanScanQ which proceeds to scan each of
+ ** the objects in the Q. This limits the recursion level by a
+ ** large amount though the stack frames get larger to hold
+ ** the GCScanQ's.
+ */
+ pScanQ->q[pScanQ->queued++] = p;
+ if (pScanQ->queued == MAX_SCAN_Q) {
+ METER(_pr_scanDepth++);
+ ScanScanQ(pScanQ);
+ }
+ }
+ }
+}
+
+static void PR_CALLBACK ProcessRootPointer(void *ptr)
+{
+ PRWord *p0, *p, h, tix, *segBase;
+ GCSeg* sp;
+ CollectorType *ct;
+
+ p0 = (PRWord*) ptr;
+
+ /*
+ ** XXX:
+ ** Until Win16 maintains lowSeg and highSeg correctly,
+ ** (ie. lowSeg=MIN(all segs) and highSeg = MAX(all segs))
+ ** Allways scan through the segment list
+ */
+#if !defined(WIN16)
+ if (p0 < _pr_gcData.lowSeg) return; /* below gc heap */
+ if (p0 >= _pr_gcData.highSeg) return; /* above gc heap */
+#endif
+
+ /* NOTE: inline expansion of InHeap */
+ /* Find segment */
+ sp = lastInHeap;
+ if (!sp || !IN_SEGMENT(sp,p0)) {
+ GCSeg *esp;
+ sp = segs;
+ esp = segs + nsegs;
+ for (; sp < esp; sp++) {
+ if (IN_SEGMENT(sp, p0)) {
+ lastInHeap = sp;
+ goto find_object;
+ }
+ }
+ return;
+ }
+
+ find_object:
+ /* NOTE: Inline expansion of FindObject */
+ /* Align p to it's proper boundary before we start fiddling with it */
+ p = (PRWord*) ((PRWord)p0 & ~(BYTES_PER_WORD-1L));
+ segBase = (PRWord *) sp->base;
+ do {
+ if (IS_HBIT(sp, p)) {
+ goto winner;
+ }
+ p--;
+ } while (p >= segBase);
+
+ /*
+ ** We have a pointer into the heap, but it has no header
+ ** bit. This means that somehow the very first object in the heap
+ ** doesn't have a header. This is impossible so when debugging
+ ** lets abort.
+ */
+#ifdef DEBUG
+ PR_Abort();
+#endif
+
+ winner:
+ h = p[0];
+ if ((h & MARK_BIT) == 0) {
+#ifdef DEBUG
+ _GCTRACE(GC_ROOTS, ("root 0x%p (%d)", p, OBJ_BYTES(h)));
+#endif
+
+ /* Mark the root we just found */
+ p[0] = h | MARK_BIT;
+
+ /*
+ * See if object we just found needs scanning. It must
+ * have a scan function to be placed on the scanQ.
+ */
+ tix = (PRWord)GET_TYPEIX(h);
+ ct = &_pr_collectorTypes[tix];
+ if (0 == ct->gctype.scan) {
+ return;
+ }
+
+ /*
+ ** Put a pointer onto the scan Q. We use the scan Q to avoid
+ ** deep recursion on the C call stack. Objects are added to
+ ** the scan Q until the scan Q fills up. At that point we
+ ** make a call to ScanScanQ which proceeds to scan each of
+ ** the objects in the Q. This limits the recursion level by a
+ ** large amount though the stack frames get larger to hold
+ ** the GCScanQ's.
+ */
+ pScanQ->q[pScanQ->queued++] = p;
+ if (pScanQ->queued == MAX_SCAN_Q) {
+ METER(_pr_scanDepth++);
+ ScanScanQ(pScanQ);
+ }
+ }
+}
+
+/************************************************************************/
+
+/*
+** Empty the freelist for each segment. This is done to make sure that
+** the root finding step works properly (otherwise, if we had a pointer
+** into a free section, we might not find its header word and abort in
+** FindObject)
+*/
+static void EmptyFreelists(void)
+{
+ GCFreeChunk *cp;
+ GCFreeChunk *next;
+ GCSeg *sp;
+ PRWord *p;
+ PRInt32 chunkSize;
+ PRInt32 bin;
+
+ /*
+ ** Run over the freelist and make all of the free chunks look like
+ ** object debris.
+ */
+ for (bin = 0; bin <= NUM_BINS-1; bin++) {
+ cp = bins[bin];
+ while (cp) {
+ next = cp->next;
+ sp = cp->segment;
+ chunkSize = cp->chunkSize >> BYTES_PER_WORD_LOG2;
+ p = (PRWord*) cp;
+ PR_ASSERT(chunkSize != 0);
+ p[0] = MAKE_HEADER(FREE_MEMORY_TYPEIX, chunkSize);
+ SET_HBIT(sp, p);
+ cp = next;
+ }
+ bins[bin] = 0;
+ }
+ minBin = NUM_BINS - 1;
+ maxBin = 0;
+}
+
+typedef struct GCBlockEnd {
+ PRInt32 check;
+#ifdef GC_CHECK
+ PRInt32 requestedBytes;
+#endif
+#ifdef GC_STATS
+ PRInt32 bin;
+ PRInt64 allocTime;
+#endif
+#ifdef GC_TRACEROOTS
+ PRInt32 traceGeneration;
+#endif
+} GCBlockEnd;
+
+#define PR_BLOCK_END 0xDEADBEEF
+
+/************************************************************************/
+
+#ifdef GC_STATS
+
+typedef struct GCStat {
+ PRInt32 nallocs;
+ double allocTime;
+ double allocTimeVariance;
+ PRInt32 nfrees;
+ double lifetime;
+ double lifetimeVariance;
+} GCStat;
+
+#define GCSTAT_BINS NUM_BINS
+
+GCStat gcstats[GCSTAT_BINS];
+
+#define GCLTFREQ_BINS NUM_BINS
+
+PRInt32 gcltfreq[GCSTAT_BINS][GCLTFREQ_BINS];
+
+#include <math.h>
+
+static char*
+pr_GetSizeString(PRUint32 size)
+{
+ char* sizeStr;
+ if (size < 1024)
+ sizeStr = PR_smprintf("<= %ld", size);
+ else if (size < 1024 * 1024)
+ sizeStr = PR_smprintf("<= %ldk", size / 1024);
+ else
+ sizeStr = PR_smprintf("<= %ldM", size / (1024 * 1024));
+ return sizeStr;
+}
+
+static void
+pr_FreeSizeString(char *sizestr)
+{
+ PR_smprintf_free(sizestr);
+}
+
+
+static void
+pr_PrintGCAllocStats(FILE* out)
+{
+ PRInt32 i, j;
+ _PR_DebugPrint(out, "\n--Allocation-Stats-----------------------------------------------------------");
+ _PR_DebugPrint(out, "\n--Obj-Size----Count-----Avg-Alloc-Time-----------Avg-Lifetime---------%%Freed-\n");
+ for (i = 0; i < GCSTAT_BINS; i++) {
+ GCStat stat = gcstats[i];
+ double allocTimeMean = 0.0, allocTimeVariance = 0.0, lifetimeMean = 0.0, lifetimeVariance = 0.0;
+ PRUint32 maxSize = (1 << i);
+ char* sizeStr;
+ if (stat.nallocs != 0.0) {
+ allocTimeMean = stat.allocTime / stat.nallocs;
+ allocTimeVariance = fabs(stat.allocTimeVariance / stat.nallocs - allocTimeMean * allocTimeMean);
+ }
+ if (stat.nfrees != 0.0) {
+ lifetimeMean = stat.lifetime / stat.nfrees;
+ lifetimeVariance = fabs(stat.lifetimeVariance / stat.nfrees - lifetimeMean * lifetimeMean);
+ }
+ sizeStr = pr_GetSizeString(maxSize);
+ _PR_DebugPrint(out, "%10s %8lu %10.3f +- %10.3f %10.3f +- %10.3f (%2ld%%)\n",
+ sizeStr, stat.nallocs,
+ allocTimeMean, sqrt(allocTimeVariance),
+ lifetimeMean, sqrt(lifetimeVariance),
+ (stat.nallocs ? (stat.nfrees * 100 / stat.nallocs) : 0));
+ pr_FreeSizeString(sizeStr);
+ }
+ _PR_DebugPrint(out, "--Lifetime-Frequency-Counts----------------------------------------------------\n");
+ _PR_DebugPrint(out, "size\\cnt");
+ for (j = 0; j < GCLTFREQ_BINS; j++) {
+ _PR_DebugPrint(out, "\t%lu", j);
+ }
+ _PR_DebugPrint(out, "\n");
+ for (i = 0; i < GCSTAT_BINS; i++) {
+ PRInt32* freqs = gcltfreq[i];
+ _PR_DebugPrint(out, "%lu", (1 << i));
+ for (j = 0; j < GCLTFREQ_BINS; j++) {
+ _PR_DebugPrint(out, "\t%lu", freqs[j]);
+ }
+ _PR_DebugPrint(out, "\n");
+ }
+ _PR_DebugPrint(out, "-------------------------------------------------------------------------------\n");
+}
+
+PR_PUBLIC_API(void)
+PR_PrintGCAllocStats(void)
+{
+ pr_PrintGCAllocStats(stderr);
+}
+
+#endif /* GC_STATS */
+
+/************************************************************************/
+
+/*
+** Sweep a segment, cleaning up all of the debris. Coallese the debris
+** into GCFreeChunk's which are added to the freelist bins.
+*/
+static PRBool SweepSegment(GCSeg *sp)
+{
+ PRWord h, tix;
+ PRWord *p;
+ PRWord *np;
+ PRWord *limit;
+ GCFreeChunk *cp;
+ PRInt32 bytes, chunkSize, segmentSize, totalFree;
+ CollectorType *ct;
+ PRInt32 bin;
+
+ /*
+ ** Now scan over the segment's memory in memory order, coallescing
+ ** all of the debris into a FreeChunk list.
+ */
+ totalFree = 0;
+ segmentSize = sp->limit - sp->base;
+ p = (PRWord *) sp->base;
+ limit = (PRWord *) sp->limit;
+ PR_ASSERT(segmentSize > 0);
+ while (p < limit) {
+ chunkSize = 0;
+ cp = (GCFreeChunk *) p;
+
+ /* Attempt to coallesce any neighboring free objects */
+ for (;;) {
+ PR_ASSERT(IS_HBIT(sp, p) != 0);
+ h = p[0];
+ bytes = OBJ_BYTES(h);
+ PR_ASSERT(bytes != 0);
+ np = (PRWord *) ((char *)p + bytes);
+ tix = (PRWord)GET_TYPEIX(h);
+ if ((h & MARK_BIT) && (tix != FREE_MEMORY_TYPEIX)) {
+#ifdef DEBUG
+ if (tix != FREE_MEMORY_TYPEIX) {
+ PR_ASSERT(_pr_collectorTypes[tix].flags != 0);
+ }
+#endif
+ p[0] = h & ~(MARK_BIT|FINAL_BIT);
+ _GCTRACE(GC_SWEEP, ("busy 0x%x (%d)", p, bytes));
+ break;
+ }
+ _GCTRACE(GC_SWEEP, ("free 0x%x (%d)", p, bytes));
+
+ /* Found a free object */
+#ifdef GC_STATS
+ {
+ PRInt32 userSize = bytes - sizeof(GCBlockEnd);
+ GCBlockEnd* end = (GCBlockEnd*)((char*)p + userSize);
+ if (userSize >= 0 && end->check == PR_BLOCK_END) {
+ PRInt64 now = PR_Now();
+ double nowd, delta;
+ PRInt32 freq;
+ LL_L2D(nowd, now);
+ delta = nowd - end->allocTime;
+ gcstats[end->bin].nfrees++;
+ gcstats[end->bin].lifetime += delta;
+ gcstats[end->bin].lifetimeVariance += delta * delta;
+
+ InlineBinNumber(freq, delta);
+ gcltfreq[end->bin][freq]++;
+
+ end->check = 0;
+ }
+ }
+#endif
+ CLEAR_HBIT(sp, p);
+ ct = &_pr_collectorTypes[tix];
+ if (0 != ct->gctype.free) {
+ (*ct->gctype.free)(p + 1);
+ }
+ chunkSize = chunkSize + bytes;
+ if (np == limit) {
+ /* Found the end of heap */
+ break;
+ }
+ PR_ASSERT(np < limit);
+ p = np;
+ }
+
+ if (chunkSize) {
+ _GCTRACE(GC_SWEEP, ("free chunk 0x%p to 0x%p (%d)",
+ cp, (char*)cp + chunkSize - 1, chunkSize));
+ if (chunkSize < MIN_FREE_CHUNK_BYTES) {
+ /* Lost a tiny fragment until (maybe) next time */
+ METER(meter.wastedBytes += chunkSize);
+ p = (PRWord *) cp;
+ chunkSize >>= BYTES_PER_WORD_LOG2;
+ PR_ASSERT(chunkSize != 0);
+ p[0] = MAKE_HEADER(FREE_MEMORY_TYPEIX, chunkSize);
+ SET_HBIT(sp, p);
+ } else {
+ /* See if the chunk constitutes the entire segment */
+ if (chunkSize == segmentSize) {
+ /* Free up the segment right now */
+ if (sp->info->fromMalloc) {
+ ShrinkGCHeap(sp);
+ return PR_TRUE;
+ }
+ }
+
+ /* Put free chunk into the appropriate bin */
+ cp->segment = sp;
+ cp->chunkSize = chunkSize;
+ InlineBinNumber(bin, chunkSize)
+ cp->next = bins[bin];
+ bins[bin] = cp;
+ if (bin < minBin) minBin = bin;
+ if (bin > maxBin) maxBin = bin;
+
+ /* Zero swept memory now */
+ memset(cp+1, 0, chunkSize - sizeof(*cp));
+ METER(meter.numFreeChunks++);
+ totalFree += chunkSize;
+ }
+ }
+
+ /* Advance to next object */
+ p = np;
+ }
+
+ PR_ASSERT(totalFree <= segmentSize);
+
+ _pr_gcData.freeMemory += totalFree;
+ _pr_gcData.busyMemory += (sp->limit - sp->base) - totalFree;
+ return PR_FALSE;
+}
+
+/************************************************************************/
+
+/* This is a list of all the objects that are finalizable. This is not
+ the list of objects that are awaiting finalization because they
+ have been collected. */
+PRCList _pr_finalizeableObjects;
+
+/* This is the list of objects that are awaiting finalization because
+ they have been collected. */
+PRCList _pr_finalQueue;
+
+/* Each object that requires finalization has one of these objects
+ allocated as well. The GCFinal objects are put on the
+ _pr_finalizeableObjects list until the object is collected at which
+ point the GCFinal object is moved to the _pr_finalQueue */
+typedef struct GCFinalStr {
+ PRCList links;
+ PRWord *object;
+} GCFinal;
+
+/* Find pointer to GCFinal struct from the list linkaged embedded in it */
+#define FinalPtr(_qp) \
+ ((GCFinal*) ((char*) (_qp) - offsetof(GCFinal,links)))
+
+static GCFinal *AllocFinalNode(void)
+{
+ return PR_NEWZAP(GCFinal);
+}
+
+static void FreeFinalNode(GCFinal *node)
+{
+ PR_DELETE(node);
+}
+
+/*
+** Prepare for finalization. At this point in the GC cycle we have
+** identified all of the live objects. For each object on the
+** _pr_finalizeableObjects list see if the object is alive or dead. If
+** it's dead, resurrect it and move it from the _pr_finalizeableObjects
+** list to the _pr_finalQueue (object's only get finalized once).
+**
+** Once _pr_finalizeableObjects has been processed we can finish the
+** GC and free up memory and release the threading lock. After that we
+** can invoke the finalization procs for each object that is on the
+** _pr_finalQueue.
+*/
+static void PrepareFinalize(void)
+{
+ PRCList *qp;
+ GCFinal *fp;
+ PRWord h;
+ PRWord *p;
+ void (PR_CALLBACK *livePointer)(void *ptr);
+#ifdef DEBUG
+ CollectorType *ct;
+#endif
+
+ /* This must be done under the same lock that the finalizer uses */
+ PR_ASSERT( GC_IS_LOCKED() );
+
+ /* cache this ptr */
+ livePointer = _pr_gcData.livePointer;
+
+ /*
+ * Pass #1: Identify objects that are to be finalized, set their
+ * FINAL_BIT.
+ */
+ qp = _pr_finalizeableObjects.next;
+ while (qp != &_pr_finalizeableObjects) {
+ fp = FinalPtr(qp);
+ qp = qp->next;
+ h = fp->object[0]; /* Grab header word */
+ if (h & MARK_BIT) {
+ /* Object is already alive */
+ continue;
+ }
+
+#ifdef DEBUG
+ ct = &_pr_collectorTypes[GET_TYPEIX(h)];
+ PR_ASSERT((0 != ct->flags) && (0 != ct->gctype.finalize));
+#endif
+ fp->object[0] |= FINAL_BIT;
+ _GCTRACE(GC_FINAL, ("moving %p (%d) to finalQueue",
+ fp->object, OBJ_BYTES(h)));
+ }
+
+ /*
+ * Pass #2: For each object that is going to be finalized, move it to
+ * the finalization queue and resurrect it
+ */
+ qp = _pr_finalizeableObjects.next;
+ while (qp != &_pr_finalizeableObjects) {
+ fp = FinalPtr(qp);
+ qp = qp->next;
+ h = fp->object[0]; /* Grab header word */
+ if ((h & FINAL_BIT) == 0) {
+ continue;
+ }
+
+ /* Resurrect the object and any objects it refers to */
+ p = &fp->object[1];
+ (*livePointer)(p);
+ PR_REMOVE_LINK(&fp->links);
+ PR_APPEND_LINK(&fp->links, &_pr_finalQueue);
+ }
+}
+
+/*
+** Scan the finalQ, marking each and every object on it live. This is
+** necessary because we might do a GC before objects that are on the
+** final queue get finalized. Since there are no other references
+** (otherwise they would be on the final queue), we have to scan them.
+** This really only does work if we call the GC before the finalizer
+** has a chance to do its job.
+*/
+extern void PR_CALLBACK _PR_ScanFinalQueue(void *notused)
+{
+#ifdef XP_MAC
+#pragma unused (notused)
+#endif
+ PRCList *qp;
+ GCFinal *fp;
+ PRWord *p;
+ void ( PR_CALLBACK *livePointer)(void *ptr);
+
+ livePointer = _pr_gcData.livePointer;
+ qp = _pr_finalQueue.next;
+ while (qp != &_pr_finalQueue) {
+ fp = FinalPtr(qp);
+ _GCTRACE(GC_FINAL, ("marking 0x%x (on final queue)", fp->object));
+ p = &fp->object[1];
+ (*livePointer)(p);
+ qp = qp->next;
+ }
+}
+
+void PR_CALLBACK FinalizerLoop(void* unused)
+{
+#ifdef XP_MAC
+#pragma unused (unused)
+#endif
+ GCFinal *fp;
+ PRWord *p;
+ PRWord h, tix;
+ CollectorType *ct;
+
+ LOCK_GC();
+ for (;;) {
+ p = 0; h = 0; /* don't let the gc find these pointers */
+ while (PR_CLIST_IS_EMPTY(&_pr_finalQueue))
+ PR_Wait(_pr_gcData.lock, PR_INTERVAL_NO_TIMEOUT);
+
+ _GCTRACE(GC_FINAL, ("begin finalization"));
+ while (_pr_finalQueue.next != &_pr_finalQueue) {
+ fp = FinalPtr(_pr_finalQueue.next);
+ PR_REMOVE_LINK(&fp->links);
+ p = fp->object;
+
+ h = p[0]; /* Grab header word */
+ tix = (PRWord)GET_TYPEIX(h);
+ ct = &_pr_collectorTypes[tix];
+ _GCTRACE(GC_FINAL, ("finalize 0x%x (%d)", p, OBJ_BYTES(h)));
+
+ /*
+ ** Give up the GC lock so that other threads can allocate memory
+ ** while this finalization method is running. Get it back
+ ** afterwards so that the list remains thread safe.
+ */
+ UNLOCK_GC();
+ FreeFinalNode(fp);
+ PR_ASSERT(ct->gctype.finalize != 0);
+ (*ct->gctype.finalize)(p + 1);
+ LOCK_GC();
+ }
+ _GCTRACE(GC_FINAL, ("end finalization"));
+ PR_Notify(_pr_gcData.lock);
+ }
+}
+
+static void NotifyFinalizer(void)
+{
+ if (!PR_CLIST_IS_EMPTY(&_pr_finalQueue)) {
+ PR_ASSERT( GC_IS_LOCKED() );
+ PR_Notify(_pr_gcData.lock);
+ }
+}
+
+void _PR_CreateFinalizer(PRThreadScope scope)
+{
+ if (!_pr_gcData.finalizer) {
+ _pr_gcData.finalizer = PR_CreateThreadGCAble(PR_SYSTEM_THREAD,
+ FinalizerLoop, 0,
+ PR_PRIORITY_LOW, scope,
+ PR_UNJOINABLE_THREAD, 0);
+
+ if (_pr_gcData.finalizer == NULL)
+ /* We are doomed if we can't start the finalizer */
+ PR_Abort();
+
+ }
+}
+
+void pr_FinalizeOnExit(void)
+{
+#ifdef DEBUG_warren
+ OutputDebugString("### Doing finalize-on-exit pass\n");
+#endif
+ PR_ForceFinalize();
+#ifdef DEBUG_warren
+ OutputDebugString("### Finalize-on-exit complete. Dumping object left to memory.out\n");
+ PR_DumpMemorySummary();
+ PR_DumpMemory(PR_TRUE);
+#endif
+}
+
+PR_IMPLEMENT(void) PR_ForceFinalize()
+{
+ LOCK_GC();
+ NotifyFinalizer();
+ while (!PR_CLIST_IS_EMPTY(&_pr_finalQueue)) {
+ PR_ASSERT( GC_IS_LOCKED() );
+ (void) PR_Wait(_pr_gcData.lock, PR_INTERVAL_NO_TIMEOUT);
+ }
+ UNLOCK_GC();
+
+ /* XXX I don't know how to make it wait (yet) */
+}
+
+/************************************************************************/
+
+typedef struct GCWeakStr {
+ PRCList links;
+ PRWord *object;
+} GCWeak;
+
+/*
+** Find pointer to GCWeak struct from the list linkaged embedded in it
+*/
+#define WeakPtr(_qp) \
+ ((GCWeak*) ((char*) (_qp) - offsetof(GCWeak,links)))
+
+PRCList _pr_weakLinks = PR_INIT_STATIC_CLIST(&_pr_weakLinks);
+PRCList _pr_freeWeakLinks = PR_INIT_STATIC_CLIST(&_pr_freeWeakLinks);
+
+#define WEAK_FREELIST_ISEMPTY() (_pr_freeWeakLinks.next == &_pr_freeWeakLinks)
+
+/*
+ * Keep objects referred to by weak free list alive until they can be
+ * freed
+ */
+static void PR_CALLBACK ScanWeakFreeList(void *notused) {
+#ifdef XP_MAC
+#pragma unused (notused)
+#endif
+ PRCList *qp = _pr_freeWeakLinks.next;
+ while (qp != &_pr_freeWeakLinks) {
+ GCWeak *wp = WeakPtr(qp);
+ qp = qp->next;
+ ProcessRootPointer(wp->object);
+ }
+}
+
+/*
+ * Empty the list of weak objects. Note that we can't call malloc/free
+ * under the cover of the GC's lock (we might deadlock), so transfer the
+ * list of free objects to a local list under the cover of the lock, then
+ * release the lock and free up the memory.
+ */
+static void EmptyWeakFreeList(void) {
+ if (!WEAK_FREELIST_ISEMPTY()) {
+ PRCList *qp, freeLinks;
+
+ PR_INIT_CLIST(&freeLinks);
+
+ /*
+ * Transfer list of free weak links from the global list to a
+ * local list.
+ */
+ LOCK_GC();
+ qp = _pr_freeWeakLinks.next;
+ while (qp != &_pr_freeWeakLinks) {
+ GCWeak *wp = WeakPtr(qp);
+ qp = qp->next;
+ PR_REMOVE_LINK(&wp->links);
+ PR_APPEND_LINK(&wp->links, &freeLinks);
+ }
+ UNLOCK_GC();
+
+ /* Free up storage now */
+ qp = freeLinks.next;
+ while (qp != &freeLinks) {
+ GCWeak *wp = WeakPtr(qp);
+ qp = qp->next;
+ PR_DELETE(wp);
+ }
+ }
+}
+
+/*
+ * Allocate a new weak node in the weak objects list
+ */
+static GCWeak *AllocWeakNode(void)
+{
+ EmptyWeakFreeList();
+ return PR_NEWZAP(GCWeak);
+}
+
+static void FreeWeakNode(GCWeak *node)
+{
+ PR_DELETE(node);
+}
+
+/*
+ * Check the weak links for validity. Note that the list of weak links is
+ * itself weak (otherwise we would keep the objects with weak links in
+ * them alive forever). As we scan the list check the weak link object
+ * itself and if it's not marked then remove it from the weak link list
+ */
+static void CheckWeakLinks(void) {
+ PRCList *qp;
+ GCWeak *wp;
+ PRWord *p, h, tix, **weakPtrAddress;
+ CollectorType *ct;
+ PRUint32 offset;
+
+ qp = _pr_weakLinks.next;
+ while (qp != &_pr_weakLinks) {
+ wp = WeakPtr(qp);
+ qp = qp->next;
+ if ((p = wp->object) != 0) {
+ h = p[0]; /* Grab header word */
+ if ((h & MARK_BIT) == 0) {
+ /*
+ * The object that has a weak link is no longer being
+ * referenced; remove it from the chain and let it get
+ * swept away by the GC. Transfer it to the list of
+ * free weak links for later freeing.
+ */
+ PR_REMOVE_LINK(&wp->links);
+ PR_APPEND_LINK(&wp->links, &_pr_freeWeakLinks);
+ collectorCleanupNeeded = 1;
+ continue;
+ }
+
+ /* Examine a live object that contains weak links */
+ tix = GET_TYPEIX(h);
+ ct = &_pr_collectorTypes[tix];
+ PR_ASSERT((ct->flags != 0) && (ct->gctype.getWeakLinkOffset != 0));
+ if (0 == ct->gctype.getWeakLinkOffset) {
+ /* Heap is probably corrupted */
+ continue;
+ }
+
+ /* Get offset into the object of where the weak pointer is */
+ offset = (*ct->gctype.getWeakLinkOffset)(p + 1);
+
+ /* Check the weak pointer */
+ weakPtrAddress = (PRWord**)((char*)(p + 1) + offset);
+ p = *weakPtrAddress;
+ if (p != 0) {
+ h = p[-1]; /* Grab header word for pointed to object */
+ if (h & MARK_BIT) {
+ /* Object can't be dead */
+ continue;
+ }
+ /* Break weak link to an object that is about to be swept */
+ *weakPtrAddress = 0;
+ }
+ }
+ }
+}
+
+/************************************************************************/
+
+/*
+** Perform a complete garbage collection
+*/
+
+extern GCLockHook *_pr_GCLockHook;
+
+static void dogc(void)
+{
+ RootFinder *rf;
+ GCLockHook* lhook;
+
+ GCScanQ scanQ;
+ GCSeg *sp, *esp;
+ PRInt64 start, end, diff;
+
+#if defined(GCMETER) || defined(GCTIMINGHOOK)
+ start = PR_Now();
+#endif
+
+ /*
+ ** Stop all of the other threads. This also promises to capture the
+ ** register state of each and every thread
+ */
+
+ /*
+ ** Get all the locks that will be need during GC after SuspendAll. We
+ ** cannot make any locking/library calls after SuspendAll.
+ */
+ if (_pr_GCLockHook) {
+ for (lhook = _pr_GCLockHook->next; lhook != _pr_GCLockHook;
+ lhook = lhook->next) {
+ (*lhook->func)(PR_GCBEGIN, lhook->arg);
+ }
+ }
+
+ PR_SuspendAll();
+
+#ifdef GCMETER
+ /* Reset meter info */
+ if (_pr_gcMeter & _GC_METER_STATS) {
+ fprintf(stderr,
+ "[GCSTATS: busy:%ld skipped:%ld, alloced:%ld+wasted:%ld+free:%ld = total:%ld]\n",
+ (long) _pr_gcData.busyMemory,
+ (long) meter.skippedFreeChunks,
+ (long) meter.allocBytes,
+ (long) meter.wastedBytes,
+ (long) _pr_gcData.freeMemory,
+ (long) _pr_gcData.allocMemory);
+ }
+ memset(&meter, 0, sizeof(meter));
+#endif
+
+ PR_LOG(_pr_msgc_lm, PR_LOG_ALWAYS, ("begin mark phase; busy=%d free=%d total=%d",
+ _pr_gcData.busyMemory, _pr_gcData.freeMemory,
+ _pr_gcData.allocMemory));
+
+ if (_pr_beginGCHook) {
+ (*_pr_beginGCHook)(_pr_beginGCHookArg);
+ }
+
+ /*
+ ** Initialize scanQ to all zero's so that root finder doesn't walk
+ ** over it...
+ */
+ memset(&scanQ, 0, sizeof(scanQ));
+ pScanQ = &scanQ;
+
+ /******************************************/
+ /* MARK PHASE */
+
+ EmptyFreelists();
+
+ /* Find root's */
+ PR_LOG(_pr_msgc_lm, PR_LOG_WARNING,
+ ("begin mark phase; busy=%d free=%d total=%d",
+ _pr_gcData.busyMemory, _pr_gcData.freeMemory,
+ _pr_gcData.allocMemory));
+ METER(_pr_scanDepth = 0);
+ rf = _pr_rootFinders;
+ while (rf) {
+ _GCTRACE(GC_ROOTS, ("finding roots in %s", rf->name));
+ (*rf->func)(rf->arg);
+ rf = rf->next;
+ }
+ _GCTRACE(GC_ROOTS, ("done finding roots"));
+
+ /* Scan remaining object's that need scanning */
+ ScanScanQ(&scanQ);
+ PR_ASSERT(pScanQ == &scanQ);
+ PR_ASSERT(scanQ.queued == 0);
+ METER({
+ if (_pr_scanDepth > _pr_maxScanDepth) {
+ _pr_maxScanDepth = _pr_scanDepth;
+ }
+ });
+
+ /******************************************/
+ /* FINALIZATION PHASE */
+
+ METER(_pr_scanDepth = 0);
+ PrepareFinalize();
+
+ /* Scan any resurrected objects found during finalization */
+ ScanScanQ(&scanQ);
+ PR_ASSERT(pScanQ == &scanQ);
+ PR_ASSERT(scanQ.queued == 0);
+ METER({
+ if (_pr_scanDepth > _pr_maxScanDepth) {
+ _pr_maxScanDepth = _pr_scanDepth;
+ }
+ });
+ pScanQ = 0;
+
+ /******************************************/
+ /* SWEEP PHASE */
+
+ /*
+ ** Sweep each segment clean. While we are at it, figure out which
+ ** segment has the most free space and make that the current segment.
+ */
+ CheckWeakLinks();
+ _GCTRACE(GC_SWEEP, ("begin sweep phase"));
+ _pr_gcData.freeMemory = 0;
+ _pr_gcData.busyMemory = 0;
+ sp = segs;
+ esp = sp + nsegs;
+ while (sp < esp) {
+ if (SweepSegment(sp)) {
+ /*
+ ** Segment is now free and has been replaced with a different
+ ** segment object.
+ */
+ esp--;
+ continue;
+ }
+ sp++;
+ }
+
+#if defined(GCMETER) || defined(GCTIMINGHOOK)
+ end = PR_Now();
+#endif
+#ifdef GCMETER
+ LL_SUB(diff, end, start);
+ PR_LOG(GC, PR_LOG_ALWAYS,
+ ("done; busy=%d free=%d chunks=%d total=%d time=%lldms",
+ _pr_gcData.busyMemory, _pr_gcData.freeMemory,
+ meter.numFreeChunks, _pr_gcData.allocMemory, diff));
+ if (_pr_gcMeter & _GC_METER_FREE_LIST) {
+ PRIntn bin;
+ fprintf(stderr, "Freelist bins:\n");
+ for (bin = 0; bin < NUM_BINS; bin++) {
+ GCFreeChunk *cp = bins[bin];
+ while (cp != NULL) {
+ fprintf(stderr, "%3d: %p %8ld\n",
+ bin, cp, (long) cp->chunkSize);
+ cp = cp->next;
+ }
+ }
+ }
+#endif
+
+ if (_pr_endGCHook) {
+ (*_pr_endGCHook)(_pr_endGCHookArg);
+ }
+
+ /* clear the running total of the bytes allocated via BigAlloc() */
+ bigAllocBytes = 0;
+
+ /* And resume multi-threading */
+ PR_ResumeAll();
+
+ if (_pr_GCLockHook) {
+ for (lhook = _pr_GCLockHook->prev; lhook != _pr_GCLockHook;
+ lhook = lhook->prev) {
+ (*lhook->func)(PR_GCEND, lhook->arg);
+ }
+ }
+
+ /* Kick finalizer */
+ NotifyFinalizer();
+#ifdef GCTIMINGHOOK
+ if (_pr_gcData.gcTimingHook) {
+ PRInt32 time;
+ LL_SUB(diff, end, start);
+ LL_L2I(time, diff);
+ _pr_gcData.gcTimingHook(time);
+ }
+#endif
+}
+
+PR_IMPLEMENT(void) PR_GC(void)
+{
+ LOCK_GC();
+ dogc();
+ UNLOCK_GC();
+
+ EmptyWeakFreeList();
+}
+
+/*******************************************************************************
+ * Heap Walker
+ ******************************************************************************/
+
+/*
+** This is yet another disgusting copy of the body of ProcessRootPointer
+** (the other being ProcessRootBlock), but we're not leveraging a single
+** function in their cases in interest of performance (avoiding the function
+** call).
+*/
+static PRInt32 PR_CALLBACK
+pr_ConservativeWalkPointer(void* ptr, PRWalkFun walkRootPointer, void* data)
+{
+ PRWord *p0, *p, *segBase;
+ GCSeg* sp;
+
+ p0 = (PRWord*) ptr;
+
+ /*
+ ** XXX:
+ ** Until Win16 maintains lowSeg and highSeg correctly,
+ ** (ie. lowSeg=MIN(all segs) and highSeg = MAX(all segs))
+ ** Allways scan through the segment list
+ */
+#if !defined(WIN16)
+ if (p0 < _pr_gcData.lowSeg) return 0; /* below gc heap */
+ if (p0 >= _pr_gcData.highSeg) return 0; /* above gc heap */
+#endif
+
+ /* NOTE: inline expansion of InHeap */
+ /* Find segment */
+ sp = lastInHeap;
+ if (!sp || !IN_SEGMENT(sp,p0)) {
+ GCSeg *esp;
+ sp = segs;
+ esp = segs + nsegs;
+ for (; sp < esp; sp++) {
+ if (IN_SEGMENT(sp, p0)) {
+ lastInHeap = sp;
+ goto find_object;
+ }
+ }
+ return 0;
+ }
+
+ find_object:
+ /* NOTE: Inline expansion of FindObject */
+ /* Align p to it's proper boundary before we start fiddling with it */
+ p = (PRWord*) ((PRWord)p0 & ~(BYTES_PER_WORD-1L));
+ segBase = (PRWord *) sp->base;
+ do {
+ if (IS_HBIT(sp, p)) {
+ goto winner;
+ }
+ p--;
+ } while (p >= segBase);
+
+ /*
+ ** We have a pointer into the heap, but it has no header
+ ** bit. This means that somehow the very first object in the heap
+ ** doesn't have a header. This is impossible so when debugging
+ ** lets abort.
+ */
+#ifdef DEBUG
+ PR_Abort();
+#endif
+ return 0;
+
+ winner:
+ return walkRootPointer(p, data);
+}
+
+static PRInt32 PR_CALLBACK
+pr_ConservativeWalkBlock(void **base, PRInt32 count,
+ PRWalkFun walkRootPointer, void* data)
+{
+ PRWord *p0;
+ while (--count >= 0) {
+ PRInt32 status;
+ p0 = (PRWord*) *base++;
+ status = pr_ConservativeWalkPointer(p0, walkRootPointer, data);
+ if (status) return status;
+ }
+ return 0;
+}
+
+/******************************************************************************/
+
+typedef void (*WalkObject_t)(FILE *out, GCType* tp, PRWord *obj,
+ size_t bytes, PRBool detailed);
+typedef void (*WalkUnknown_t)(FILE *out, GCType* tp, PRWord tix, PRWord *p,
+ size_t bytes, PRBool detailed);
+typedef void (*WalkFree_t)(FILE *out, PRWord *p, size_t size, PRBool detailed);
+typedef void (*WalkSegment_t)(FILE *out, GCSeg* sp, PRBool detailed);
+
+static void
+pr_WalkSegment(FILE* out, GCSeg* sp, PRBool detailed,
+ char* enterMsg, char* exitMsg,
+ WalkObject_t walkObject, WalkUnknown_t walkUnknown, WalkFree_t walkFree)
+{
+ PRWord *p, *limit;
+
+ p = (PRWord *) sp->base;
+ limit = (PRWord *) sp->limit;
+ if (enterMsg)
+ fprintf(out, enterMsg, p);
+ while (p < limit)
+ {
+ if (IS_HBIT(sp, p)) /* Is this an object header? */
+ {
+ PRWord h = p[0];
+ PRWord tix = GET_TYPEIX(h);
+ size_t bytes = OBJ_BYTES(h);
+ PRWord* np = (PRWord*) ((char*)p + bytes);
+
+ GCType* tp = &_pr_collectorTypes[tix].gctype;
+ if ((0 != tp) && walkObject)
+ walkObject(out, tp, p, bytes, detailed);
+ else if (walkUnknown)
+ walkUnknown(out, tp, tix, p, bytes, detailed);
+ p = np;
+ }
+ else
+ {
+ /* Must be a freelist item */
+ size_t size = ((GCFreeChunk*)p)->chunkSize;
+ if (walkFree)
+ walkFree(out, p, size, detailed);
+ p = (PRWord*)((char*)p + size);
+ }
+ }
+ if (p != limit)
+ fprintf(out, "SEGMENT OVERRUN (end should be at 0x%p)\n", limit);
+ if (exitMsg)
+ fprintf(out, exitMsg, p);
+}
+
+static void
+pr_WalkSegments(FILE *out, WalkSegment_t walkSegment, PRBool detailed)
+{
+ GCSeg *sp = segs;
+ GCSeg *esp;
+
+ LOCK_GC();
+ esp = sp + nsegs;
+ while (sp < esp)
+ {
+ walkSegment(out, sp, detailed);
+ sp++;
+ }
+ fprintf(out, "End of heap\n");
+ UNLOCK_GC();
+}
+
+/*******************************************************************************
+ * Heap Dumper
+ ******************************************************************************/
+
+PR_IMPLEMENT(void)
+PR_DumpIndent(FILE *out, int indent)
+{
+ while (--indent >= 0)
+ fprintf(out, " ");
+}
+
+static void
+PR_DumpHexWords(FILE *out, PRWord *p, int nWords,
+ int indent, int nWordsPerLine)
+{
+ while (nWords > 0)
+ {
+ int i;
+
+ PR_DumpIndent(out, indent);
+ i = nWordsPerLine;
+ if (i > nWords)
+ i = nWords;
+ nWords -= i;
+ while (i--)
+ {
+ fprintf(out, "0x%.8lX", (long) *p++);
+ if (i)
+ fputc(' ', out);
+ }
+ fputc('\n', out);
+ }
+}
+
+static void PR_CALLBACK
+pr_DumpObject(FILE *out, GCType* tp, PRWord *p,
+ size_t bytes, PRBool detailed)
+{
+ char kindChar = tp->kindChar;
+ fprintf(out, "0x%p: 0x%.6lX %c ",
+ p, (long) bytes, kindChar ? kindChar : '?');
+ if (tp->dump)
+ (*tp->dump)(out, (void*) (p + 1), detailed, 0);
+ if (detailed)
+ PR_DumpHexWords(out, p, bytes>>2, 22, 4);
+}
+
+static void PR_CALLBACK
+pr_DumpUnknown(FILE *out, GCType* tp, PRWord tix, PRWord *p,
+ size_t bytes, PRBool detailed)
+{
+ char kindChar = tp->kindChar;
+ fprintf(out, "0x%p: 0x%.6lX %c ",
+ p, (long) bytes, kindChar ? kindChar : '?');
+ fprintf(out, "UNKNOWN KIND %ld\n", (long) tix);
+ if (detailed)
+ PR_DumpHexWords(out, p, bytes>>2, 22, 4);
+}
+
+static void PR_CALLBACK
+pr_DumpFree(FILE *out, PRWord *p, size_t size, PRBool detailed)
+{
+#if defined(XP_MAC) && XP_MAC
+# pragma unused( detailed )
+#endif
+
+ fprintf(out, "0x%p: 0x%.6lX - FREE\n", p, (long) size);
+}
+
+static void PR_CALLBACK
+pr_DumpSegment(FILE* out, GCSeg* sp, PRBool detailed)
+{
+ pr_WalkSegment(out, sp, detailed,
+ "\n Address: Length\n0x%p: Beginning of segment\n",
+ "0x%p: End of segment\n\n",
+ pr_DumpObject, pr_DumpUnknown, pr_DumpFree);
+}
+
+static void pr_DumpRoots(FILE *out);
+
+/*
+** Dump out the GC heap.
+*/
+PR_IMPLEMENT(void)
+PR_DumpGCHeap(FILE *out, PRBool detailed)
+{
+ fprintf(out, "\n"
+ "The kinds are:\n"
+ " U unscanned block\n"
+ " W weak link block\n"
+ " S scanned block\n"
+ " F scanned and final block\n"
+ " C class record\n"
+ " X context record\n"
+ " - free list item\n"
+ " ? other\n");
+ LOCK_GC();
+ pr_WalkSegments(out, pr_DumpSegment, detailed);
+ if (detailed)
+ pr_DumpRoots(out);
+ UNLOCK_GC();
+}
+
+PR_IMPLEMENT(void)
+PR_DumpMemory(PRBool detailed)
+{
+ PR_DumpToFile("memory.out", "Dumping memory", PR_DumpGCHeap, detailed);
+}
+
+/******************************************************************************/
+
+static PRInt32 PR_CALLBACK
+pr_DumpRootPointer(PRWord* p, void* data)
+{
+#ifdef XP_MAC
+#pragma unused(data)
+#endif
+ PRWord h = p[0];
+ PRWord tix = GET_TYPEIX(h);
+ size_t bytes = OBJ_BYTES(h);
+
+ GCType* tp = &_pr_collectorTypes[tix].gctype;
+ if (0 != tp)
+ pr_DumpObject(_pr_gcData.dumpOutput, tp, p, bytes, PR_FALSE);
+ else
+ pr_DumpUnknown(_pr_gcData.dumpOutput, tp, tix, p, bytes, PR_FALSE);
+ return 0;
+}
+
+static void PR_CALLBACK
+pr_ConservativeDumpRootPointer(void* ptr)
+{
+ (void)pr_ConservativeWalkPointer(ptr, (PRWalkFun) pr_DumpRootPointer, NULL);
+}
+
+static void PR_CALLBACK
+pr_ConservativeDumpRootBlock(void **base, PRInt32 count)
+{
+ (void)pr_ConservativeWalkBlock(base, count, (PRWalkFun) pr_DumpRootPointer, NULL);
+}
+
+extern int
+DumpThreadRoots(PRThread *t, int i, void *notused);
+
+static void
+pr_DumpRoots(FILE *out)
+{
+ RootFinder *rf;
+ void (*liveBlock)(void **base, PRInt32 count);
+ void (*livePointer)(void *ptr);
+ void (*processRootBlock)(void **base, PRInt32 count);
+ void (*processRootPointer)(void *ptr);
+
+ LOCK_GC();
+
+ liveBlock = _pr_gcData.liveBlock;
+ livePointer = _pr_gcData.livePointer;
+ processRootBlock = _pr_gcData.processRootBlock;
+ processRootPointer = _pr_gcData.processRootPointer;
+
+ _pr_gcData.liveBlock = pr_ConservativeDumpRootBlock;
+ _pr_gcData.livePointer = pr_ConservativeDumpRootPointer;
+ _pr_gcData.processRootBlock = pr_ConservativeDumpRootBlock;
+ _pr_gcData.processRootPointer = pr_ConservativeDumpRootPointer;
+ _pr_gcData.dumpOutput = out;
+
+ rf = _pr_rootFinders;
+ while (rf) {
+ fprintf(out, "\n===== Roots for %s\n", rf->name);
+ (*rf->func)(rf->arg);
+ rf = rf->next;
+ }
+
+ _pr_gcData.liveBlock = liveBlock;
+ _pr_gcData.livePointer = livePointer;
+ _pr_gcData.processRootBlock = processRootBlock;
+ _pr_gcData.processRootPointer = processRootPointer;
+ _pr_gcData.dumpOutput = NULL;
+
+ UNLOCK_GC();
+}
+
+/*******************************************************************************
+ * Heap Summary Dumper
+ ******************************************************************************/
+
+PRSummaryPrinter summaryPrinter = NULL;
+void* summaryPrinterClosure = NULL;
+
+PR_IMPLEMENT(void)
+PR_RegisterSummaryPrinter(PRSummaryPrinter fun, void* closure)
+{
+ summaryPrinter = fun;
+ summaryPrinterClosure = closure;
+}
+
+static void PR_CALLBACK
+pr_SummarizeObject(FILE *out, GCType* tp, PRWord *p,
+ size_t bytes, PRBool detailed)
+{
+#if defined(XP_MAC) && XP_MAC
+# pragma unused( out, detailed )
+#endif
+
+ if (tp->summarize)
+ (*tp->summarize)((void GCPTR*)(p + 1), bytes);
+}
+
+static void PR_CALLBACK
+pr_DumpSummary(FILE* out, GCSeg* sp, PRBool detailed)
+{
+ pr_WalkSegment(out, sp, detailed, NULL, NULL,
+ pr_SummarizeObject, NULL, NULL);
+}
+
+PR_IMPLEMENT(void)
+PR_DumpGCSummary(FILE *out, PRBool detailed)
+{
+ if (summaryPrinter) {
+ pr_WalkSegments(out, pr_DumpSummary, detailed);
+ summaryPrinter(out, summaryPrinterClosure);
+ }
+#if 0
+ fprintf(out, "\nFinalizable objects:\n");
+ {
+ PRCList *qp;
+ qp = _pr_pendingFinalQueue.next;
+ while (qp != &_pr_pendingFinalQueue) {
+ GCFinal* fp = FinalPtr(qp);
+ PRWord h = fp->object[0]; /* Grab header word */
+ PRWord tix = GET_TYPEIX(h);
+ GCType* tp = _pr_gcTypes[tix];
+ size_t bytes = OBJ_BYTES(h);
+ pr_DumpObject(out, tp, fp->object, bytes, PR_FALSE);
+ qp = qp->next;
+ }
+ }
+#endif
+}
+
+PR_IMPLEMENT(void)
+PR_DumpMemorySummary(void)
+{
+ PR_DumpToFile("memory.out", "Memory Summary", PR_DumpGCSummary, PR_FALSE);
+}
+
+/*******************************************************************************
+ * End Of Heap Walker
+ ******************************************************************************/
+
+#ifdef GC_TRACEROOTS
+
+PRInt32 pr_traceGen = 0;
+
+static PRBool
+pr_IsMarked(PRWord* p)
+{
+ GCBlockEnd* end = (GCBlockEnd*)((char*)p + OBJ_BYTES(p[0]) - sizeof(GCBlockEnd));
+ PR_ASSERT(end->check == PR_BLOCK_END);
+ return end->traceGeneration == pr_traceGen;
+}
+
+static void
+pr_Mark(PRWord* p)
+{
+ GCBlockEnd* end = (GCBlockEnd*)((char*)p + OBJ_BYTES(p[0]) - sizeof(GCBlockEnd));
+ PR_ASSERT(end->check == PR_BLOCK_END);
+ end->traceGeneration = pr_traceGen;
+}
+
+PRWord* pr_traceObj; /* set this in the debugger, then execute PR_TraceRoot() */
+
+static PRInt32 PR_CALLBACK
+pr_TraceRootObject(void* obj, void* data);
+
+static PRInt32 PR_CALLBACK
+pr_TraceRootPointer(PRWord *p, void* data)
+{
+ PRInt32 printTrace = 0;
+ PRWord h = p[0];
+ PRWord tix = GET_TYPEIX(h);
+ GCType* tp = &_pr_collectorTypes[tix].gctype;
+ FILE* out = _pr_gcData.dumpOutput;
+
+ PR_ASSERT(tp);
+ if (pr_IsMarked(p))
+ return printTrace;
+
+ pr_Mark(p);
+ if (p == pr_traceObj) {
+ fprintf(out, "\n### Found path to:\n");
+ printTrace = 1;
+ }
+ else {
+ if (PR_StackSpaceLeft(PR_CurrentThread()) < 512) {
+ fprintf(out, "\n### Path too deep (giving up):\n");
+ printTrace = 1;
+ }
+ else if (tp->walk) {
+ printTrace = tp->walk((void*)(p + 1), pr_TraceRootObject, data);
+ }
+ /* else there's no way to walk this object, so we
+ haven't found what we're looking for */
+ }
+
+ if (printTrace == 1) {
+ PR_ASSERT(tp->dump);
+ fprintf(out, "0x%p: ", p);
+ tp->dump(out, (void*)(p + 1), PR_FALSE, 1);
+ }
+ return printTrace;
+}
+
+static PRInt32 PR_CALLBACK
+pr_TraceRootObject(void* obj, void* data)
+{
+ /* This version of pr_TraceRootPointer takes object
+ pointers, instead of gc header pointers. */
+ return pr_TraceRootPointer((PRWord*)obj - 1, data);
+}
+
+static void PR_CALLBACK
+pr_ConservativeTraceRootPointer(PRWord *p)
+{
+ PRInt32 status;
+ ++pr_traceGen;
+ status = pr_ConservativeWalkPointer(p, pr_TraceRootPointer, NULL);
+ if (status) {
+ FILE* out = _pr_gcData.dumpOutput;
+ fprintf(out, "### from root at 0x%p\n\n", p);
+ }
+}
+
+static void PR_CALLBACK
+pr_ConservativeTraceRootBlock(void **base, PRInt32 count)
+{
+ PRInt32 status;
+ ++pr_traceGen;
+ status = pr_ConservativeWalkBlock(base, count, pr_TraceRootPointer, NULL);
+ if (status) {
+ FILE* out = _pr_gcData.dumpOutput;
+ fprintf(out, "### from root in range 0x%p + 0x%lx\n\n",
+ base, (long) count);
+ }
+}
+
+static void
+PR_TraceRoot1(FILE* out, PRBool detailed)
+{
+ RootFinder *rf;
+ void (*liveBlock)(void **base, PRInt32 count);
+ void (*livePointer)(void *ptr);
+ void (*processRootBlock)(void **base, PRInt32 count);
+ void (*processRootPointer)(void *ptr);
+
+ LOCK_GC();
+
+ liveBlock = _pr_gcData.liveBlock;
+ livePointer = _pr_gcData.livePointer;
+ processRootBlock = _pr_gcData.processRootBlock;
+ processRootPointer = _pr_gcData.processRootPointer;
+
+ _pr_gcData.liveBlock = pr_ConservativeTraceRootBlock;
+ _pr_gcData.livePointer = pr_ConservativeTraceRootPointer;
+ _pr_gcData.processRootBlock = pr_ConservativeTraceRootBlock;
+ _pr_gcData.processRootPointer = pr_ConservativeTraceRootPointer;
+ _pr_gcData.dumpOutput = out;
+
+ fprintf(out, "### Looking for paths to 0x%p\n\n", pr_traceObj);
+
+ rf = _pr_rootFinders;
+ while (rf) {
+ fprintf(out, "\n===== Roots for %s\n", rf->name);
+ (*rf->func)(rf->arg);
+ rf = rf->next;
+ }
+
+ _pr_gcData.liveBlock = liveBlock;
+ _pr_gcData.livePointer = livePointer;
+ _pr_gcData.processRootBlock = processRootBlock;
+ _pr_gcData.processRootPointer = processRootPointer;
+ _pr_gcData.dumpOutput = NULL;
+
+ UNLOCK_GC();
+}
+
+PR_PUBLIC_API(void)
+PR_TraceRoot()
+{
+ /*
+ ** How this works:
+ ** Once you find the object you want to trace the roots of, set the
+ ** global variable pr_traceObj to point to it (the header, not the
+ ** java handle), and then call this routine (on Windows, you can set
+ ** a breakpoint at the end of a function that returns void (e.g. dogc)
+ ** and then do a "set next statement" to point to this routine and go.
+ ** This will dump a list of the paths from the roots to the object in
+ ** question to your memory.out file.
+ */
+ PR_DumpToFile("memory.out", "Tracing Roots", PR_TraceRoot1, PR_FALSE);
+}
+
+#endif /* GC_TRACEROOTS */
+
+/******************************************************************************/
+
+#if defined(DEBUG) && defined(WIN32)
+static void DumpApplicationHeap(FILE *out, HANDLE heap)
+{
+ PROCESS_HEAP_ENTRY entry;
+ DWORD err;
+
+ if (!HeapLock(heap))
+ OutputDebugString("Can't lock the heap.\n");
+ entry.lpData = 0;
+ fprintf(out, " address: size ovhd region\n");
+ while (HeapWalk(heap, &entry))
+ {
+ WORD flags = entry.wFlags;
+
+ fprintf(out, "0x%.8X: 0x%.8X 0x%.2X 0x%.2X ", entry.lpData, entry.cbData,
+ entry.cbOverhead, entry.iRegionIndex);
+ if (flags & PROCESS_HEAP_REGION)
+ fprintf(out, "REGION committedSize=0x%.8X uncommittedSize=0x%.8X firstBlock=0x%.8X lastBlock=0x%.8X",
+ entry.Region.dwCommittedSize, entry.Region.dwUnCommittedSize,
+ entry.Region.lpFirstBlock, entry.Region.lpLastBlock);
+ else if (flags & PROCESS_HEAP_UNCOMMITTED_RANGE)
+ fprintf(out, "UNCOMMITTED");
+ else if (flags & PROCESS_HEAP_ENTRY_BUSY)
+ {
+ if (flags & PROCESS_HEAP_ENTRY_DDESHARE)
+ fprintf(out, "DDEShare ");
+ if (flags & PROCESS_HEAP_ENTRY_MOVEABLE)
+ fprintf(out, "Moveable Block handle=0x%.8X", entry.Block.hMem);
+ else
+ fprintf(out, "Block");
+ }
+ fprintf(out, "\n");
+ }
+ if ((err = GetLastError()) != ERROR_NO_MORE_ITEMS)
+ fprintf(out, "ERROR %d iterating through the heap\n", err);
+ if (!HeapUnlock(heap))
+ OutputDebugString("Can't unlock the heap.\n");
+}
+#endif
+
+#if defined(DEBUG) && defined(WIN32)
+static void DumpApplicationHeaps(FILE *out)
+{
+ HANDLE mainHeap;
+ HANDLE heaps[100];
+ DWORD nHeaps;
+ PRInt32 i;
+
+ mainHeap = GetProcessHeap();
+ nHeaps = GetProcessHeaps(100, heaps);
+ if (nHeaps > 100)
+ nHeaps = 0;
+ fprintf(out, "%ld heaps:\n", (long) nHeaps);
+ for (i = 0; i<nHeaps; i++)
+ {
+ HANDLE heap = heaps[i];
+
+ fprintf(out, "Heap at 0x%.8lX", (long) heap);
+ if (heap == mainHeap)
+ fprintf(out, " (main)");
+ fprintf(out, ":\n");
+ DumpApplicationHeap(out, heap);
+ fprintf(out, "\n");
+ }
+ fprintf(out, "End of heap dump\n\n");
+}
+#endif
+
+#if defined(DEBUG) && defined(WIN32)
+PR_IMPLEMENT(void) PR_DumpApplicationHeaps(void)
+{
+ FILE *out;
+
+ OutputDebugString("Dumping heaps...");
+ out = fopen("heaps.out", "a");
+ if (!out)
+ OutputDebugString("Can't open \"heaps.out\"\n");
+ else
+ {
+ struct tm *newtime;
+ time_t aclock;
+
+ time(&aclock);
+ newtime = localtime(&aclock);
+ fprintf(out, "Heap dump on %s\n", asctime(newtime)); /* Print current time */
+ DumpApplicationHeaps(out);
+ fprintf(out, "\n\n");
+ fclose(out);
+ }
+ OutputDebugString(" done\n");
+}
+#else
+
+PR_IMPLEMENT(void) PR_DumpApplicationHeaps(void)
+{
+ fprintf(stderr, "Native heap dumping is currently implemented only for Windows32.\n");
+}
+#endif
+
+/************************************************************************/
+
+/*
+** Scan the freelist bins looking for a big enough chunk of memory to
+** hold "bytes" worth of allocation. "bytes" already has the
+** per-allocation header added to it. Return a pointer to the object with
+** its per-allocation header already prepared.
+*/
+static PRWord *BinAlloc(int cbix, PRInt32 bytes, int dub)
+{
+ GCFreeChunk **cpp, *cp, *cpNext;
+ GCSeg *sp;
+ PRInt32 chunkSize, remainder;
+ PRWord *p, *np;
+ PRInt32 bin, newbin;
+
+ /* Compute bin that allocation belongs in */
+ InlineBinNumber(bin,bytes)
+ if (bin < minBin) {
+ bin = minBin; /* Start at first filled bin */
+ }
+
+ /* Search in the bin, and larger bins, for a big enough piece */
+ for (; bin <= NUM_BINS-1; bin++) {
+ cpp = &bins[bin];
+ while ((cp = *cpp) != 0) {
+ chunkSize = cp->chunkSize;
+ if (chunkSize < bytes) {
+ /* Too small; skip it */
+ METER(meter.skippedFreeChunks++);
+ cpp = &cp->next;
+ continue;
+ }
+
+ /* We have found a hunk of memory large enough to use */
+ p = (PRWord*) cp;
+ sp = cp->segment;
+ cpNext = cp->next;
+#ifndef IS_64
+ if (dub && (((PRWord)p & (PR_BYTES_PER_DWORD-1)) == 0)) {
+ /*
+ * We are double aligning the memory and the current free
+ * chunk is aligned on an even boundary. Because header
+ * words are one word long we need to discard the first
+ * word of memory.
+ */
+ p[0] = MAKE_HEADER(FREE_MEMORY_TYPEIX, 1);
+ SET_HBIT(sp, p);
+ p++;
+ chunkSize -= PR_BYTES_PER_WORD;
+ bytes -= PR_BYTES_PER_WORD;
+ PR_ASSERT(((PRWord)p & (PR_BYTES_PER_DWORD-1)) != 0);
+ _pr_gcData.freeMemory -= PR_BYTES_PER_WORD;
+ _pr_gcData.busyMemory += PR_BYTES_PER_WORD;
+ }
+#endif
+ np = (PRWord*) ((char*) p + bytes);
+ remainder = chunkSize - bytes;
+ if (remainder >= MIN_FREE_CHUNK_BYTES) {
+ /* The left over memory is large enough to be freed. */
+ cp = (GCFreeChunk*) np;
+ cp->segment = sp;
+ cp->chunkSize = remainder;
+ InlineBinNumber(newbin, remainder)
+ if (newbin != bin) {
+ *cpp = (GCFreeChunk*) cpNext; /* remove */
+ cp->next = bins[newbin]; /* insert */
+ bins[newbin] = cp;
+ if (newbin < minBin) minBin = newbin;
+ if (newbin > maxBin) maxBin = newbin;
+ } else {
+ /* Leave it on the same list */
+ cp->next = cpNext;
+ *cpp = (GCFreeChunk*) np;
+ }
+ } else {
+ /*
+ * The left over memory is too small to be released. Just
+ * leave it attached to the chunk of memory being
+ * returned.
+ */
+ *cpp = cpNext;
+ bytes = chunkSize;
+ }
+ p[0] = MAKE_HEADER(cbix, (bytes >> PR_BYTES_PER_WORD_LOG2));
+ SET_HBIT(sp, p);
+ _pr_gcData.freeMemory -= bytes;
+ _pr_gcData.busyMemory += bytes;
+ return p;
+ }
+ }
+ return 0;
+}
+
+/*
+** Allocate a piece of memory that is "big" in it's own segment. Make
+** the object consume the entire segment to avoid fragmentation. When
+** the object is no longer referenced, the segment is freed.
+*/
+static PRWord *BigAlloc(int cbix, PRInt32 bytes, int dub)
+{
+ GCSeg *sp;
+ PRWord *p, h;
+ PRInt32 chunkSize;
+
+ /*
+ ** If the number of bytes allocated via BigAlloc() since the last GC
+ ** exceeds BIG_ALLOC_GC_SIZE then do a GC Now...
+ */
+ if (bigAllocBytes >= BIG_ALLOC_GC_SIZE) {
+ dogc();
+ }
+ bigAllocBytes += bytes;
+
+ /* Get a segment to hold this allocation */
+ sp = GrowHeapExactly(bytes);
+
+ if (sp) {
+ p = (PRWord*) sp->base;
+ chunkSize = sp->limit - sp->base;
+
+ /* All memory is double aligned on 64 bit machines... */
+#ifndef IS_64
+ if (dub && (((PRWord)p & (PR_BYTES_PER_DWORD-1)) == 0)) {
+ /*
+ ** Consume the first word of the chunk with a dummy
+ ** unreferenced object.
+ */
+ p[0] = MAKE_HEADER(FREE_MEMORY_TYPEIX, 1);
+ SET_HBIT(sp, p);
+ p++;
+ chunkSize -= PR_BYTES_PER_WORD;
+ _pr_gcData.freeMemory -= PR_BYTES_PER_WORD;
+ _pr_gcData.busyMemory += PR_BYTES_PER_WORD;
+ PR_ASSERT(((PRWord)p & (PR_BYTES_PER_DWORD-1)) != 0);
+ }
+#endif
+
+#if defined(WIN16)
+ /* All memory MUST be aligned on 32bit boundaries */
+ PR_ASSERT( (((PRWord)p) & (PR_BYTES_PER_WORD-1)) == 0 );
+#endif
+
+ /* Consume the *entire* segment with a single allocation */
+ h = MAKE_HEADER(cbix, (chunkSize >> PR_BYTES_PER_WORD_LOG2));
+ p[0] = h;
+ SET_HBIT(sp, p);
+ _pr_gcData.freeMemory -= chunkSize;
+ _pr_gcData.busyMemory += chunkSize;
+ return p;
+ }
+ return 0;
+}
+
+/* we disable gc allocation during low memory conditions */
+static PRBool allocationEnabled = PR_TRUE;
+
+PR_IMPLEMENT(void) PR_EnableAllocation(PRBool yesOrNo)
+{
+ allocationEnabled = yesOrNo;
+}
+
+static void CollectorCleanup(void) {
+ while (collectorCleanupNeeded) {
+ LOCK_GC();
+ collectorCleanupNeeded = 0;
+ UNLOCK_GC();
+ if (freeSegs) {
+ FreeSegments();
+ }
+ if (!WEAK_FREELIST_ISEMPTY()) {
+ EmptyWeakFreeList();
+ }
+ }
+}
+
+/******************************************************************************/
+
+#ifdef GC_CHECK
+static PRInt32 allocationCount;
+
+static void EarthShatteringKaBoom(PRInt32 whichOne) {
+ long* p = 0;
+ *p = 0;
+}
+
+/* Check a segment of heap memory. Verify that the object memory
+ hasn't been overwritten (past the end at least) */
+static void CheckSegment(GCSeg* sp) {
+ PRWord h, tix;
+ PRWord *p, *lastp, *np, *limit;
+
+ lastp = p = (PRWord *) sp->base;
+ limit = (PRWord *) sp->limit;
+ while (p < limit) {
+ if (IS_HBIT(sp, p)) {
+ char *cp, i;
+ GCBlockEnd* end;
+ PRWord bytes, requestedBytes;
+
+ h = p[0];
+ tix = GET_TYPEIX(h);
+ bytes = OBJ_BYTES(h);
+ np = (PRWord *) ((char *)p + bytes);
+ if (tix != FREE_MEMORY_TYPEIX) {
+ PRInt32 test; /* msdev get's fooled without this local */
+ /* A live object is here. The last word in the object will
+ contain the objects requestedSize */
+ end = (GCBlockEnd*)((char*)(p) + bytes - sizeof(GCBlockEnd));
+ test = end->check;
+ if (test != PR_BLOCK_END) {
+ PR_ASSERT(test == PR_BLOCK_END);
+ }
+ requestedBytes = end->requestedBytes;
+ if (requestedBytes >= bytes) EarthShatteringKaBoom(0);
+ cp = (char*)(p + 1) + requestedBytes;
+ i = (char) 0xff;
+ while (cp < (char*)end) {
+ if (*cp != i) EarthShatteringKaBoom(1);
+ cp++;
+ i--;
+ }
+ }
+ lastp = p;
+ p = np;
+ } else {
+ /* Must be a freelist item */
+ GCFreeChunk *cp = (GCFreeChunk*) p;
+ if ((PRInt32)cp->chunkSize < (PRInt32)sizeof(GCFreeChunk)) {
+ EarthShatteringKaBoom(3);
+ }
+ lastp = p;
+ p = (PRWord*) ((char*)p + cp->chunkSize);
+ }
+ }
+}
+
+static void CheckHeap(void) {
+ GCSeg *sp = segs;
+ GCSeg *esp = sp + nsegs;
+ while (sp < esp) {
+ CheckSegment(sp);
+ sp++;
+ }
+}
+
+#endif /* GC_CHECK */
+
+/******************************************************************************/
+
+#ifdef DEBUG
+long gc_thrash = -1L;
+#endif
+
+/*
+** Allocate memory from the GC Heap. Performs garbage collections if
+** memory gets tight and grows the heap as needed. May return NULL if
+** memory cannot be found.
+*/
+PR_IMPLEMENT(PRWord GCPTR *)PR_AllocMemory(
+ PRWord requestedBytes, PRInt32 tix, PRWord flags)
+{
+ PRWord *p;
+ CollectorType *ct;
+ PRInt32 bytes;
+ GCFinal *final = 0;
+ GCWeak *weak = 0;
+ int dub = flags & PR_ALLOC_DOUBLE;
+ PRInt32 objBytes;
+#ifdef GC_STATS
+ PRInt64 allocTime, ldelta;
+#endif
+
+ if (!allocationEnabled) return NULL;
+
+ PR_ASSERT(requestedBytes >= 0);
+ PR_ASSERT(_pr_collectorTypes[tix].flags != 0);
+
+#ifdef DEBUG
+ if (_pr_do_a_dump) {
+ /*
+ ** Collect, pause for a second (lets finalizer run), and then GC
+ ** again.
+ */
+ PR_GC();
+ PR_Sleep(PR_MicrosecondsToInterval(1000000L));
+ PR_GC();
+ PR_DumpGCHeap(_pr_dump_file, PR_TRUE);
+ _pr_do_a_dump = 0;
+ }
+#endif
+
+#ifdef GC_STATS
+ allocTime = PR_Now();
+#endif
+ bytes = (PRInt32) requestedBytes;
+
+ /*
+ ** Align bytes to a multiple of a PRWord, then add in enough space
+ ** to hold the header word.
+ **
+ ** MSVC 1.52 crashed on the ff. code because of the "complex" shifting :-(
+ */
+#if !defined(WIN16)
+ /* Check for possible overflow of bytes before performing add */
+ if ((MAX_INT - PR_BYTES_PER_WORD) < bytes ) return NULL;
+ bytes = (bytes + PR_BYTES_PER_WORD - 1) >> PR_BYTES_PER_WORD_LOG2;
+ bytes <<= PR_BYTES_PER_WORD_LOG2;
+ /* Check for possible overflow of bytes before performing add */
+ if ((MAX_INT - sizeof(PRWord)) < bytes ) return NULL;
+ bytes += sizeof(PRWord);
+#else
+ /*
+ ** For WIN16 the shifts have been broken out into separate statements
+ ** to prevent the compiler from crashing...
+ */
+ {
+ PRWord shiftVal;
+
+ /* Check for possible overflow of bytes before performing add */
+ if ((MAX_INT - PR_BYTES_PER_WORD) < bytes ) return NULL;
+ bytes += PR_BYTES_PER_WORD - 1L;
+ shiftVal = PR_BYTES_PER_WORD_LOG2;
+ bytes >>= shiftVal;
+ bytes <<= shiftVal;
+ /* Check for possible overflow of bytes before performing add */
+ if ((MAX_INT - sizeof(PRWord)) < bytes ) return NULL;
+ bytes += sizeof(PRWord);
+ }
+#endif
+ /*
+ * Add in an extra word of memory for double-aligned memory. Some
+ * percentage of the time this will waste a word of memory (too
+ * bad). Howver, it makes the allocation logic much simpler and
+ * faster.
+ */
+#ifndef IS_64
+ if (dub) {
+ /* Check for possible overflow of bytes before performing add */
+ if ((MAX_INT - PR_BYTES_PER_WORD) < bytes ) return NULL;
+ bytes += PR_BYTES_PER_WORD;
+ }
+#endif
+
+#ifdef GC_CHECK
+ if (_pr_gcData.flags & GC_CHECK) {
+ /* Bloat the allocation a bit so that we can lay down
+ a check pattern that we will validate */
+ /* Check for possible overflow of bytes before performing add */
+ if ((MAX_INT - PR_BYTES_PER_WORD * 3) < bytes ) return NULL;
+ bytes += PR_BYTES_PER_WORD * 3;
+ }
+#endif
+
+#if defined(GC_CHECK) || defined(GC_STATS) || defined(GC_TRACEROOTS)
+ if ((MAX_INT - sizeof(GCBlockEnd)) < bytes ) return NULL;
+ bytes += sizeof(GCBlockEnd);
+#endif
+
+ PR_ASSERT( bytes < MAX_ALLOC_SIZE );
+ /*
+ ** Java can ask for objects bigger than MAX_ALLOC_SIZE,
+ ** but it won't get them.
+ */
+ if (bytes >= MAX_ALLOC_SIZE) return NULL;
+
+#ifdef DEBUG
+ if (gc_thrash == -1L ? (gc_thrash = (long)PR_GetEnv("GC_THRASH")):gc_thrash) PR_GC();
+#endif
+
+ ct = &_pr_collectorTypes[tix];
+ if (ct->flags & (_GC_TYPE_FINAL|_GC_TYPE_WEAK)) {
+ if (0 != ct->gctype.finalize) {
+ /*
+ ** Allocate a GCFinal struct for this object in advance. Don't put
+ ** it on the pending list until we have allocated the object
+ */
+ final = AllocFinalNode();
+ if (!final) {
+ /* XXX THIS IS NOT ACCEPTABLE*/
+ PR_ASSERT(0);
+ return 0;
+ }
+ }
+ if (0 != ct->gctype.getWeakLinkOffset) {
+ /*
+ ** Allocate a GCWeak struct for this object in advance. Don't put
+ ** it on the weak links list until we have allocated the object
+ */
+ weak = AllocWeakNode();
+ if (!weak) {
+ /* XXX THIS IS NOT ACCEPTABLE*/
+ if (0 != final) {
+ FreeFinalNode(final);
+ }
+ PR_ASSERT(0);
+ return 0;
+ }
+ }
+ }
+
+ LOCK_GC();
+#ifdef GC_CHECK
+ if (_pr_gcData.flags & GC_CHECK) CheckHeap();
+ allocationCount++;
+#endif
+
+ /* Check for overflow of maximum size we can handle */
+ if (bytes > MAX_ALLOC) goto lost;
+
+ /* Try default allocation */
+ p = ((bytes >= BIG_ALLOC) && (nsegs < MAX_SEGS)) ?
+ BigAlloc(tix, bytes, dub) : BinAlloc(tix, bytes, dub);
+ if (0 == p) {
+#ifdef GC_STATS
+ LL_SUB(ldelta, PR_Now(), allocTime);
+#endif
+ /* Collect some memory */
+ _GCTRACE(GC_ALLOC, ("force GC: want %d", bytes));
+ dogc();
+ PR_ASSERT( GC_IS_LOCKED() );
+
+ /* After a collection we check and see if we should grow the
+ ** heap. We grow the heap when the amount of memory free is less
+ ** than a certain percentage of the heap size. We don't check to
+ ** see if the grow succeeded because our fallback strategy in
+ ** either case is to try one more time to allocate. */
+ if ((_pr_gcData.allocMemory < _pr_gcData.maxMemory)
+ && ((_pr_gcData.freeMemory <
+ ((_pr_gcData.allocMemory * MIN_FREE_THRESHOLD_AFTER_GC) / 100L))
+ || (_pr_gcData.freeMemory < bytes))) {
+ GrowHeap(PR_MAX(bytes, segmentSize));
+ }
+#ifdef GC_STATS
+ LL_ADD(allocTime, PR_Now(), ldelta);
+#endif
+
+ /* Try again */
+ p = ((bytes >= BIG_ALLOC) && (nsegs < MAX_SEGS)) ?
+ BigAlloc(tix, bytes, dub) : BinAlloc(tix, bytes, dub);
+ if (0 == p) {
+ /* Well that lost big time. Memory must be pretty well fragmented */
+ if (!GrowHeap(PR_MAX(bytes, segmentSize))) goto lost;
+ p = BinAlloc(tix, bytes, dub);
+ if (0 == p) goto lost;
+ }
+ }
+
+ /* Zero out the portion of the object memory that was used by
+ the GCFreeChunk structure (skip the first word because it
+ was already overwritten by the gc header word) */
+ objBytes = OBJ_BYTES(p[0]);
+ if (objBytes > sizeof(PRWord)) p[1] = 0;
+ if (objBytes > sizeof(PRWord)*2) p[2] = 0;
+
+ if (final) {
+ _GCTRACE(GC_ALLOC, ("alloc 0x%x (%d) final=0x%x",
+ p, bytes, final));
+ final->object = p;
+ PR_APPEND_LINK(&final->links, &_pr_finalizeableObjects);
+ } else {
+ _GCTRACE(GC_ALLOC, ("alloc 0x%x (%d)", p, bytes));
+ }
+ if (weak) {
+ weak->object = p;
+ PR_APPEND_LINK(&weak->links, &_pr_weakLinks);
+ }
+ METER(meter.allocBytes += bytes);
+ METER(meter.wastedBytes += (bytes - requestedBytes));
+ UNLOCK_GC();
+
+ if (collectorCleanupNeeded) {
+ CollectorCleanup();
+ }
+
+#if defined(GC_CHECK) || defined(GC_STATS) || defined(GC_TRACEROOTS)
+ {
+ GCBlockEnd* end = (GCBlockEnd*)((char*)p + OBJ_BYTES(p[0]) - sizeof(GCBlockEnd));
+ end->check = PR_BLOCK_END;
+ }
+#endif
+#ifdef GC_STATS
+ {
+ PRInt64 now = PR_Now();
+ double delta;
+ PRInt32 bin;
+ GCBlockEnd* end = (GCBlockEnd*)((char*)p + OBJ_BYTES(p[0]) - sizeof(GCBlockEnd));
+
+ end->allocTime = allocTime;
+ LL_SUB(ldelta, now, allocTime);
+ LL_L2D(delta, ldelta);
+ InlineBinNumber(bin, requestedBytes);
+ end->bin = bin;
+ gcstats[bin].nallocs++;
+ gcstats[bin].allocTime += delta;
+ gcstats[bin].allocTimeVariance += delta * delta;
+ }
+#endif
+#ifdef GC_CHECK
+ if (_pr_gcData.flags & GC_CHECK) {
+ /* Place a pattern in the memory that was allocated that was not
+ requested. We will check the pattern later. */
+ char* cp = (char*)(p + 1) + requestedBytes;
+ GCBlockEnd* end = (GCBlockEnd*)((char*)p + OBJ_BYTES(p[0]) - sizeof(GCBlockEnd));
+ char i = (char) 0xff;
+ while (cp < (char*)end) {
+ *cp++ = i--;
+ }
+ end->requestedBytes = requestedBytes;
+ CheckHeap();
+ }
+#endif
+ return p + 1;
+
+ lost:
+ /* Out of memory */
+ UNLOCK_GC();
+ if (final) {
+ FreeFinalNode(final);
+ }
+ if (weak) {
+ FreeWeakNode(weak);
+ }
+ if (collectorCleanupNeeded) {
+ CollectorCleanup();
+ }
+ return 0;
+}
+
+/* Shortcut allocator for objects that do not require finalization or
+ are weak objects */
+PR_IMPLEMENT(PRWord GCPTR *)
+PR_AllocSimpleMemory(PRWord requestedBytes, PRInt32 tix)
+{
+ PRWord *p;
+ PRInt32 bytes;
+ PRInt32 objBytes;
+#ifdef GC_STATS
+ PRInt64 allocTime, ldelta;
+#endif
+
+ if (!allocationEnabled) return NULL;
+
+ PR_ASSERT(requestedBytes >= 0);
+ PR_ASSERT(_pr_collectorTypes[tix].flags != 0);
+
+#ifdef DEBUG
+ if (_pr_do_a_dump) {
+ /*
+ ** Collect, pause for a second (lets finalizer run), and then GC
+ ** again.
+ */
+ PR_GC();
+ PR_Sleep(PR_MicrosecondsToInterval(1000000L));
+ PR_GC();
+ PR_DumpGCHeap(_pr_dump_file, PR_TRUE);
+ _pr_do_a_dump = 0;
+ }
+#endif
+
+#ifdef GC_STATS
+ allocTime = PR_NowMS();
+#endif
+ bytes = (PRInt32) requestedBytes;
+
+ /*
+ ** Align bytes to a multiple of a PRWord, then add in enough space
+ ** to hold the header word.
+ **
+ ** MSVC 1.52 crashed on the ff. code because of the "complex" shifting :-(
+ */
+#if !defined(WIN16)
+ bytes = (bytes + PR_BYTES_PER_WORD - 1) >> PR_BYTES_PER_WORD_LOG2;
+ bytes <<= PR_BYTES_PER_WORD_LOG2;
+ bytes += sizeof(PRWord);
+#else
+ /*
+ ** For WIN16 the shifts have been broken out into separate statements
+ ** to prevent the compiler from crashing...
+ */
+ {
+ PRWord shiftVal;
+
+ bytes += PR_BYTES_PER_WORD - 1L;
+ shiftVal = PR_BYTES_PER_WORD_LOG2;
+ bytes >>= shiftVal;
+ bytes <<= shiftVal;
+ bytes += sizeof(PRWord);
+ }
+#endif
+
+ /*
+ * Add in an extra word of memory for double-aligned memory. Some
+ * percentage of the time this will waste a word of memory (too
+ * bad). Howver, it makes the allocation logic much simpler and
+ * faster.
+ */
+#ifndef IS_64
+ bytes += PR_BYTES_PER_WORD;
+#endif
+
+#ifdef GC_CHECK
+ if (_pr_gcData.flags & GC_CHECK) {
+ /* Bloat the allocation a bit so that we can lay down
+ a check pattern that we will validate */
+ bytes += PR_BYTES_PER_WORD * 2;
+ }
+#endif
+
+#if defined(GC_CHECK) || defined(GC_STATS) || defined(GC_TRACEROOTS)
+ bytes += sizeof(GCBlockEnd);
+#endif
+
+#if defined(WIN16)
+ PR_ASSERT( bytes < MAX_ALLOC_SIZE );
+#endif
+ /* Java can ask for objects bigger than 4M, but it won't get them */
+ /*
+ * This check was added because there is a fundamental limit of
+ * the size field maintained by the gc code. Going over the 4M
+ * limit caused some bits to roll over into another bit field,
+ * violating the max segment size and causing a bug.
+ */
+ if (bytes >= MAX_ALLOC_SIZE) {
+ return NULL;
+ }
+#ifdef DEBUG
+ if (gc_thrash == -1L
+ ? (gc_thrash = (long)PR_GetEnv("GC_THRASH"))
+ : gc_thrash) {
+ PR_GC();
+ }
+#endif
+
+ LOCK_GC();
+#ifdef GC_CHECK
+ if (_pr_gcData.flags & GC_CHECK) {
+ CheckHeap();
+ }
+ allocationCount++;
+#endif
+
+ /* Try default allocation */
+ if ((bytes >= BIG_ALLOC) && (nsegs < MAX_SEGS)) {
+ p = BigAlloc(tix, bytes, 1);
+ } else {
+ p = BinAlloc(tix, bytes, 1);
+ }
+ if (0 == p) {
+#ifdef GC_STATS
+ LL_SUB(ldelta, PR_Now(), allocTime);
+#endif
+ /* Collect some memory */
+ _GCTRACE(GC_ALLOC, ("force GC: want %d", bytes));
+ dogc();
+ PR_ASSERT( GC_IS_LOCKED() );
+
+ /* After a collection we check and see if we should grow the
+ heap. We grow the heap when the amount of memory free is less
+ than a certain percentage of the heap size. We don't check to
+ see if the grow succeeded because our fallback strategy in
+ either case is to try one more time to allocate. */
+ if ((_pr_gcData.allocMemory < _pr_gcData.maxMemory) &&
+ (_pr_gcData.freeMemory <
+ ((_pr_gcData.allocMemory * MIN_FREE_THRESHOLD_AFTER_GC) / 100L))) {
+ GrowHeap(PR_MAX(bytes, segmentSize));
+ }
+#ifdef GC_STATS
+ LL_ADD(allocTime, PR_Now(), ldelta);
+#endif
+
+ /* Try one last time */
+ if ((bytes >= BIG_ALLOC) && (nsegs < MAX_SEGS)) {
+ p = BigAlloc(tix, bytes, 1);
+ } else {
+ p = BinAlloc(tix, bytes, 1);
+ }
+ if (0 == p) {
+ /* Well that lost big time. Memory must be pretty well fragmented */
+ if (!GrowHeap(PR_MAX(bytes, segmentSize))) {
+ goto lost;
+ }
+ p = BinAlloc(tix, bytes, 1);
+ if (0 == p) goto lost;
+ }
+ }
+
+ /* Zero out the portion of the object memory that was used by
+ the GCFreeChunk structure (skip the first word because it
+ was already overwritten by the gc header word) */
+ objBytes = OBJ_BYTES(p[0]);
+ if (objBytes > sizeof(PRWord)) p[1] = 0;
+ if (objBytes > sizeof(PRWord)*2) p[2] = 0;
+
+ METER(meter.allocBytes += bytes);
+ METER(meter.wastedBytes += (bytes - requestedBytes));
+ UNLOCK_GC();
+
+ if (collectorCleanupNeeded) {
+ CollectorCleanup();
+ }
+
+#if defined(GC_CHECK) || defined(GC_STATS) || defined(GC_TRACEROOTS)
+ {
+ GCBlockEnd* end = (GCBlockEnd*)((char*)p + OBJ_BYTES(p[0]) - sizeof(GCBlockEnd));
+ end->check = PR_BLOCK_END;
+ }
+#endif
+#ifdef GC_STATS
+ {
+ PRInt64 now = PR_Now();
+ double delta;
+ PRInt32 bin;
+ GCBlockEnd* end = (GCBlockEnd*)((char*)p + OBJ_BYTES(p[0]) - sizeof(GCBlockEnd));
+
+ end->allocTime = allocTime;
+ LL_SUB(ldelta, now, allocTime);
+ LL_L2D(delta, ldelta);
+ InlineBinNumber(bin, requestedBytes);
+ end->bin = bin;
+ gcstats[bin].nallocs++;
+ gcstats[bin].allocTime += delta;
+ gcstats[bin].allocTimeVariance += delta * delta;
+ }
+#endif
+#ifdef GC_CHECK
+ if (_pr_gcData.flags & GC_CHECK) {
+ /* Place a pattern in the memory that was allocated that was not
+ requested. We will check the pattern later. */
+ char* cp = (char*)(p + 1) + requestedBytes;
+ GCBlockEnd* end = (GCBlockEnd*)((char*)p + OBJ_BYTES(p[0]) - sizeof(GCBlockEnd));
+ char i = (char) 0xff;
+ while (cp < (char*)end) {
+ *cp++ = i--;
+ }
+ end->requestedBytes = requestedBytes;
+ CheckHeap();
+ }
+#endif
+ return p + 1;
+
+ lost:
+ /* Out of memory */
+ UNLOCK_GC();
+ if (collectorCleanupNeeded) {
+ CollectorCleanup();
+ }
+ return 0;
+}
+
+/************************************************************************/
+
+PR_IMPLEMENT(PRWord) PR_GetObjectHeader(void *ptr) {
+ GCSeg *sp;
+ PRWord *h;
+
+ if (ptr == 0) return 0;
+ sp = InHeap(ptr);
+ if (sp == 0) return 0;
+ h = (PRWord*)FindObject(sp, (PRWord*)ptr);
+ return GC_GET_USER_BITS(h[0]);
+}
+
+PR_IMPLEMENT(PRWord) PR_SetObjectHeader(void *ptr, PRWord newUserBits) {
+ GCSeg *sp;
+ PRWord *h, rv;
+
+ if (ptr == 0) return 0;
+ sp = InHeap(ptr);
+ if (sp == 0) return 0;
+ h = (PRWord*)FindObject(sp, (PRWord*)ptr);
+ rv = GC_GET_USER_BITS(h[0]);
+ h[0] = (h[0] & ~GC_USER_BITS) |
+ ((newUserBits << GC_USER_BITS_SHIFT) & GC_USER_BITS);
+ return rv;
+}
+
+PR_IMPLEMENT(void) PR_InitGC(
+ PRWord flags, PRInt32 initialHeapSize, PRInt32 segSize, PRThreadScope scope)
+{
+ static char firstTime = 1;
+
+ if (!firstTime) return;
+ firstTime = 0;
+
+ _pr_msgc_lm = PR_NewLogModule("msgc");
+ _pr_pageShift = PR_GetPageShift();
+ _pr_pageSize = PR_GetPageSize();
+
+#if defined(WIN16)
+ PR_ASSERT( initialHeapSize < MAX_ALLOC_SIZE );
+#endif
+
+ /* Setup initial heap size and initial segment size */
+ if (0 != segSize) segmentSize = segSize;
+#ifdef DEBUG
+ GC = PR_NewLogModule("GC");
+ {
+ char *ev = PR_GetEnv("GC_SEGMENT_SIZE");
+ if (ev && ev[0]) {
+ PRInt32 newSegmentSize = atoi(ev);
+ if (0 != newSegmentSize) segmentSize = newSegmentSize;
+ }
+ ev = PR_GetEnv("GC_INITIAL_HEAP_SIZE");
+ if (ev && ev[0]) {
+ PRInt32 newInitialHeapSize = atoi(ev);
+ if (0 != newInitialHeapSize) initialHeapSize = newInitialHeapSize;
+ }
+ ev = PR_GetEnv("GC_FLAGS");
+ if (ev && ev[0]) {
+ flags |= atoi(ev);
+ }
+#ifdef GCMETER
+ ev = PR_GetEnv("GC_METER");
+ if (ev && ev[0]) {
+ _pr_gcMeter = atoi(ev);
+ }
+#endif
+ }
+#endif
+ if (0 == initialHeapSize) initialHeapSize = segmentSize;
+ if (initialHeapSize < segmentSize) initialHeapSize = segmentSize;
+
+ _pr_gcData.maxMemory = MAX_SEGS * segmentSize;
+ _pr_gcData.liveBlock = ProcessRootBlock;
+ _pr_gcData.livePointer = ProcessRootPointer;
+ _pr_gcData.processRootBlock = ProcessRootBlock;
+ _pr_gcData.processRootPointer = ProcessRootPointer;
+ _pr_gcData.dumpOutput = NULL;
+
+ PR_INIT_CLIST(&_pr_finalizeableObjects);
+ PR_INIT_CLIST(&_pr_finalQueue);
+ _PR_InitGC(flags);
+
+ /* Create finalizer thread */
+ _PR_CreateFinalizer(scope);
+
+ /* Allocate the initial segment for the heap */
+ minBin = 31;
+ maxBin = 0;
+ GrowHeap(initialHeapSize);
+ PR_RegisterRootFinder(ScanWeakFreeList, "scan weak free list", 0);
+}
+
+#if defined(WIN16)
+/*
+** For WIN16 the GC_IN_HEAP() macro must call the private InHeap function.
+** This public wrapper function makes this possible...
+*/
+PR_IMPLEMENT(PRBool)
+PR_GC_In_Heap(void *object)
+{
+ return InHeap( object ) != NULL;
+}
+#endif
+
+
+/** Added by Vishy for sanity checking a few GC structures **/
+/** Can use SanityCheckGC to debug corrupted GC Heap situations **/
+
+#ifdef DEBUG
+
+static int SegmentOverlaps(int i, int j)
+{
+ return
+ (((segs[i].limit > segs[j].base) && (segs[i].base < segs[j].base)) ||
+ ((segs[j].limit > segs[i].base) && (segs[j].base < segs[i].base)));
+}
+
+static void NoSegmentOverlaps(void)
+{
+ int i,j;
+
+ for (i = 0; i < nsegs; i++)
+ for (j = i+1 ; j < nsegs ; j++)
+ PR_ASSERT(!SegmentOverlaps(i,j));
+}
+
+static void SegInfoCheck(void)
+{
+ int i;
+ for (i = 0 ; i < nsegs ; i++)
+ PR_ASSERT((segs[i].info->hbits) &&
+ (segs[i].info->hbits == segs[i].hbits) &&
+ (segs[i].info->base == segs[i].base) &&
+ (segs[i].info->limit == segs[i].limit));
+}
+
+static void SanityCheckGC()
+{
+ NoSegmentOverlaps();
+ SegInfoCheck();
+}
+
+#endif
+
+#if defined(DEBUG) && defined(WIN32)
+
+extern void *baseaddr;
+extern void *lastaddr;
+
+PR_IMPLEMENT(void)
+PR_PrintGCStats(void)
+{
+ long reportedSegSpace = _pr_gcData.busyMemory + _pr_gcData.freeMemory;
+ char* msg;
+ long largeCount = 0, largeSize = 0;
+ long segCount = 0, segSize = 0;
+ long freeCount = 0, freeSize = 0;
+ GCSeg *sp, *esp;
+ GCSegInfo* si;
+
+ LOCK_GC();
+
+ sp = segs;
+ esp = sp + nsegs;
+ while (sp < esp) {
+ long size = sp->info->limit - sp->info->base;
+ segCount++;
+ segSize += size;
+ if (sp->info->fromMalloc) {
+ largeCount++;
+ largeSize += size;
+ }
+ sp++;
+ }
+
+ si = freeSegs;
+ while (si != NULL) {
+ long size = si->limit - si->base;
+ freeCount++;
+ freeSize += size;
+ si = si->next;
+ }
+
+ msg = PR_smprintf("\
+# GC Stats:\n\
+# vm space:\n\
+# range: %ld - %ld\n\
+# size: %ld\n\
+# segments:\n\
+# range: %ld - %ld\n\
+# count: %ld (reported: %ld)\n\
+# size: %ld (reported: %ld)\n\
+# free count: %ld\n\
+# free size: %ld\n\
+# busy objs: %ld (%ld%%)\n\
+# free objs: %ld (%ld%%)\n\
+# large blocks:\n\
+# count: %ld\n\
+# total size: %ld (%ld%%)\n\
+# avg size: %ld\n\
+",
+ /* vm space */
+ (long)baseaddr, (long)lastaddr,
+ (long)lastaddr - (long)baseaddr,
+ /* segments */
+ _pr_gcData.lowSeg, _pr_gcData.highSeg,
+ segCount, nsegs,
+ segSize, reportedSegSpace,
+ freeCount,
+ freeSize,
+ _pr_gcData.busyMemory,
+ (_pr_gcData.busyMemory * 100 / reportedSegSpace),
+ _pr_gcData.freeMemory,
+ (_pr_gcData.freeMemory * 100 / reportedSegSpace),
+ /* large blocks */
+ largeCount,
+ largeSize, (largeSize * 100 / reportedSegSpace),
+ (largeCount ? largeSize / largeCount : 0)
+ );
+ UNLOCK_GC();
+ fprintf(stderr, msg);
+ OutputDebugString(msg);
+ PR_smprintf_free(msg);
+#ifdef GC_STATS
+ PR_PrintGCAllocStats();
+#endif
+}
+#endif
+
+PR_IMPLEMENT(void)
+PR_DumpToFile(char* filename, char* msg, PRFileDumper dump, PRBool detailed)
+{
+ FILE *out;
+ OutputDebugString(msg);
+ out = fopen(filename, "a");
+ if (!out) {
+ char buf[64];
+ PR_ASSERT(strlen(filename) < sizeof(buf) - 16);
+ PR_snprintf(buf, sizeof(buf), "Can't open \"%s\"\n",
+ filename);
+ OutputDebugString(buf);
+ }
+ else
+ {
+ struct tm *newtime;
+ time_t aclock;
+ int i;
+
+ time(&aclock);
+ newtime = localtime(&aclock);
+ fprintf(out, "%s on %s\n", msg, asctime(newtime)); /* Print current time */
+ dump(out, detailed);
+ fprintf(out, "\n\n");
+ for (i = 0; i < 80; i++)
+ fprintf(out, "=");
+ fprintf(out, "\n\n");
+ fclose(out);
+ }
+ OutputDebugString(" done\n");
+}
+
diff --git a/lib/msgc/src/unixgc.c b/lib/msgc/src/unixgc.c
new file mode 100644
index 00000000..1c36f65b
--- /dev/null
+++ b/lib/msgc/src/unixgc.c
@@ -0,0 +1,136 @@
+/* -*- 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 "prlock.h"
+#include "prlog.h"
+#include "prmem.h"
+#include "gcint.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#define _PR_GC_VMBASE 0x40000000
+
+#if defined(SOLARIS)
+#define _MD_MMAP_FLAGS MAP_SHARED
+#elif defined(RELIANTUNIX)
+#define _MD_MMAP_FLAGS MAP_PRIVATE|MAP_FIXED
+#else
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+#endif
+
+static PRInt32 zero_fd = -1;
+static PRLock *zero_fd_lock = NULL;
+
+void _MD_InitGC(void)
+{
+#ifdef DEBUG
+ /*
+ * Disable using mmap(2) if NSPR_NO_MMAP is set
+ */
+ if (getenv("NSPR_NO_MMAP")) {
+ zero_fd = -2;
+ return;
+ }
+#endif
+ zero_fd = open("/dev/zero",O_RDWR , 0);
+ zero_fd_lock = PR_NewLock();
+}
+
+/* This static variable is used by _MD_GrowGCHeap and _MD_ExtendGCHeap */
+static void *lastaddr = (void*) _PR_GC_VMBASE;
+
+void *_MD_GrowGCHeap(PRUint32 *sizep)
+{
+ void *addr;
+ PRUint32 size;
+
+ size = *sizep;
+
+ PR_Lock(zero_fd_lock);
+ if (zero_fd < 0) {
+ goto mmap_loses;
+ }
+
+ /* Extend the mapping */
+ addr = mmap(lastaddr, size, PROT_READ|PROT_WRITE|PROT_EXEC,
+ _MD_MMAP_FLAGS,
+ zero_fd, 0);
+ if (addr == (void*)-1) {
+ zero_fd = -1;
+ goto mmap_loses;
+ }
+ lastaddr = ((char*)addr + size);
+#ifdef DEBUG
+ PR_LOG(_pr_msgc_lm, PR_LOG_WARNING,
+ ("GC: heap extends from %08x to %08x\n",
+ _PR_GC_VMBASE,
+ _PR_GC_VMBASE + (char*)lastaddr - (char*)_PR_GC_VMBASE));
+#endif
+ PR_Unlock(zero_fd_lock);
+ return addr;
+
+mmap_loses:
+ PR_Unlock(zero_fd_lock);
+ return PR_MALLOC(size);
+}
+
+/* XXX - This is disabled. MAP_FIXED just does not work. */
+#if 0
+PRBool _MD_ExtendGCHeap(char *base, PRInt32 oldSize, PRInt32 newSize) {
+ PRBool rv = PR_FALSE;
+ void* addr;
+ PRInt32 allocSize = newSize - oldSize;
+
+ PR_Lock(zero_fd_lock);
+ addr = mmap(base + oldSize, allocSize, PROT_READ|PROT_WRITE|PROT_EXEC,
+ _MD_MMAP_FLAGS | MAP_FIXED, zero_fd, 0);
+ if (addr == (void*)-1) {
+ goto loser;
+ }
+ if (addr != (void*) (base + oldSize)) {
+ munmap(base + oldSize, allocSize);
+ goto loser;
+ }
+ lastaddr = ((char*)base + newSize);
+ PR_LOG(_pr_msgc_lm, PR_LOG_ALWAYS,
+ ("GC: heap now extends from %p to %p",
+ base, base + newSize));
+ rv = PR_TRUE;
+
+loser:
+ PR_Unlock(zero_fd_lock);
+ return rv;
+}
+#else
+PRBool _MD_ExtendGCHeap(char *base, PRInt32 oldSize, PRInt32 newSize) {
+ return PR_FALSE;
+}
+#endif
+
+void _MD_FreeGCSegment(void *base, PRInt32 len)
+{
+ if (zero_fd < 0) {
+ PR_DELETE(base);
+ } else {
+ (void) munmap(base, len);
+ }
+}
diff --git a/lib/msgc/src/win16gc.c b/lib/msgc/src/win16gc.c
new file mode 100644
index 00000000..86c67a75
--- /dev/null
+++ b/lib/msgc/src/win16gc.c
@@ -0,0 +1,57 @@
+/* -*- 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(WIN16)
+#include <windows.h>
+#endif
+#include "prtypes.h"
+#include <stdlib.h>
+
+#define MAX_SEGMENT_SIZE (65536l - 4096l)
+
+/************************************************************************/
+/*
+** Machine dependent GC Heap management routines:
+** _MD_GrowGCHeap
+*/
+/************************************************************************/
+
+void _MD_InitGC(void) {}
+
+extern void *
+_MD_GrowGCHeap(PRUint32 *sizep)
+{
+ void *addr;
+
+ if( *sizep > MAX_SEGMENT_SIZE ) {
+ *sizep = MAX_SEGMENT_SIZE;
+ }
+
+ addr = malloc((size_t)*sizep);
+ return addr;
+}
+
+HINSTANCE _pr_hInstance;
+
+int CALLBACK LibMain( HINSTANCE hInst, WORD wDataSeg,
+ WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ _pr_hInstance = hInst;
+ return TRUE;
+}
+
+
diff --git a/lib/msgc/src/win32gc.c b/lib/msgc/src/win32gc.c
new file mode 100644
index 00000000..4f916b53
--- /dev/null
+++ b/lib/msgc/src/win32gc.c
@@ -0,0 +1,110 @@
+/* -*- 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 "prlog.h"
+
+extern PRLogModuleInfo* _pr_msgc_lm;
+
+#define GC_VMBASE 0x40000000
+#define GC_VMLIMIT 0x00FFFFFF
+
+/************************************************************************/
+/*
+** Machine dependent GC Heap management routines:
+** _MD_GrowGCHeap
+*/
+/************************************************************************/
+
+void *baseaddr = (void*) GC_VMBASE;
+void *lastaddr = (void*) GC_VMBASE;
+
+void _MD_InitGC() {}
+
+void *_MD_GrowGCHeap(PRUint32 *sizep)
+{
+ void *addr;
+ size_t size;
+
+ /* Reserve a block of memory for the GC */
+ if( lastaddr == baseaddr ) {
+ addr = VirtualAlloc( (void *)GC_VMBASE, GC_VMLIMIT, MEM_RESERVE, PAGE_READWRITE );
+
+ /*
+ ** If the GC_VMBASE address is already mapped, then let the OS choose a
+ ** base address that is available...
+ */
+ if (addr == NULL) {
+ addr = VirtualAlloc( NULL, GC_VMLIMIT, MEM_RESERVE, PAGE_READWRITE );
+
+ baseaddr = lastaddr = addr;
+ if (addr == NULL) {
+ PR_LOG(_pr_msgc_lm, PR_LOG_ALWAYS, ("GC: unable to allocate heap: LastError=%ld",
+ GetLastError()));
+ return 0;
+ }
+ }
+ }
+ size = *sizep;
+
+ /* Extend the mapping */
+ addr = VirtualAlloc( lastaddr, size, MEM_COMMIT, PAGE_READWRITE );
+ if (addr == NULL) {
+ return 0;
+ }
+
+ lastaddr = ((char*)addr + size);
+ PR_LOG(_pr_msgc_lm, PR_LOG_ALWAYS,
+ ("GC: heap extends from %08x to %08x",
+ baseaddr, (long)baseaddr + (char*)lastaddr - (char*)baseaddr));
+
+ return addr;
+}
+
+PRBool _MD_ExtendGCHeap(char *base, PRInt32 oldSize, PRInt32 newSize) {
+ void* addr;
+
+ addr = VirtualAlloc( base + oldSize, newSize - oldSize,
+ MEM_COMMIT, PAGE_READWRITE );
+ if (NULL == addr) {
+ PR_LOG(_pr_msgc_lm, PR_LOG_ALWAYS, ("GC: unable to extend heap: LastError=%ld",
+ GetLastError()));
+ return PR_FALSE;
+ }
+ if (base + oldSize != (char*)addr) {
+ PR_LOG(_pr_msgc_lm, PR_LOG_ALWAYS, ("GC: segment extension returned %x instead of %x",
+ addr, base + oldSize));
+ VirtualFree(addr, newSize - oldSize, MEM_DECOMMIT);
+ return PR_FALSE;
+ }
+ lastaddr = base + newSize;
+ PR_LOG(_pr_msgc_lm, PR_LOG_ALWAYS,
+ ("GC: heap now extends from %p to %p",
+ base, base + newSize));
+ return PR_TRUE;
+}
+
+
+void _MD_FreeGCSegment(void *base, PRInt32 len)
+{
+ (void)VirtualFree(base, 0, MEM_RELEASE);
+}
diff --git a/lib/msgc/tests/Makefile b/lib/msgc/tests/Makefile
new file mode 100644
index 00000000..e3f4b1c7
--- /dev/null
+++ b/lib/msgc/tests/Makefile
@@ -0,0 +1,292 @@
+#
+# 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)
+OS_CFLAGS = $(OS_EXE_CFLAGS)
+W16STDIO = $(MOD_DEPTH)/pr/src/md/windows/$(OBJDIR)/w16stdio.$(OBJ_SUFFIX)
+endif
+
+ifeq ($(OS_TARGET), OS2)
+OS_CFLAGS = $(OS_EXE_CFLAGS)
+endif
+
+CSRCS = gc1.c thrashgc.c
+
+ifeq ($(OS_ARCH), WINNT)
+PROG_SUFFIX = .exe
+else
+PROG_SUFFIX =
+endif
+
+PROGS = $(addprefix $(OBJDIR)/, $(CSRCS:.c=$(PROG_SUFFIX)))
+
+TARGETS = $(PROGS) $(OBJS)
+
+INCLUDES = -I$(DIST)/include
+
+# Setting the variables LDOPTS and LIBPR. We first initialize
+# them to the default values, then adjust them for some platforms.
+LDOPTS = -L$(DIST)/lib
+NSPR_VERSION = $(MOD_VERSION)
+GC_VERSION = $(MOD_VERSION)
+LIBPR = -lnspr$(NSPR_VERSION)
+LIBPLC = -lplc$(NSPR_VERSION)
+LIBGC = -lmsgc$(GC_VERSION)
+
+ifeq ($(OS_ARCH), WINNT)
+ifeq ($(OS_TARGET), WIN16)
+ LIBPR = $(DIST)/lib/nspr$(NSPR_VERSION).lib
+ LIBPLC = $(DIST)/lib/plc$(NSPR_VERSION).lib
+ LIBGC= $(DIST)/lib/msgc$(GC_VERSION).lib
+else
+ifeq ($(OS_TARGET),OS2)
+ LDOPTS = -NOE -DEBUG -nologo -PMTYPE:VIO
+ LIBPR = $(DIST)/lib/nspr$(NSPR_VERSION).lib
+ LIBPLC = $(DIST)/lib/plc$(NSPR_VERSION).lib
+ LIBGC= $(DIST)/lib/msgc$(GC_VERSION).lib
+else
+ LDOPTS = -NOLOGO -DEBUG -DEBUGTYPE:CV -INCREMENTAL:NO
+ LIBPR = $(DIST)/lib/libnspr$(NSPR_VERSION).$(LIB_SUFFIX)
+ LIBPLC = $(DIST)/lib/libplc$(NSPR_VERSION).$(LIB_SUFFIX)
+ LIBGC= $(DIST)/lib/libmsgc$(GC_VERSION).$(LIB_SUFFIX)
+endif
+endif
+endif
+
+ifneq ($(OS_ARCH), WINNT)
+PWD = $(shell pwd)
+endif
+
+ifeq ($(OS_ARCH), IRIX)
+LDOPTS += -rpath $(PWD)/$(DIST)/lib -rdata_shared
+
+# For 6.x machines, include this flag
+ifeq ($(basename $(OS_RELEASE)),6)
+ifeq ($(USE_N32),1)
+LDOPTS += -n32
+else
+LDOPTS += -32
+endif
+endif
+
+endif
+
+ifeq ($(OS_ARCH), OSF1)
+# I haven't figured out how to pass -rpath to cc on OSF1 V3.2, so
+# we do static linking.
+ifeq ($(OS_RELEASE), V3.2)
+ LIBPR = $(DIST)/lib/libnspr$(NSPR_VERSION).a
+ LIBPLC = $(DIST)/lib/libplc$(NSPR_VERSION).a
+ LIBGC = $(DIST)/lib/libmsgc$(GC_VERSION).a
+ EXTRA_LIBS = -lc_r
+else
+ LDOPTS += -rpath $(PWD)/$(DIST)/lib
+endif
+endif
+
+ifeq ($(OS_ARCH), HP-UX)
+LDOPTS += -z -Wl,+s,+b,$(PWD)/$(DIST)/lib
+endif
+
+# AIX
+ifeq ($(OS_ARCH),AIX)
+ifeq ($(CLASSIC_NSPR),1)
+LDOPTS += -blibpath:.:$(PWD)/$(DIST)/lib:/usr/lpp/xlC/lib:/usr/lib:/lib
+else
+LDOPTS += -blibpath:.:$(PWD)/$(DIST)/lib:/usr/lib/threads:/usr/lpp/xlC/lib:/usr/lib:/lib
+endif
+ifeq ($(OS_ARCH)$(OS_RELEASE),AIX4.1)
+LIBPR = -lnspr$(NSPR_VERSION)_shr
+LIBPLC = -lplc$(NSPR_VERSION)_shr
+LIBGC = -lmsgc$(GC_VERSION)_shr
+else
+LDOPTS += -brtl
+EXTRA_LIBS = -ldl
+endif
+endif
+
+# Solaris
+ifeq ($(OS_ARCH), SunOS)
+ifneq ($(OS_RELEASE), 4.1.3_U1)
+ifdef NS_USE_GCC
+LDOPTS += -Xlinker -R -Xlinker $(PWD)/$(DIST)/lib
+else
+LDOPTS += -R $(PWD)/$(DIST)/lib
+endif
+endif
+
+ifneq ($(LOCAL_THREADS_ONLY),1)
+# SunOS 5.4 and 5.5 need to link with -lthread or -lpthread,
+# even though we already linked with these system libraries
+# when we built libnspr.so.
+ifeq ($(OS_RELEASE), 5.4)
+EXTRA_LIBS = -lthread
+endif
+
+ifeq ($(OS_RELEASE), 5.5)
+ifdef USE_PTHREADS
+EXTRA_LIBS = -lpthread
+else
+EXTRA_LIBS = -lthread
+endif
+endif
+endif # LOCAL_THREADS_ONLY
+endif # SunOS
+
+ifeq ($(OS_ARCH),NEC)
+EXTRA_LIBS = $(OS_LIBS)
+# This hardcodes in the executable programs the directory to find
+# libnspr.so etc. at program startup. Equivalent to the -R or -rpath
+# option for ld on other platforms.
+export LD_RUN_PATH = $(PWD)/$(DIST)/lib
+endif
+
+ifeq ($(OS_ARCH), NCR)
+# XXX: We see some strange problems when we link with libnspr.so.
+# So for now we use static libraries on NCR. The shared library
+# stuff below is commented out.
+LIBPR = $(DIST)/lib/libnspr$(NSPR_VERSION).a
+LIBPLC = $(DIST)/lib/libplc$(NSPR_VERSION).a
+LIBGC = $(DIST)/lib/libmsgc$(GC_VERSION).a
+EXTRA_LIBS = -lsocket -lnsl -ldl
+
+# NCR needs to link against -lsocket -lnsl (and -lc, which is linked
+# implicitly by $(CC)) again even though we already linked with these
+# system libraries when we built libnspr.so.
+#EXTRA_LIBS = -lsocket -lnsl
+# This hardcodes in the executable programs the directory to find
+# libnspr.so etc. at program startup. Equivalent to the -R or -rpath
+# option for ld on other platforms.
+#export LD_RUN_PATH = $(PWD)/$(DIST)/lib
+endif
+
+ifeq ($(OS_ARCH), Linux)
+ifeq ($(OS_RELEASE), 1.2)
+EXTRA_LIBS = -ldl
+endif
+endif
+
+ifeq ($(OS_ARCH), SCOOS)
+# SCO Unix needs to link against -lsocket again even though we
+# already linked with these system libraries when we built libnspr.so.
+EXTRA_LIBS = -lsocket
+# This hardcodes in the executable programs the directory to find
+# libnspr.so etc. at program startup. Equivalent to the -R or -rpath
+# option for ld on other platforms.
+export LD_RUN_PATH = $(PWD)/$(DIST)/lib
+endif
+
+ifeq ($(OS_ARCH),SINIX)
+EXTRA_LIBS = -lsocket -lnsl -lresolv -ldl
+# This hardcodes in the executable programs the directory to find
+# libnspr.so etc. at program startup. Equivalent to the -R or -rpath
+# option for ld on other platforms.
+export LD_RUN_PATH = $(PWD)/$(DIST)/lib
+endif
+
+ifeq ($(OS_ARCH), UNIXWARE)
+export LD_RUN_PATH = $(PWD)/$(DIST)/lib
+endif
+
+ifeq ($(OS_ARCH),BSD_OS)
+EXTRA_LIBS = -ldl
+endif
+
+ifeq ($(OS_ARCH),DGUX)
+EXTRA_LIBS = -lsocket -lnsl -ldl
+endif
+
+#####################################################
+#
+# The rules
+#
+#####################################################
+
+include $(MOD_DEPTH)/config/rules.mk
+
+AIX_PRE_4_2 = 0
+ifeq ($(OS_ARCH),AIX)
+ifneq ($(OS_RELEASE),4.2)
+ifneq ($(USE_PTHREADS), 1)
+#AIX_PRE_4_2 = 1
+endif
+endif
+endif
+
+ifeq ($(AIX_PRE_4_2),1)
+
+# AIX releases prior to 4.2 need a special two-step linking hack
+# in order to both override the system select() and be able to
+# get at the original system select().
+#
+# We use a pattern rule in ns/nspr20/config/rules.mk to generate
+# the .$(OBJ_SUFFIX) file from the .c source file, then do the
+# two-step linking hack below.
+
+$(OBJDIR)/%: $(OBJDIR)/%.$(OBJ_SUFFIX)
+ @$(MAKE_OBJDIR)
+ rm -f $@ $(AIX_TMP)
+ $(CC) $(AIX_LINK_OPTS) -o $(AIX_TMP) $< $(DIST)/lib/libnspr$(NSPR_VERSION).a
+ $(CC) -o $@ $(AIX_TMP) $(AIX_WRAP)
+ rm -f $(AIX_TMP)
+
+else
+
+# All platforms that are not AIX pre-4.2.
+
+$(OBJDIR)/%$(PROG_SUFFIX): $(OBJDIR)/%.$(OBJ_SUFFIX)
+ @$(MAKE_OBJDIR)
+ifeq ($(OS_ARCH), WINNT)
+ifeq ($(OS_TARGET),WIN16)
+ echo system windows >w16link
+ echo name $@ >>w16link
+ echo option map >>w16link
+# echo option CASEEXACT >>w16link
+ echo option stack=16K >>w16link
+ echo debug $(DEBUGTYPE) all >>w16link
+ echo file >>w16link
+ echo $< , >>w16link
+ echo $(W16STDIO) >>w16link
+ echo library >>w16link
+ echo $(LIBPR), >>w16link
+ echo $(LIBPLC), >>w16link
+ echo $(LIBGC), >>w16link
+ echo winsock.lib >>w16link
+ wlink @w16link.
+else
+ifeq ($(OS_TARGET),OS2)
+ $(LINK) $(LDOPTS) $< $(LIBGC) $(LIBPLC) $(LIBPR) so32dll.lib tcp32dll.lib -MAP:$(@:.exe=.map) -out:$@
+else
+ link $(LDOPTS) $< $(LIBGC) $(LIBPLC) $(LIBPR) wsock32.lib -out:$@
+endif
+endif
+else
+ $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBGC) $(LIBPLC) $(LIBPR) $(EXTRA_LIBS) -o $@
+endif
+
+endif
+
+export:: $(TARGETS)
+export:: install
+clean::
+ rm -f $(TARGETS)
diff --git a/lib/msgc/tests/gc1.c b/lib/msgc/tests/gc1.c
new file mode 100644
index 00000000..64a05e2f
--- /dev/null
+++ b/lib/msgc/tests/gc1.c
@@ -0,0 +1,238 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "prgc.h"
+#include "prinit.h"
+#include "prmon.h"
+#include "prinrval.h"
+#ifndef XP_MAC
+#include "private/pprthred.h"
+#else
+#include "pprthred.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+#endif
+
+static PRMonitor *mon;
+static PRInt32 threads, waiting, iterations;
+static PRInt32 scanCount, finalizeCount, freeCount;
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+
+typedef struct Array {
+ PRUintn size;
+ void *body[1];
+} Array;
+
+int arrayTypeIndex;
+
+static void PR_CALLBACK ScanArray(void *a)
+{
+/* printf ("In ScanArray a = %X size = %d \n", a, a->size); */
+ scanCount++;
+}
+
+static void PR_CALLBACK FinalizeArray(void *a)
+{
+/* printf ("In FinalizeArray a = %X size = %d \n", a, a->size); */
+ finalizeCount++;
+}
+
+static void PR_CALLBACK FreeArray(void *a)
+{
+/* printf ("In FreeArray\n"); */
+ freeCount++;
+}
+
+static Array *NewArray(PRUintn size)
+{
+ Array *a;
+
+ a = (Array *)PR_AllocMemory(sizeof(Array) + size*sizeof(void*) - 1*sizeof(void*),
+ arrayTypeIndex, PR_ALLOC_CLEAN);
+
+/* printf ("In NewArray a = %X \n", a); */
+
+ if (a)
+ a->size = size;
+ return a;
+}
+
+GCType arrayType = {
+ ScanArray,
+ FinalizeArray,
+ 0,
+ 0,
+ FreeArray,
+ 0
+};
+
+static void Initialize(void)
+{
+ PR_InitGC(0, 0, 0, PR_GLOBAL_THREAD);
+ arrayTypeIndex = PR_RegisterType(&arrayType);
+}
+
+static void PR_CALLBACK AllocateLikeMad(void *arg)
+{
+ Array *prev;
+ PRInt32 i;
+ PRInt32 count;
+
+ count = (PRInt32)arg;
+ prev = 0;
+ for (i = 0; i < count; i++) {
+ Array *leak = NewArray(i & 511);
+ if ((i & 1023) == 0) {
+ prev = 0; /* forget */
+ } else {
+ if (i & 1) {
+ prev = leak; /* remember */
+ }
+ }
+ }
+ PR_EnterMonitor(mon);
+ waiting++;
+ PR_Notify(mon);
+ PR_ExitMonitor(mon);
+}
+
+int main(int argc, char **argv)
+{
+ PRIntervalTime start, stop, usec;
+ double d;
+ PRIntn i, totalIterations;
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dt:c:");
+
+ threads = 10;
+ iterations = 100;
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) {
+ fprintf(stderr, "Invalid command-line option\n");
+ exit(1);
+ }
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ case 't': /* number of threads */
+ threads = atoi(opt->value);
+ break;
+ case 'c': /* iteration count */
+ iterations = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ fprintf(stderr, "t is %ld, i is %ld\n", (long) threads, (long) iterations);
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 5);
+ PR_STDIO_INIT();
+ Initialize();
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("gc1.log");
+ debug_mode = 1;
+#endif
+
+ /* Spin all of the allocator threads and then wait for them to exit */
+ start = PR_IntervalNow();
+ mon = PR_NewMonitor();
+ PR_EnterMonitor(mon);
+ waiting = 0;
+ for (i = 0; i < threads; i++) {
+ (void) PR_CreateThreadGCAble(PR_USER_THREAD,
+ AllocateLikeMad, (void*)iterations,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ }
+ while (waiting != threads) {
+ PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_ExitMonitor(mon);
+
+ PR_GC();
+ PR_ForceFinalize();
+
+ totalIterations = iterations * threads;
+/*
+ if (scanCount != totalIterations)
+ printf ("scanCount discrepancy scanCount = %d totalIterations = %d \n",
+ scanCount, totalIterations);
+ if (freeCount != totalIterations)
+ printf ("freeCount discrepancy freeCount = %d totalIterations = %d \n",
+ freeCount, totalIterations);
+ if ((finalizeCount != totalIterations) && (finalizeCount != (totalIterations-1)))
+ printf ("finalizeCount discrepancy finalizeCount = %d totalIterations = %d \n",
+ finalizeCount,totalIterations);
+*/
+
+ stop = PR_IntervalNow();
+
+ usec = stop = stop - start;
+ d = (double)usec;
+
+ if (debug_mode) printf("%40s: %6.2f usec\n", "GC allocation", d / (iterations * threads));
+ else {
+ if (d == 0.0) failed_already = PR_TRUE;
+
+ }
+
+ PR_Cleanup();
+ if(failed_already)
+ {
+ printf("FAIL\n");
+ return 1;
+ }
+ else
+ {
+ printf("PASS\n");
+ return 0;
+ }
+}
diff --git a/lib/msgc/tests/thrashgc.c b/lib/msgc/tests/thrashgc.c
new file mode 100644
index 00000000..70d3fa1f
--- /dev/null
+++ b/lib/msgc/tests/thrashgc.c
@@ -0,0 +1,255 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+** Name: thrashgc
+**
+** Description: test garbace collection functions.
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+/***********************************************************************
+** Includes
+***********************************************************************/
+#include "prthread.h"
+#include "prgc.h"
+#include "prprf.h"
+#include "prinrval.h"
+#include "prlock.h"
+#include "prinit.h"
+#include "prcvar.h"
+
+#ifndef XP_MAC
+#include "private/pprthred.h"
+#else
+#include "pprthred.h"
+#endif
+
+#include <stdio.h>
+#include <memory.h>
+#include <string.h>
+
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+#endif
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+static char* progname;
+static PRInt32 loops = 1000;
+static int tix1, tix2, tix3;
+static GCInfo* gcInfo;
+static PRLock* stderrLock;
+
+typedef struct Type1 Type1;
+typedef struct Type2 Type2;
+
+struct Type1 {
+ Type2* atwo;
+ Type1* next;
+};
+
+struct Type2 {
+ void* buf;
+};
+
+static void PR_CALLBACK ScanType1(void *obj) {
+ gcInfo->livePointer(((Type1 *)obj)->atwo);
+ gcInfo->livePointer(((Type1 *)obj)->next);
+}
+
+static void PR_CALLBACK ScanType2(void *obj) {
+ gcInfo->livePointer(((Type2 *)obj)->buf);
+}
+
+static GCType type1 = {
+ ScanType1
+};
+
+static GCType type2 = {
+ ScanType2
+/* (void (*)(void*)) ScanType2 */
+};
+
+static GCType type3 = {
+ 0
+};
+
+Type1* NewType1(void) {
+ Type1* p = (Type1*) PR_AllocMemory(sizeof(Type1), tix1, PR_ALLOC_DOUBLE);
+ PR_ASSERT(p != NULL);
+ return p;
+}
+
+Type2* NewType2(void) {
+ Type2* p = (Type2*) PR_AllocMemory(sizeof(Type2), tix2, PR_ALLOC_DOUBLE);
+ PR_ASSERT(p != NULL);
+ return p;
+}
+
+void* NewBuffer(PRInt32 size) {
+ void* p = PR_AllocMemory(size, tix3, PR_ALLOC_DOUBLE);
+ PR_ASSERT(p != NULL);
+ return p;
+}
+
+/* Allocate alot of garbage */
+static void PR_CALLBACK AllocStuff(void *unused) {
+ PRInt32 i;
+ void* danglingRefs[50];
+ PRIntervalTime start, end;
+ char msg[100];
+
+ start = PR_IntervalNow();
+ for (i = 0; i < loops; i++) {
+ void* p;
+ if (i & 1) {
+ Type1* t1 = NewType1();
+ t1->atwo = NewType2();
+ t1->next = NewType1();
+ t1->atwo->buf = NewBuffer(100);
+ p = t1;
+ } else {
+ Type2* t2 = NewType2();
+ t2->buf = NewBuffer(i & 16383);
+ p = t2;
+ }
+ if ((i % 10) == 0) {
+ memmove(&danglingRefs[0], &danglingRefs[1], 49*sizeof(void*));
+ danglingRefs[49] = p;
+ }
+ }
+ end = PR_IntervalNow();
+ if (debug_mode) PR_snprintf(msg, sizeof(msg), "Thread %p: %ld allocations took %ld ms",
+ PR_GetCurrentThread(), loops,
+ PR_IntervalToMilliseconds((PRIntervalTime) (end - start)));
+ PR_Lock(stderrLock);
+#ifndef XP_MAC
+ fprintf(stderr, "%s\n", msg);
+#else
+ if (debug_mode) printf("%s\n", msg);
+#endif
+ PR_Unlock(stderrLock);
+ }
+
+static void usage(char *progname) {
+#ifndef XP_MAC
+ fprintf(stderr, "Usage: %s [-t threads] [-l loops]\n", progname);
+#else
+ printf("Usage: %s [-t threads] [-l loops]\n", progname);
+#endif
+ exit(-1);
+}
+
+static int realMain(int argc, char **argv, char *notused) {
+ int i;
+ int threads = 0;
+
+#ifndef XP_MAC
+ progname = strrchr(argv[0], '/');
+ if (progname == 0) progname = argv[0];
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-t") == 0) {
+ if (i == argc - 1) {
+ usage(progname);
+ }
+ threads = atoi(argv[++i]);
+ if (threads < 0) threads = 0;
+ if (threads > 10000) threads = 10000;
+ continue;
+ }
+ if (strcmp(argv[i], "-l") == 0) {
+ if (i == argc - 1) {
+ usage(progname);
+ }
+ loops = atoi(argv[++i]);
+ continue;
+ }
+ usage(progname);
+ }
+#else
+ threads = 50;
+#endif
+
+ for (i = 0; i < threads; i++) {
+ PRThread* thread;
+
+ /* XXXXX */
+ thread = PR_CreateThreadGCAble(PR_USER_THREAD, /* thread type */
+ AllocStuff, /* start function */
+ NULL, /* arg */
+ PR_PRIORITY_NORMAL, /* priority */
+ PR_LOCAL_THREAD, /* thread scope */
+ PR_UNJOINABLE_THREAD, /* thread state */
+ 0); /* stack size */
+ if (thread == 0) {
+#ifndef XP_MAC
+ fprintf(stderr, "%s: no more threads (only %d were created)\n",
+ progname, i);
+#else
+ printf("%s: no more threads (only %d were created)\n",
+ progname, i);
+#endif
+ break;
+ }
+ }
+ AllocStuff(NULL);
+ return 0;
+}
+
+static int padMain(int argc, char **argv) {
+ char pad[512];
+ return realMain(argc, argv, pad);
+}
+
+int main(int argc, char **argv) {
+ int rv;
+
+ debug_mode = 1;
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_SetThreadGCAble();
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("thrashgc.log");
+ debug_mode = 1;
+#endif
+
+ PR_InitGC(0, 0, 0, PR_GLOBAL_THREAD);
+ PR_STDIO_INIT();
+ stderrLock = PR_NewLock();
+ tix1 = PR_RegisterType(&type1);
+ tix2 = PR_RegisterType(&type2);
+ tix3 = PR_RegisterType(&type3);
+ gcInfo = PR_GetGCInfo();
+ rv = padMain(argc, argv);
+ printf("PASS\n");
+ PR_Cleanup();
+ return rv;
+}
diff --git a/lib/prstreams/Makefile b/lib/prstreams/Makefile
new file mode 100644
index 00000000..1cfaef52
--- /dev/null
+++ b/lib/prstreams/Makefile
@@ -0,0 +1,116 @@
+#
+# 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
+
+ifeq ($(OS_ARCH), IRIX)
+ CFLAGS += -KPIC
+ ifneq ($(OS_RELEASE),5.3)
+ CFLAGS += -exceptions
+ endif
+endif
+
+ifeq ($(OS_ARCH),HP-UX)
+ ifeq (($USE_64),1)
+ CCCFLAGS += +DA2.0W
+ endif
+endif
+
+INCLUDES = -I$(DIST)/include -I../../../include
+
+HEADERS = *.h
+
+CXXSRCS = \
+ prstrms.cpp \
+ $(NULL)
+
+OBJS = $(addprefix $(OBJDIR)/,$(CXXSRCS:.cpp=.$(OBJ_SUFFIX)))
+
+ifeq ($(OS_ARCH), WINNT)
+ ifeq (,$(filter-out WIN16 OS2,$(OS_TARGET)))
+ EXTRA_LIBS = $(DIST)/lib/nspr$(MOD_VERSION).lib
+ else
+ DLLBASE=/BASE:0x30000000
+ RES=$(OBJDIR)/prstrms.res
+ RESNAME=$(MOD_DEPTH)/pr/src/nspr.rc
+ OS_LIBS = user32.lib
+ EXTRA_LIBS = $(DIST)/lib/libnspr$(MOD_VERSION).lib
+ endif
+else
+ ifeq ($(OS_ARCH), AIX)
+ ifeq ($(OS_RELEASE), 4.1)
+ ifeq ($(CLASSIC_NSPR),1)
+ OS_LIBS += -lC -lc
+ else
+ OS_LIBS += -lC_r -lc_r
+ endif
+ else
+ ifeq ($(CLASSIC_NSPR),1)
+ MKSHLIB = /usr/lpp/xlC/bin/makeC++SharedLib -p 0
+ else
+ MKSHLIB = /usr/lpp/xlC/bin/makeC++SharedLib_r -p 0
+ endif
+ OS_LIBS += -ldl
+ endif
+ endif
+ ifeq ($(OS_ARCH)$(OS_RELEASE), AIX4.1)
+ EXTRA_LIBS = -L$(DIST)/lib -lnspr$(MOD_VERSION)_shr
+ else
+ EXTRA_LIBS = -L$(DIST)/lib -lnspr$(MOD_VERSION)
+ endif
+endif
+
+# On NCR and SCOOS, we can't link with extra libraries when
+# we build a shared library. If we do so, the linker doesn't
+# complain, but we would run into weird problems at run-time.
+# Therefore on these platforms, we link just the object files.
+ifeq ($(OS_ARCH),NCR)
+ EXTRA_LIBS =
+endif
+ifeq ($(OS_ARCH),SCOOS)
+ EXTRA_LIBS =
+endif
+
+ifeq ($(OS_ARCH), UNIXWARE)
+ OS_LIBS += -lC
+endif
+
+LIBRARY_NAME = prstrms
+LIBRARY_VERSION = $(MOD_VERSION)
+
+RELEASE_HEADERS = $(HEADERS)
+RELEASE_HEADERS_DEST = $(RELEASE_INCLUDE_DIR)
+RELEASE_LIBS = $(TARGETS)
+
+include $(MOD_DEPTH)/config/rules.mk
+
+export:: $(TARGETS) $(HEADERS)
+ $(INSTALL) -m 444 $(HEADERS) $(DIST)/include
+ $(INSTALL) -m 444 $(TARGETS) $(DIST)/lib
+
+install:: export
diff --git a/lib/prstreams/prstrms.cpp b/lib/prstreams/prstrms.cpp
new file mode 100644
index 00000000..473b70bf
--- /dev/null
+++ b/lib/prstreams/prstrms.cpp
@@ -0,0 +1,525 @@
+/* -*- 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.
+ */
+
+/*
+ * Robin J. Maxwell 11-22-96
+ */
+
+#include "prstrms.h"
+#include <string.h> // memmove
+
+//
+// Definition of macros _PRSTR_BP, _PRSTR_DELBUF, and _PRSTR_DELBUF_C.
+//
+// _PRSTR_BP is the protected member of class ios that is returned
+// by the public method rdbuf().
+//
+// _PRSTR_DELBUF is the method or data member of class ios, if available,
+// with which we can ensure that the ios destructor does not delete
+// the associated streambuf. If such a method or data member does not
+// exist, define _PRSTR_DELBUF to be empty.
+//
+// _PRSTR_DELBUF_C is just _PRSTR_DELBUF qualified by a base class.
+//
+
+#if defined(__GNUC__)
+#define _PRSTR_BP _strbuf
+#define _PRSTR_DELBUF(x) /* as nothing */
+#define _PRSTR_DELBUF_C(c, x) /* as nothing */
+#elif defined(WIN32)
+#define _PRSTR_BP bp
+#define _PRSTR_DELBUF(x) delbuf(x)
+#define _PRSTR_DELBUF_C(c, x) c::_PRSTR_DELBUF(x)
+#elif defined(OSF1)
+#define _PRSTR_BP m_psb
+#define _PRSTR_DELBUF(x) /* as nothing */
+#define _PRSTR_DELBUF_C(c, x) /* as nothing */
+#else
+#define _PRSTR_BP bp
+// Unix compilers don't believe in encapsulation
+// At least on Solaris this is also ignored
+#define _PRSTR_DELBUF(x) delbuf = x
+#define _PRSTR_DELBUF_C(c, x) c::_PRSTR_DELBUF(x)
+#endif
+
+const PRIntn STRM_BUFSIZ = 8192;
+
+#if !defined (PRFSTREAMS_BROKEN)
+
+PRfilebuf::PRfilebuf():
+_fd(0),
+_opened(PR_FALSE),
+_allocated(PR_FALSE)
+{
+}
+
+PRfilebuf::PRfilebuf(PRFileDesc *fd):
+streambuf(),
+_fd(fd),
+_opened(PR_FALSE),
+_allocated(PR_FALSE)
+{
+}
+
+PRfilebuf::PRfilebuf(PRFileDesc *fd, char * buffptr, int bufflen):
+_fd(fd),
+_opened(PR_FALSE),
+_allocated(PR_FALSE)
+{
+ PRfilebuf::setbuf(buffptr, bufflen);
+}
+
+PRfilebuf::~PRfilebuf()
+{
+ if (_opened){
+ close();
+ }else
+ sync();
+ if (_allocated)
+ delete base();
+}
+
+PRfilebuf*
+PRfilebuf::open(const char *name, int mode, int flags)
+{
+ if (_fd != 0)
+ return 0; // error if already open
+ PRIntn PRmode = 0;
+ // translate mode argument
+ if (!(mode & ios::nocreate))
+ PRmode |= PR_CREATE_FILE;
+ //if (mode & ios::noreplace)
+ // PRmode |= O_EXCL;
+ if (mode & ios::app){
+ mode |= ios::out;
+ PRmode |= PR_APPEND;
+ }
+ if (mode & ios::trunc){
+ mode |= ios::out; // IMPLIED
+ PRmode |= PR_TRUNCATE;
+ }
+ if (mode & ios::out){
+ if (mode & ios::in)
+ PRmode |= PR_RDWR;
+ else
+ PRmode |= PR_WRONLY;
+ if (!(mode & (ios::in|ios::app|ios::ate|ios::noreplace))){
+ mode |= ios::trunc; // IMPLIED
+ PRmode |= PR_TRUNCATE;
+ }
+ }else if (mode & ios::in)
+ PRmode |= PR_RDONLY;
+ else
+ return 0; // error if not ios:in or ios::out
+
+
+ //
+ // The usual portable across unix crap...
+ // NT gets a hokey piece of junk layer that prevents
+ // access to the API.
+#ifdef WIN32
+ _fd = PR_Open(name, PRmode, PRmode);
+#else
+ _fd = PR_Open(name, PRmode, flags);
+#endif
+ if (_fd == 0)
+ return 0;
+ _opened = PR_TRUE;
+ if ((!unbuffered()) && (!ebuf())){
+ char * sbuf = new char[STRM_BUFSIZ];
+ if (!sbuf)
+ unbuffered(1);
+ else{
+ _allocated = PR_TRUE;
+ streambuf::setb(sbuf,sbuf+STRM_BUFSIZ,0);
+ }
+ }
+ if (mode & ios::ate){
+ if (seekoff(0,ios::end,mode)==EOF){
+ close();
+ return 0;
+ }
+ }
+ return this;
+}
+
+PRfilebuf*
+PRfilebuf::attach(PRFileDesc *fd)
+{
+ _opened = PR_FALSE;
+ _fd = fd;
+ return this;
+}
+
+int
+PRfilebuf::overflow(int c)
+{
+ if (allocate()==EOF) // make sure there is a reserve area
+ return EOF;
+ if (PRfilebuf::sync()==EOF) // sync before new buffer created below
+ return EOF;
+
+ if (!unbuffered())
+ setp(base(),ebuf());
+
+ if (c!=EOF){
+ if ((!unbuffered()) && (pptr() < epptr())) // guard against recursion
+ sputc(c);
+ else{
+ if (PR_Write(_fd, &c, 1)!=1)
+ return(EOF);
+ }
+ }
+ return(1); // return something other than EOF if successful
+}
+
+int
+PRfilebuf::underflow()
+{
+ int count;
+ unsigned char tbuf;
+
+ if (in_avail())
+ return (int)(unsigned char) *gptr();
+
+ if (allocate()==EOF) // make sure there is a reserve area
+ return EOF;
+ if (PRfilebuf::sync()==EOF)
+ return EOF;
+
+ if (unbuffered())
+ {
+ if (PR_Read(_fd,(void *)&tbuf,1)<=0)
+ return EOF;
+ return (int)tbuf;
+ }
+
+ if ((count=PR_Read(_fd,(void *)base(),blen())) <= 0)
+ return EOF; // reached EOF
+ setg(base(),base(),base()+count);
+ return (int)(unsigned char) *gptr();
+}
+
+streambuf*
+PRfilebuf::setbuf(char *buffptr, int bufflen)
+{
+ if (is_open() && (ebuf()))
+ return 0;
+ if ((!buffptr) || (bufflen <= 0))
+ unbuffered(1);
+ else
+ setb(buffptr, buffptr+bufflen, 0);
+ return this;
+}
+
+streampos
+PRfilebuf::seekoff(streamoff offset, ios::seek_dir dir, int /* mode */)
+{
+ if (PR_GetDescType(_fd) == PR_DESC_FILE){
+ PRSeekWhence fdir;
+ PRInt32 retpos;
+ switch (dir) {
+ case ios::beg :
+ fdir = PR_SEEK_SET;
+ break;
+ case ios::cur :
+ fdir = PR_SEEK_CUR;
+ break;
+ case ios::end :
+ fdir = PR_SEEK_END;
+ break;
+ default:
+ // error
+ return(EOF);
+ }
+
+ if (PRfilebuf::sync()==EOF)
+ return EOF;
+ if ((retpos=PR_Seek(_fd, offset, fdir))==-1L)
+ return (EOF);
+ return((streampos)retpos);
+ }else
+ return (EOF);
+}
+
+
+int
+PRfilebuf::sync()
+{
+ PRInt32 count;
+
+ if (_fd==0)
+ return(EOF);
+
+ if (!unbuffered()){
+ // Sync write area
+ if ((count=out_waiting())!=0){
+ PRInt32 nout;
+ if ((nout =PR_Write(_fd,
+ (void *) pbase(),
+ (unsigned int)count)) != count){
+ if (nout > 0) {
+ // should set _pptr -= nout
+ pbump(-(int)nout);
+ memmove(pbase(), pbase()+nout, (int)(count-nout));
+ }
+ return(EOF);
+ }
+ }
+ setp(0,0); // empty put area
+
+ if (PR_GetDescType(_fd) == PR_DESC_FILE){
+ // Sockets can't seek; don't need this
+ if ((count=in_avail()) > 0){
+ if (PR_Seek(_fd, -count, PR_SEEK_CUR)!=-1L)
+ {
+ return (EOF);
+ }
+ }
+ }
+ setg(0,0,0); // empty get area
+ }
+ return(0);
+}
+
+PRfilebuf *
+PRfilebuf::close()
+{
+ int retval;
+ if (_fd==0)
+ return 0;
+
+ retval = sync();
+
+ if ((PR_Close(_fd)==0) || (retval==EOF))
+ return 0;
+ _fd = 0;
+ return this;
+}
+
+PRifstream::PRifstream():
+istream(new PRfilebuf)
+{
+ _PRSTR_DELBUF(0);
+}
+
+PRifstream::PRifstream(PRFileDesc *fd):
+istream(new PRfilebuf(fd))
+{
+ _PRSTR_DELBUF(0);
+}
+
+PRifstream::PRifstream(PRFileDesc *fd, char *buff, int bufflen):
+istream(new PRfilebuf(fd, buff, bufflen))
+{
+ _PRSTR_DELBUF(0);
+}
+
+PRifstream::PRifstream(const char * name, int mode, int flags):
+istream(new PRfilebuf)
+{
+ _PRSTR_DELBUF(0);
+ if (!rdbuf()->open(name, (mode|ios::in), flags))
+ clear(rdstate() | ios::failbit);
+}
+
+PRifstream::~PRifstream()
+{
+ sync();
+
+ delete rdbuf();
+#ifdef _PRSTR_BP
+ _PRSTR_BP = 0;
+#endif
+}
+
+streambuf *
+PRifstream::setbuf(char * ptr, int len)
+{
+ if ((is_open()) || (!(rdbuf()->setbuf(ptr, len)))){
+ clear(rdstate() | ios::failbit);
+ return 0;
+ }
+ return rdbuf();
+}
+
+void
+PRifstream::attach(PRFileDesc *fd)
+{
+ if (!(rdbuf()->attach(fd)))
+ clear(rdstate() | ios::failbit);
+}
+
+void
+PRifstream::open(const char * name, int mode, int flags)
+{
+ if (is_open() || !(rdbuf()->open(name, (mode|ios::in), flags)))
+ clear(rdstate() | ios::failbit);
+}
+
+void
+PRifstream::close()
+{
+ clear((rdbuf()->close()) ? 0 : (rdstate() | ios::failbit));
+}
+
+PRofstream::PRofstream():
+ostream(new PRfilebuf)
+{
+ _PRSTR_DELBUF(0);
+}
+
+PRofstream::PRofstream(PRFileDesc *fd):
+ostream(new PRfilebuf(fd))
+{
+ _PRSTR_DELBUF(0);
+}
+
+PRofstream::PRofstream(PRFileDesc *fd, char *buff, int bufflen):
+ostream(new PRfilebuf(fd, buff, bufflen))
+{
+ _PRSTR_DELBUF(0);
+}
+
+PRofstream::PRofstream(const char *name, int mode, int flags):
+ostream(new PRfilebuf)
+{
+ _PRSTR_DELBUF(0);
+ if (!rdbuf()->open(name, (mode|ios::out), flags))
+ clear(rdstate() | ios::failbit);
+}
+
+PRofstream::~PRofstream()
+{
+ flush();
+
+ delete rdbuf();
+#ifdef _PRSTR_BP
+ _PRSTR_BP = 0;
+#endif
+}
+
+streambuf *
+PRofstream::setbuf(char * ptr, int len)
+{
+ if ((is_open()) || (!(rdbuf()->setbuf(ptr, len)))){
+ clear(rdstate() | ios::failbit);
+ return 0;
+ }
+ return rdbuf();
+}
+
+void
+PRofstream::attach(PRFileDesc *fd)
+{
+ if (!(rdbuf()->attach(fd)))
+ clear(rdstate() | ios::failbit);
+}
+
+void
+PRofstream::open(const char * name, int mode, int flags)
+{
+ if (is_open() || !(rdbuf()->open(name, (mode|ios::out), flags)))
+ clear(rdstate() | ios::failbit);
+}
+
+void
+PRofstream::close()
+{
+ clear((rdbuf()->close()) ? 0 : (rdstate() | ios::failbit));
+}
+
+PRfstream::PRfstream():
+iostream(new PRfilebuf)
+{
+ _PRSTR_DELBUF_C(istream, 0);
+ _PRSTR_DELBUF_C(ostream, 0);
+}
+
+PRfstream::PRfstream(PRFileDesc *fd):
+iostream(new PRfilebuf(fd))
+{
+ _PRSTR_DELBUF_C(istream, 0);
+ _PRSTR_DELBUF_C(ostream, 0);
+}
+
+PRfstream::PRfstream(PRFileDesc *fd, char *buff, int bufflen):
+iostream(new PRfilebuf(fd, buff, bufflen))
+{
+ _PRSTR_DELBUF_C(istream, 0);
+ _PRSTR_DELBUF_C(ostream, 0);
+}
+
+PRfstream::PRfstream(const char *name, int mode, int flags):
+iostream(new PRfilebuf)
+{
+ _PRSTR_DELBUF_C(istream, 0);
+ _PRSTR_DELBUF_C(ostream, 0);
+ if (!rdbuf()->open(name, (mode|(ios::in|ios::out)), flags))
+ clear(rdstate() | ios::failbit);
+}
+
+PRfstream::~PRfstream()
+{
+ sync();
+ flush();
+
+ delete rdbuf();
+#ifdef _PRSTR_BP
+ istream::_PRSTR_BP = 0;
+ ostream::_PRSTR_BP = 0;
+#endif
+}
+
+streambuf *
+PRfstream::setbuf(char * ptr, int len)
+{
+ if ((is_open()) || (!(rdbuf()->setbuf(ptr, len)))){
+ clear(rdstate() | ios::failbit);
+ return 0;
+ }
+ return rdbuf();
+}
+
+void
+PRfstream::attach(PRFileDesc *fd)
+{
+ if (!(rdbuf()->attach(fd)))
+ clear(rdstate() | ios::failbit);
+}
+
+void
+PRfstream::open(const char * name, int mode, int flags)
+{
+ if (is_open() || !(rdbuf()->open(name, (mode|(ios::in|ios::out)), flags)))
+ clear(rdstate() | ios::failbit);
+}
+
+void
+PRfstream::close()
+{
+ clear((rdbuf()->close()) ? 0 : (rdstate() | ios::failbit));
+}
+
+#else
+
+// fix it sometime
+
+int fix_prfstreams () { return 0; }
+
+#endif
diff --git a/lib/prstreams/prstrms.h b/lib/prstreams/prstrms.h
new file mode 100644
index 00000000..2a1ac3ef
--- /dev/null
+++ b/lib/prstreams/prstrms.h
@@ -0,0 +1,128 @@
+/* -*- 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.
+ */
+
+/*
+ * Robin J. Maxwell 11-22-96
+ */
+
+#ifndef _PRSTRMS_H
+#define _PRSTRMS_H
+
+#include "prtypes.h"
+#include "prio.h"
+
+#ifdef _MSC_VER
+#pragma warning( disable : 4275)
+#endif
+#include <iostream.h>
+
+#if defined (PRFSTREAMS_BROKEN)
+
+// fix it sometime
+
+#define PRfilebuf streambuf
+#define PRifstream ifstream
+#define PRofstream ofstream
+#define PRfstream fstream
+
+#else
+
+class PR_IMPLEMENT(PRfilebuf): public streambuf
+{
+public:
+ PRfilebuf();
+ PRfilebuf(PRFileDesc *fd);
+ PRfilebuf(PRFileDesc *fd, char * buffptr, int bufflen);
+ ~PRfilebuf();
+ virtual int overflow(int=EOF);
+ virtual int underflow();
+ virtual streambuf *setbuf(char *buff, int bufflen);
+ virtual streampos seekoff(streamoff, ios::seek_dir, int);
+ virtual int sync();
+ PRfilebuf *open(const char *name, int mode, int flags);
+ PRfilebuf *attach(PRFileDesc *fd);
+ PRfilebuf *close();
+ int is_open() const {return (_fd != 0);}
+ PRFileDesc *fd(){return _fd;}
+
+private:
+ PRFileDesc * _fd;
+ PRBool _opened;
+ PRBool _allocated;
+};
+
+class PR_IMPLEMENT(PRifstream): public istream {
+public:
+ PRifstream();
+ PRifstream(const char *, int mode=ios::in, int flags = 0);
+ PRifstream(PRFileDesc *);
+ PRifstream(PRFileDesc *, char *, int);
+ ~PRifstream();
+
+ streambuf * setbuf(char *, int);
+ PRfilebuf* rdbuf(){return (PRfilebuf*) ios::rdbuf(); }
+
+ void attach(PRFileDesc *fd);
+ PRFileDesc *fd() {return rdbuf()->fd();}
+
+ int is_open(){return rdbuf()->is_open();}
+ void open(const char *, int mode=ios::in, int flags= 0);
+ void close();
+};
+
+class PR_IMPLEMENT(PRofstream) : public ostream {
+public:
+ PRofstream();
+ PRofstream(const char *, int mode=ios::out, int flags = 0);
+ PRofstream(PRFileDesc *);
+ PRofstream(PRFileDesc *, char *, int);
+ ~PRofstream();
+
+ streambuf * setbuf(char *, int);
+ PRfilebuf* rdbuf() { return (PRfilebuf*) ios::rdbuf(); }
+
+ void attach(PRFileDesc *);
+ PRFileDesc *fd() {return rdbuf()->fd();}
+
+ int is_open(){return rdbuf()->is_open();}
+ void open(const char *, int =ios::out, int = 0);
+ void close();
+};
+
+class PR_IMPLEMENT(PRfstream) : public iostream {
+public:
+ PRfstream();
+ PRfstream(const char *name, int mode, int flags= 0);
+ PRfstream(PRFileDesc *fd);
+ PRfstream(PRFileDesc *fd, char *buff, int bufflen);
+ ~PRfstream();
+
+ streambuf * setbuf(char *, int);
+ PRfilebuf* rdbuf(){ return (PRfilebuf*) ostream::rdbuf(); }
+
+ void attach(PRFileDesc *);
+ PRFileDesc *fd() { return rdbuf()->fd(); }
+
+ int is_open() { return rdbuf()->is_open(); }
+ void open(const char *, int, int = 0);
+ void close();
+};
+
+#endif
+
+#endif /* _PRSTRMS_H */
diff --git a/lib/prstreams/tests/testprstrm/Makefile b/lib/prstreams/tests/testprstrm/Makefile
new file mode 100644
index 00000000..f17cb0d4
--- /dev/null
+++ b/lib/prstreams/tests/testprstrm/Makefile
@@ -0,0 +1,213 @@
+#
+# 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)
+OS_CFLAGS = $(OS_EXE_CFLAGS)
+endif
+
+CXXSRCS = \
+ testprstrm.cpp \
+ $(NULL)
+
+OBJS = $(addprefix $(OBJDIR)/,$(CXXSRCS:.cpp=.$(OBJ_SUFFIX)))
+
+ifeq ($(OS_ARCH), WINNT)
+PROG_SUFFIX = .exe
+else
+PROG_SUFFIX =
+endif
+
+PROGS = $(addprefix $(OBJDIR)/, $(CXXSRCS:.cpp=$(PROG_SUFFIX)))
+
+TARGETS = $(PROGS) $(OBJS)
+
+INCLUDES = -I$(DIST)/include
+
+# Setting the variables LDOPTS and LIBPR. We first initialize
+# them to the default values, then adjust them for some platforms.
+LDOPTS = -L$(DIST)/lib
+LIBPR = -lnspr$(MOD_VERSION)
+LIBPRSTRMS = -lprstrms$(MOD_VERSION)
+
+ifeq ($(OS_ARCH), WINNT)
+ifeq ($(OS_TARGET), WIN16)
+ LIBPR = $(DIST)/lib/nspr$(MOD_VERSION).lib
+ LIBPRSTRMS = $(DIST)/lib/prstrms$(MOD_VERSION).lib
+else
+LDOPTS = -NOLOGO -DEBUG -DEBUGTYPE:CV -INCREMENTAL:NO
+LIBPR = $(DIST)/lib/libnspr$(MOD_VERSION).$(LIB_SUFFIX)
+LIBPRSTRMS = $(DIST)/lib/libprstrms$(MOD_VERSION).$(LIB_SUFFIX)
+endif
+endif
+
+ifneq ($(OS_ARCH), WINNT)
+PWD = $(shell pwd)
+endif
+
+ifeq ($(OS_ARCH), IRIX)
+LDOPTS += -rpath $(PWD)/$(DIST)/lib
+endif
+
+ifeq ($(OS_ARCH), OSF1)
+LDOPTS += -rpath $(PWD)/$(DIST)/lib
+endif
+
+ifeq ($(OS_ARCH), HP-UX)
+LDOPTS += -Wl,+s,+b,$(PWD)/$(DIST)/lib
+endif
+
+# AIX
+ifeq ($(OS_ARCH),AIX)
+ifeq ($(CLASSIC_NSPR),1)
+LDOPTS += -blibpath:.:$(PWD)/$(DIST)/lib:/usr/lpp/xlC/lib:/usr/lib:/lib
+else
+LDOPTS += -blibpath:.:$(PWD)/$(DIST)/lib:/usr/lib/threads:/usr/lpp/xlC/lib:/usr/lib:/lib
+endif
+ifeq ($(OS_ARCH)$(OS_RELEASE),AIX4.1)
+LIBPR = -lnspr$(MOD_VERSION)_shr
+LIBPRSTRMS = -lprstrms$(MOD_VERSION)_shr
+else
+LDOPTS += -brtl
+EXTRA_LIBS = -ldl
+endif
+endif
+
+# Solaris
+ifeq ($(OS_ARCH), SunOS)
+ifneq ($(OS_RELEASE), 4.1.3_U1)
+ifdef NS_USE_GCC
+LDOPTS += -Xlinker -R -Xlinker $(PWD)/$(DIST)/lib
+else
+LDOPTS += -R $(PWD)/$(DIST)/lib
+# CC on SunOS 5.4 and 5.5.x need to link with -lthread or -lpthread
+# (or use the -mt switch) even though we already linked with these
+# system libraries when we built libnspr.so.
+ifdef USE_PTHREADS
+EXTRA_LIBS = -lpthread
+else
+EXTRA_LIBS = -lthread
+endif # USE_PTHREADS
+endif # NS_USE_GCC
+endif # 4.1.3_U1
+endif # SunOS
+
+ifeq ($(OS_ARCH), NCR)
+# XXX: We see some strange problems when we link with libnspr.so.
+# So for now we use static libraries on NCR. The shared library
+# stuff below is commented out.
+LIBPR = $(DIST)/lib/libnspr$(MOD_VERSION).a
+LIBPRSTRMS = $(DIST)/lib/libprstrms$(MOD_VERSION).a
+EXTRA_LIBS = -lsocket -lnsl -ldl
+
+# NCR needs to link against -lsocket -lnsl (and -lc, which is linked
+# implicitly by $(CC)) again even though we already linked with these
+# system libraries when we built libnspr.so.
+#EXTRA_LIBS = -lsocket -lnsl
+# This hardcodes in the executable programs the directory to find
+# libnspr.so etc. at program startup. Equivalent to the -R or -rpath
+# option for ld on other platforms.
+#export LD_RUN_PATH = $(PWD)/$(DIST)/lib
+endif
+
+ifeq ($(OS_ARCH), SCOOS)
+# SCO Unix needs to link against -lsocket again even though we
+# already linked with these system libraries when we built libnspr.so.
+EXTRA_LIBS = -lsocket
+# This hardcodes in the executable programs the directory to find
+# libnspr.so etc. at program startup. Equivalent to the -R or -rpath
+# option for ld on other platforms.
+export LD_RUN_PATH = $(PWD)/$(DIST)/lib
+endif
+
+ifeq ($(OS_ARCH), UNIXWARE)
+export LD_RUN_PATH = $(PWD)/$(DIST)/lib
+endif
+
+#####################################################
+#
+# The rules
+#
+#####################################################
+
+include $(MOD_DEPTH)/config/rules.mk
+
+AIX_PRE_4_2 = 0
+ifeq ($(OS_ARCH),AIX)
+ifneq ($(OS_RELEASE),4.2)
+ifneq ($(USE_PTHREADS), 1)
+#AIX_PRE_4_2 = 1
+endif
+endif
+endif
+
+ifeq ($(AIX_PRE_4_2),1)
+
+# AIX releases prior to 4.2 need a special two-step linking hack
+# in order to both override the system select() and be able to
+# get at the original system select().
+#
+# We use a pattern rule in ns/nspr20/config/rules.mk to generate
+# the .$(OBJ_SUFFIX) file from the .c source file, then do the
+# two-step linking hack below.
+
+$(OBJDIR)/%: $(OBJDIR)/%.$(OBJ_SUFFIX)
+ @$(MAKE_OBJDIR)
+ rm -f $@ $(AIX_TMP)
+ $(CC) $(AIX_LINK_OPTS) -o $(AIX_TMP) $< $(DIST)/lib/libnspr$(MOD_VERSION).a
+ $(CC) -o $@ $(AIX_TMP) $(AIX_WRAP)
+ rm -f $(AIX_TMP)
+
+else
+
+# All platforms that are not AIX pre-4.2.
+
+$(OBJDIR)/%$(PROG_SUFFIX): $(OBJDIR)/%.$(OBJ_SUFFIX)
+ @$(MAKE_OBJDIR)
+ifeq ($(OS_ARCH), WINNT)
+ifeq ($(OS_TARGET),WIN16)
+ echo system windows >w16link
+ echo option map >>w16link
+ echo option stack=10K >>w16link
+ echo option heapsize=32K >>w16link
+ echo debug $(DEBUGTYPE) all >>w16link
+ echo name $@ >>w16link
+ echo file >>w16link
+ echo $< >>w16link
+ echo library >>w16link
+ echo $(LIBPR), >>w16link
+ echo $(LIBPRSTRMS), >>w16link
+ echo winsock.lib >>w16link
+ wlink @w16link.
+else
+ link $(LDOPTS) $< $(LIBPR) $(LIBPRSTRMS) wsock32.lib -out:$@
+endif
+else
+ $(CCC) $(XCFLAGS) $< $(LDOPTS) $(LIBPR) $(LIBPRSTRMS) $(EXTRA_LIBS) -o $@
+endif
+
+endif
+
+export:: $(TARGETS)
+install:: export
+clean::
+ rm -f $(TARGETS)
diff --git a/lib/prstreams/tests/testprstrm/testprstrm.cpp b/lib/prstreams/tests/testprstrm/testprstrm.cpp
new file mode 100644
index 00000000..2b8e9e94
--- /dev/null
+++ b/lib/prstreams/tests/testprstrm/testprstrm.cpp
@@ -0,0 +1,182 @@
+/* -*- 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 "prinit.h"
+#include "prstrms.h"
+#include "prio.h"
+#include <string.h>
+#include <stdio.h>
+#ifdef XP_UNIX
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif
+
+const unsigned int MaxCnt = 1;
+
+void threadwork(void *mytag);
+
+
+typedef struct threadarg {
+ void *mytag;
+} threadarg;
+
+void
+threadmain(void *mytag)
+{
+ threadarg arg;
+
+ arg.mytag = mytag;
+
+ threadwork(&arg);
+}
+
+
+void
+threadwork(void *_arg)
+{
+ threadarg *arg = (threadarg *)_arg;
+ unsigned int i;
+
+ char fname1[256];
+ char fname2[256];
+
+ strcpy(fname1, (char *)arg->mytag);
+ strcpy(fname2, (char *)arg->mytag);
+ strcat(fname2, "2");
+ PR_Delete(fname1);
+ PR_Delete(fname2);
+
+ PRfilebuf *fb[MaxCnt];
+ PRifstream *ifs[MaxCnt];
+ PRofstream *ofs[MaxCnt];
+ int mode = 0;
+#ifdef XP_UNIX
+ mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IRGRP|S_IWOTH|S_IROTH;
+#endif
+
+ //
+ // Allocate a bunch
+ cout << "Testing unused filebufs ----------------" << endl;
+ for (i=0; i < MaxCnt; i++){
+ fb[i] = new PRfilebuf;
+ }
+ // Delete them
+ for (i=0; i < MaxCnt; i++){
+ delete fb[i];
+ }
+ cout << "Unused filebufs complete ---------------" << endl;
+
+ //
+ // Allocate a bunch
+ cout << "Testing unused ifstream -----------------" << endl;
+ for (i=0; i < MaxCnt; i++){
+ ifs[i] = new PRifstream;
+ }
+ //
+ // Delete them
+ for (i=0; i < MaxCnt; i++){
+ delete ifs[i];
+ }
+ cout << "Unused ifstream complete ----------------" << endl;
+ //
+ // Allocate a bunch
+ cout << "Testing unused ofstream -----------------" << endl;
+ for (i=0; i < MaxCnt; i++){
+ ofs[i] = new PRofstream;
+ }
+ for (i=0; i < MaxCnt; i++){
+ *(ofs[i]) << "A"; // Write a bit
+ delete ofs[i]; // Delete it.
+ }
+ cout << "Unused ofstream complete ----------------" << endl;
+
+ cout << "Testing use of ofstream 1 (extra filebuf allocated) ---------" << endl;
+ PRofstream *aos = new PRofstream(fname1, ios::out|ios::ate, mode);
+ for (i=0; i < MaxCnt; i++){
+ for (int j=0; j < 8192; j++)
+ *aos << "AaBbCcDdEeFfGg" << endl;
+ fb[i] = new PRfilebuf; // Allocate as we go to hack at the heap
+ }
+ //
+ // Delete the extra foo we allocated
+ for (i=0; i < MaxCnt; i++){
+ delete fb[i];
+ }
+ aos->flush(); // Explicit flush
+ delete aos;
+ cout << "Testing use of ofstream 1 complete (extra filebuf deleted) --" << endl;
+ cout << "Testing use of ofstream 2 (extra filebuf allocated) ---------" << endl;
+ PRofstream *aos2 = new PRofstream(fname2, ios::out, mode);
+
+ for (i=0; i < MaxCnt; i++){
+ *aos2 << "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz";
+ }
+ // Force flushing in the dtor
+ delete aos2;
+ cout << "Testing use of ofstream 2 complete (extra filebuf deleted) --" << endl;
+ char line[1024];
+ cout << "Testing use of ifstream 1 (stack allocation) -------------" << endl;
+ PRifstream ais(fname1);
+ for (i=0; i < MaxCnt; i++){
+ ais >> line;
+ }
+ cout << "Testing use of ifstream 1 complete -----------------------" << endl;
+ cout << "Testing use of ifstream 2 ----------------------" << endl;
+ PRifstream *ais2 = new PRifstream(fname2);
+ char achar;
+ for (i=0; i < MaxCnt*10; i++){
+ *ais2 >> achar;
+ }
+ delete ais2;
+ cout << "Testing use of ifstream 2 complete -------------" << endl;
+}
+
+#define STACKSIZE 1024*1024
+int
+main(int argc, char **argv)
+{
+ PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 256);
+ threadmain("TestFile");
+ PRThread *thr1 = PR_CreateThread(PR_SYSTEM_THREAD,
+ threadmain,
+ (void *)"TestFile1",
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ STACKSIZE);
+ PRThread *thr2 = PR_CreateThread(PR_SYSTEM_THREAD,
+ threadmain,
+ (void *)"TestFile2",
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ STACKSIZE);
+
+ PRThread *thr3 = PR_CreateThread(PR_SYSTEM_THREAD,
+ threadmain,
+ (void *)"TestFile3",
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ STACKSIZE);
+ PR_JoinThread(thr1);
+ PR_JoinThread(thr2);
+ PR_JoinThread(thr3);
+ return 0;
+}
+
diff --git a/lib/tests/Makefile b/lib/tests/Makefile
new file mode 100644
index 00000000..bc9efd42
--- /dev/null
+++ b/lib/tests/Makefile
@@ -0,0 +1,192 @@
+#
+# 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)
+OS_CFLAGS = $(OS_EXE_CFLAGS)
+endif
+
+CSRCS = \
+ string.c \
+ base64t.c
+
+ifeq ($(OS_ARCH), WINNT)
+PROG_SUFFIX = .exe
+else
+PROG_SUFFIX =
+endif
+
+PROGS = $(addprefix $(OBJDIR)/, $(CSRCS:.c=$(PROG_SUFFIX)))
+
+TARGETS = $(PROGS) $(OBJS)
+
+INCLUDES = -I$(DIST)/include
+
+# Setting the variables LDOPTS and LIBPR. We first initialize
+# them to the default values, then adjust them for some platforms.
+LDOPTS = -L$(DIST)/lib
+LIBPR = -lnspr$(MOD_VERSION)
+LIBPLC = -lplc$(MOD_VERSION)
+
+ifeq ($(OS_ARCH), WINNT)
+ifeq ($(OS_TARGET), WIN16)
+ LIBPR = $(DIST)/lib/nspr$(MOD_VERSION).lib
+ LIBPLC= $(DIST)/lib/plc$(MOD_VERSION).lib
+else
+LDOPTS = -NOLOGO -DEBUG -DEBUGTYPE:CV -INCREMENTAL:NO
+LIBPR = $(DIST)/lib/libnspr$(MOD_VERSION).$(LIB_SUFFIX)
+LIBPLC= $(DIST)/lib/libplc$(MOD_VERSION).$(LIB_SUFFIX)
+endif
+endif
+
+ifneq ($(OS_ARCH), WINNT)
+PWD = $(shell pwd)
+endif
+
+ifeq ($(OS_ARCH), IRIX)
+LDOPTS += -rpath $(PWD)/$(DIST)/lib
+endif
+
+ifeq ($(OS_ARCH), OSF1)
+LDOPTS += -rpath $(PWD)/$(DIST)/lib -lpthread
+endif
+
+ifeq ($(OS_ARCH), HP-UX)
+LDOPTS += -Wl,+s,+b,$(PWD)/$(DIST)/lib
+endif
+
+# AIX
+ifeq ($(OS_ARCH),AIX)
+LDOPTS += -blibpath:.:$(PWD)/$(DIST)/lib:/usr/lib/threads:/usr/lpp/xlC/lib:/usr/lib:/lib
+LIBPR = -lnspr$(MOD_VERSION)_shr
+LIBPLC = -lplc$(MOD_VERSION)_shr
+endif
+
+# Solaris
+ifeq ($(OS_ARCH), SunOS)
+ifneq ($(OS_RELEASE), 4.1.3_U1)
+ifdef NS_USE_GCC
+LDOPTS += -Xlinker -R -Xlinker $(PWD)/$(DIST)/lib
+else
+LDOPTS += -R $(PWD)/$(DIST)/lib
+endif
+endif
+
+# SunOS 5.4 and 5.5 need to link with -lthread or -lpthread,
+# even though we already linked with these system libraries
+# when we built libnspr.so.
+ifeq ($(OS_RELEASE), 5.4)
+EXTRA_LIBS = -lthread
+endif
+
+ifeq ($(OS_RELEASE), 5.5)
+ifdef USE_PTHREADS
+EXTRA_LIBS = -lpthread
+else
+EXTRA_LIBS = -lthread
+endif
+endif
+endif # SunOS
+
+ifeq ($(OS_ARCH), NCR)
+# NCR needs to link against -lsocket -lnsl (and -lc, which is linked
+# implicitly by $(CC)) again even though we already linked with these
+# system libraries when we built libnspr.so.
+EXTRA_LIBS = -lsocket -lnsl
+# This hardcodes in the executable programs the directory to find
+# libnspr.so etc. at program startup. Equivalent to the -R or -rpath
+# option for ld on other platforms.
+export LD_RUN_PATH = $(PWD)/$(DIST)/lib
+endif
+
+#####################################################
+#
+# The rules
+#
+#####################################################
+
+include $(MOD_DEPTH)/config/rules.mk
+
+AIX_PRE_4_2 = 0
+ifeq ($(OS_ARCH),AIX)
+ifneq ($(OS_RELEASE),4.2)
+ifneq ($(USE_PTHREADS), 1)
+#AIX_PRE_4_2 = 1
+endif
+endif
+endif
+
+ifeq ($(AIX_PRE_4_2),1)
+
+# AIX releases prior to 4.2 need a special two-step linking hack
+# in order to both override the system select() and be able to
+# get at the original system select().
+#
+# We use a pattern rule in ns/nspr20/config/rules.mk to generate
+# the .$(OBJ_SUFFIX) file from the .c source file, then do the
+# two-step linking hack below.
+
+$(OBJDIR)/%: $(OBJDIR)/%.$(OBJ_SUFFIX)
+ @$(MAKE_OBJDIR)
+ rm -f $@ $(AIX_TMP)
+ $(CC) $(AIX_LINK_OPTS) -o $(AIX_TMP) $< $(DIST)/lib/libnspr$(MOD_VERSION).a
+ $(CC) -o $@ $(AIX_TMP) $(AIX_WRAP)
+ rm -f $(AIX_TMP)
+
+else
+
+# All platforms that are not AIX pre-4.2.
+
+$(OBJDIR)/%$(PROG_SUFFIX): $(OBJDIR)/%.$(OBJ_SUFFIX)
+ @$(MAKE_OBJDIR)
+
+ifeq ($(OS_ARCH), WINNT)
+ifeq ($(OS_TARGET),WIN16)
+ echo system windows >w16link
+ echo option map >>w16link
+ echo option stack=10K >>w16link
+ echo option heapsize=32K >>w16link
+ echo debug $(DEBUGTYPE) all >>w16link
+ echo name $@ >>w16link
+ echo file >>w16link
+ echo $< >>w16link
+ echo library >>w16link
+ echo $(LIBPR), >>w16link
+ echo $(LIBPLC), >>w16link
+ echo winsock.lib >>w16link
+ wlink @w16link.
+else
+ link $(LDOPTS) $< $(LIBPR) $(LIBPLC) wsock32.lib -out:$@
+endif
+else
+ $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPR) $(LIBPLC) $(EXTRA_LIBS) -o $@
+endif
+
+endif
+
+export:: $(TARGETS)
+install:: export
+clean::
+ rm -f $(TARGETS)
+
+
+
diff --git a/lib/tests/arena.c b/lib/tests/arena.c
new file mode 100644
index 00000000..dc36fd51
--- /dev/null
+++ b/lib/tests/arena.c
@@ -0,0 +1,265 @@
+/* -*- 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: arena.c
+** Description: Testing arenas
+**
+*/
+
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#include "nspr.h"
+#include "plarena.h"
+#include "plgetopt.h"
+
+PRLogModuleInfo *tLM;
+PRIntn threadCount = 0;
+PRMonitor *tMon;
+PRBool failed_already = PR_FALSE;
+
+/* Arguments from the command line with default values */
+PRIntn debug_mode = 0;
+PRIntn poolMin = 4096;
+PRIntn poolMax = (100 * 4096);
+PRIntn arenaMin = 40;
+PRIntn arenaMax = (100 * 40);
+PRIntn stressIterations = 15;
+PRIntn maxAlloc = (1024 * 1024);
+PRIntn stressThreads = 4;
+
+/*
+** Test arena Mark and Release.
+*/
+static void MarkAndRelease( void )
+{
+ PLArenaPool ap;
+ void *ptr;
+ void *mark;
+
+ PL_InitArenaPool( &ap, "TheArena", 4096, sizeof(double));
+ PL_ARENA_ALLOCATE( ptr, &ap, 512 );
+
+ mark = PL_ARENA_MARK( &ap );
+
+ PL_ARENA_ALLOCATE( ptr, &ap, 512 );
+
+ PL_ARENA_RELEASE( &ap, mark );
+ PL_ARENA_ALLOCATE( ptr, &ap, 512 );
+
+ if ( ptr != mark )
+ {
+ failed_already = PR_TRUE;
+ PR_LOG( tLM, PR_LOG_ERROR, ("Mark and Release failed: expected %p, got %p\n", mark, ptr));
+ }
+ else
+ PR_LOG( tLM, PR_LOG_DEBUG, ("Mark and Release passed\n"));
+
+ return;
+} /* end MarkAndRelease() */
+
+/*
+** RandSize() returns a random number in the range
+** min..max, rounded to the next doubleword
+**
+*/
+static PRIntn RandSize( PRIntn min, PRIntn max )
+{
+ PRIntn sz = (rand() % (max -min)) + min + sizeof(double);
+
+ sz &= ~sizeof(double)-1;
+
+ return(sz);
+}
+
+
+/*
+** StressThread()
+** A bunch of these beat on individual arenas
+** This tests the free_list protection.
+**
+*/
+static void PR_CALLBACK StressThread( void *arg )
+{
+ PLArenaPool ap;
+ PRIntn i;
+ PRIntn sz;
+ void *ptr;
+ PRThread *tp = PR_GetCurrentThread();
+
+ PR_LOG( tLM, PR_LOG_DEBUG, ("Stress Thread %p started\n", PR_GetCurrentThread()));
+ PL_InitArenaPool( &ap, "TheArena", RandSize( poolMin, poolMax), sizeof(double));
+
+ for ( i = 0; i < stressIterations; i++ )
+ {
+ PRIntn allocated = 0;
+
+ while ( allocated < maxAlloc )
+ {
+ sz = RandSize( arenaMin, arenaMax );
+ PL_ARENA_ALLOCATE( ptr, &ap, sz );
+ if ( ptr == NULL )
+ {
+ PR_LOG( tLM, PR_LOG_ERROR, ("ARENA_ALLOCATE() returned NULL\n\tAllocated: %d\n", allocated));
+ break;
+ }
+ allocated += sz;
+ }
+ PR_LOG( tLM, PR_LOG_DEBUG, ("Stress thread %p finished one iteration\n", tp));
+ PL_FreeArenaPool( &ap );
+ }
+ PR_LOG( tLM, PR_LOG_DEBUG, ("Stress thread %p finished all iteration\n", tp));
+ PL_FinishArenaPool( &ap );
+ PR_LOG( tLM, PR_LOG_DEBUG, ("Stress thread %p after FinishArenaPool()\n", tp));
+
+ /* That's all folks! let's quit */
+ PR_EnterMonitor(tMon);
+ threadCount--;
+ PR_Notify(tMon);
+ PR_ExitMonitor(tMon);
+ return;
+}
+
+/*
+** Stress()
+** Flog the hell out of arenas multi-threaded.
+** Do NOT pass an individual arena to another thread.
+**
+*/
+static void Stress( void )
+{
+ PRThread *tt;
+ PRIntn i;
+
+ tMon = PR_NewMonitor();
+
+ for ( i = 0 ; i < stressThreads ; i++ )
+ {
+ PR_EnterMonitor(tMon);
+ tt = PR_CreateThread(PR_USER_THREAD,
+ StressThread,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ threadCount++;
+ PR_ExitMonitor(tMon);
+ }
+
+ /* Wait for all threads to exit */
+ PR_EnterMonitor(tMon);
+ while ( threadCount != 0 )
+ {
+ PR_Wait(tMon, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_ExitMonitor(tMon);
+ PR_DestroyMonitor(tMon);
+
+ return;
+} /* end Stress() */
+
+/*
+** EvaluateResults()
+** uses failed_already to display results and set program
+** exit code.
+*/
+static PRIntn EvaluateResults(void)
+{
+ PRIntn rc = 0;
+
+ if ( failed_already == PR_TRUE )
+ {
+ PR_LOG( tLM, PR_LOG_DEBUG, ("FAIL\n"));
+ rc =1;
+ }
+ else
+ {
+ PR_LOG( tLM, PR_LOG_DEBUG, ("PASS\n"));
+ }
+ return(rc);
+} /* EvaluateResults() */
+
+void Help( void )
+{
+ printf("arena [options]\n");
+ printf("where options are:\n");
+ printf("-p <n> minimum size of an arena pool. Default(%d)\n", poolMin);
+ printf("-P <n> maximum size of an arena pool. Default(%d)\n", poolMax);
+ printf("-a <n> minimum size of an arena allocation. Default(%d)\n", arenaMin);
+ printf("-A <n> maximum size of an arena allocation. Default(%d)\n", arenaMax);
+ printf("-i <n> number of iterations in a stress thread. Default(%d)\n", stressIterations);
+ printf("-s <n> maximum allocation for a single stress thread. Default(%d)\n", maxAlloc);
+ printf("-t <n> number of stress threads. Default(%d)\n", stressThreads );
+ printf("-d enable debug mode\n");
+ printf("\n");
+ exit(1);
+}
+
+PRIntn main(PRIntn argc, char *argv[])
+{
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dhp:P:a:A:i:s:t:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'a': /* arena Min size */
+ arenaMin = atol( opt->value );
+ break;
+ case 'A': /* arena Max size */
+ arenaMax = atol( opt->value );
+ break;
+ case 'p': /* pool Min size */
+ poolMin = atol( opt->value );
+ break;
+ case 'P': /* pool Max size */
+ poolMax = atol( opt->value );
+ break;
+ case 'i': /* Iterations in stress tests */
+ stressIterations = atol( opt->value );
+ break;
+ case 's': /* storage to get per iteration */
+ maxAlloc = atol( opt->value );
+ break;
+ case 't': /* Number of stress threads to create */
+ stressThreads = atol( opt->value );
+ break;
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ case 'h': /* help */
+ default:
+ Help();
+ } /* end switch() */
+ } /* end while() */
+ PL_DestroyOptState(opt);
+
+ srand( (unsigned)time( NULL ) ); /* seed random number generator */
+ tLM = PR_NewLogModule("testcase");
+
+ MarkAndRelease();
+
+ Stress();
+
+ return(EvaluateResults());
+} /* end main() */
+
+/* arena.c */
diff --git a/lib/tests/base64t.c b/lib/tests/base64t.c
new file mode 100644
index 00000000..a1906a9f
--- /dev/null
+++ b/lib/tests/base64t.c
@@ -0,0 +1,3028 @@
+/* -*- 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 "plbase64.h"
+#include "plstr.h"
+#include "nspr.h"
+
+#include <stdio.h>
+
+static unsigned char *base = (unsigned char *)"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/* PL_Base64Encode, single characters */
+PRBool test_001(void)
+{
+ PRUint32 a, b;
+ unsigned char plain[ 4 ];
+ unsigned char cypher[ 5 ];
+ char result[ 8 ];
+ char *rv;
+
+ printf("Test 001 (PL_Base64Encode, single characters) ..."); fflush(stdout);
+
+ plain[1] = plain[2] = plain[3] = (unsigned char)0;
+ cypher[2] = cypher[3] = (unsigned char)'=';
+ cypher[4] = (unsigned char)0;
+
+ for( a = 0; a < 64; a++ )
+ {
+ cypher[0] = base[a];
+
+ for( b = 0; b < 4; b++ )
+ {
+ plain[0] = (unsigned char)(a * 4 + b);
+ cypher[1] = base[(b * 16)];
+
+ rv = PL_Base64Encode((char *)plain, 1, result);
+ if( rv != result )
+ {
+ printf("FAIL\n\t(%d, %d): return value\n", a, b);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strncmp((char *)cypher, result, 4) )
+ {
+ printf("FAIL\n\t(%d, %d): expected \"%s,\" got \"%.4s.\"\n",
+ a, b, cypher, result);
+ return PR_FALSE;
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Encode, double characters */
+PRBool test_002(void)
+{
+ PRUint32 a, b, c, d;
+ unsigned char plain[ 4 ];
+ unsigned char cypher[ 5 ];
+ char result[ 8 ];
+ char *rv;
+
+ printf("Test 002 (PL_Base64Encode, double characters) ..."); fflush(stdout);
+
+ plain[2] = plain[3] = (unsigned char)0;
+ cypher[3] = (unsigned char)'=';
+ cypher[4] = (unsigned char)0;
+
+ for( a = 0; a < 64; a++ )
+ {
+ cypher[0] = base[a];
+ for( b = 0; b < 4; b++ )
+ {
+ plain[0] = (a*4) + b;
+ for( c = 0; c < 16; c++ )
+ {
+ cypher[1] = base[b*16 + c];
+ for( d = 0; d < 16; d++ )
+ {
+ plain[1] = c*16 + d;
+ cypher[2] = base[d*4];
+
+ rv = PL_Base64Encode((char *)plain, 2, result);
+ if( rv != result )
+ {
+ printf("FAIL\n\t(%d, %d, %d, %d): return value\n", a, b, c, d);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strncmp((char *)cypher, result, 4) )
+ {
+ printf("FAIL\n\t(%d, %d, %d, %d): expected \"%s,\" got \"%.4s.\"\n",
+ a, b, c, d, cypher, result);
+ return PR_FALSE;
+ }
+ }
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Encode, triple characters */
+PRBool test_003(void)
+{
+ PRUint32 a, b, c, d, e, f;
+ unsigned char plain[ 4 ];
+ unsigned char cypher[ 5 ];
+ char result[ 8 ];
+ char *rv;
+
+ printf("Test 003 (PL_Base64Encode, triple characters) ..."); fflush(stdout);
+
+ cypher[4] = (unsigned char)0;
+
+ for( a = 0; a < 64; a++ )
+ {
+ cypher[0] = base[a];
+ for( b = 0; b < 4; b++ )
+ {
+ plain[0] = (a*4) + b;
+ for( c = 0; c < 16; c++ )
+ {
+ cypher[1] = base[b*16 + c];
+ for( d = 0; d < 16; d++ )
+ {
+ plain[1] = c*16 + d;
+ for( e = 0; e < 4; e++ )
+ {
+ cypher[2] = base[d*4 + e];
+ for( f = 0; f < 64; f++ )
+ {
+ plain[2] = e * 64 + f;
+ cypher[3] = base[f];
+
+ rv = PL_Base64Encode((char *)plain, 3, result);
+ if( rv != result )
+ {
+ printf("FAIL\n\t(%d, %d, %d, %d, %d, %d): return value\n", a, b, c, d, e, f);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strncmp((char *)cypher, result, 4) )
+ {
+ printf("FAIL\n\t(%d, %d, %d, %d, %d, %d): expected \"%s,\" got \"%.4s.\"\n",
+ a, b, c, d, e, f, cypher, result);
+ return PR_FALSE;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+ static struct
+ {
+ const char *plaintext;
+ const char *cyphertext;
+ } array[] =
+ {
+ /* Cyphertexts generated with uuenview 0.5.13 */
+ { " ", "IA==" },
+ { ".", "Lg==" },
+ { "/", "Lw==" },
+ { "C", "Qw==" },
+ { "H", "SA==" },
+ { "S", "Uw==" },
+ { "^", "Xg==" },
+ { "a", "YQ==" },
+ { "o", "bw==" },
+ { "t", "dA==" },
+
+ { "AB", "QUI=" },
+ { "AH", "QUg=" },
+ { "AQ", "QVE=" },
+ { "BD", "QkQ=" },
+ { "CR", "Q1I=" },
+ { "CS", "Q1M=" },
+ { "DB", "REI=" },
+ { "DC", "REM=" },
+ { "EK", "RUs=" },
+ { "ET", "RVQ=" },
+ { "IM", "SU0=" },
+ { "JR", "SlI=" },
+ { "LO", "TE8=" },
+ { "LW", "TFc=" },
+ { "ML", "TUw=" },
+ { "SB", "U0I=" },
+ { "TO", "VE8=" },
+ { "VS", "VlM=" },
+ { "WP", "V1A=" },
+ /* legitimate two-letter words */
+ { "ad", "YWQ=" },
+ { "ah", "YWg=" },
+ { "am", "YW0=" },
+ { "an", "YW4=" },
+ { "as", "YXM=" },
+ { "at", "YXQ=" },
+ { "ax", "YXg=" },
+ { "be", "YmU=" },
+ { "by", "Ynk=" },
+ { "do", "ZG8=" },
+ { "go", "Z28=" },
+ { "he", "aGU=" },
+ { "hi", "aGk=" },
+ { "if", "aWY=" },
+ { "in", "aW4=" },
+ { "is", "aXM=" },
+ { "it", "aXQ=" },
+ { "me", "bWU=" },
+ { "my", "bXk=" },
+ { "no", "bm8=" },
+ { "of", "b2Y=" },
+ { "on", "b24=" },
+ { "or", "b3I=" },
+ { "ox", "b3g=" },
+ { "so", "c28=" },
+ { "to", "dG8=" },
+ { "up", "dXA=" },
+ { "us", "dXM=" },
+ { "we", "d2U=" },
+ /* all three-letter entries in /usr/dict/words */
+ { "1st", "MXN0" },
+ { "2nd", "Mm5k" },
+ { "3rd", "M3Jk" },
+ { "4th", "NHRo" },
+ { "5th", "NXRo" },
+ { "6th", "NnRo" },
+ { "7th", "N3Ro" },
+ { "8th", "OHRo" },
+ { "9th", "OXRo" },
+ { "AAA", "QUFB" },
+ { "AAU", "QUFV" },
+ { "ABA", "QUJB" },
+ { "abc", "YWJj" },
+ { "Abe", "QWJl" },
+ { "Abo", "QWJv" },
+ { "ace", "YWNl" },
+ { "ACM", "QUNN" },
+ { "ACS", "QUNT" },
+ { "act", "YWN0" },
+ { "Ada", "QWRh" },
+ { "add", "YWRk" },
+ { "ado", "YWRv" },
+ { "aft", "YWZ0" },
+ { "age", "YWdl" },
+ { "ago", "YWdv" },
+ { "aid", "YWlk" },
+ { "ail", "YWls" },
+ { "aim", "YWlt" },
+ { "air", "YWly" },
+ { "ala", "YWxh" },
+ { "alb", "YWxi" },
+ { "ale", "YWxl" },
+ { "Ali", "QWxp" },
+ { "all", "YWxs" },
+ { "alp", "YWxw" },
+ { "A&M", "QSZN" },
+ { "AMA", "QU1B" },
+ { "ami", "YW1p" },
+ { "amp", "YW1w" },
+ { "Amy", "QW15" },
+ { "amy", "YW15" },
+ { "ana", "YW5h" },
+ { "and", "YW5k" },
+ { "ani", "YW5p" },
+ { "Ann", "QW5u" },
+ { "ant", "YW50" },
+ { "any", "YW55" },
+ { "A&P", "QSZQ" },
+ { "ape", "YXBl" },
+ { "Apr", "QXBy" },
+ { "APS", "QVBT" },
+ { "apt", "YXB0" },
+ { "arc", "YXJj" },
+ { "are", "YXJl" },
+ { "ark", "YXJr" },
+ { "arm", "YXJt" },
+ { "art", "YXJ0" },
+ { "a's", "YSdz" },
+ { "ash", "YXNo" },
+ { "ask", "YXNr" },
+ { "ass", "YXNz" },
+ { "ate", "YXRl" },
+ { "Aug", "QXVn" },
+ { "auk", "YXVr" },
+ { "Ave", "QXZl" },
+ { "awe", "YXdl" },
+ { "awl", "YXds" },
+ { "awn", "YXdu" },
+ { "axe", "YXhl" },
+ { "aye", "YXll" },
+ { "bad", "YmFk" },
+ { "bag", "YmFn" },
+ { "bah", "YmFo" },
+ { "bam", "YmFt" },
+ { "ban", "YmFu" },
+ { "bar", "YmFy" },
+ { "bat", "YmF0" },
+ { "bay", "YmF5" },
+ { "bed", "YmVk" },
+ { "bee", "YmVl" },
+ { "beg", "YmVn" },
+ { "bel", "YmVs" },
+ { "Ben", "QmVu" },
+ { "bet", "YmV0" },
+ { "bey", "YmV5" },
+ { "bib", "Ymli" },
+ { "bid", "Ymlk" },
+ { "big", "Ymln" },
+ { "bin", "Ymlu" },
+ { "bit", "Yml0" },
+ { "biz", "Yml6" },
+ { "BMW", "Qk1X" },
+ { "boa", "Ym9h" },
+ { "bob", "Ym9i" },
+ { "bog", "Ym9n" },
+ { "bon", "Ym9u" },
+ { "boo", "Ym9v" },
+ { "bop", "Ym9w" },
+ { "bow", "Ym93" },
+ { "box", "Ym94" },
+ { "boy", "Ym95" },
+ { "b's", "Yidz" },
+ { "BTL", "QlRM" },
+ { "BTU", "QlRV" },
+ { "bub", "YnVi" },
+ { "bud", "YnVk" },
+ { "bug", "YnVn" },
+ { "bum", "YnVt" },
+ { "bun", "YnVu" },
+ { "bus", "YnVz" },
+ { "but", "YnV0" },
+ { "buy", "YnV5" },
+ { "bye", "Ynll" },
+ { "cab", "Y2Fi" },
+ { "Cal", "Q2Fs" },
+ { "cam", "Y2Ft" },
+ { "can", "Y2Fu" },
+ { "cap", "Y2Fw" },
+ { "car", "Y2Fy" },
+ { "cat", "Y2F0" },
+ { "caw", "Y2F3" },
+ { "CBS", "Q0JT" },
+ { "CDC", "Q0RD" },
+ { "CEQ", "Q0VR" },
+ { "chi", "Y2hp" },
+ { "CIA", "Q0lB" },
+ { "cit", "Y2l0" },
+ { "cod", "Y29k" },
+ { "cog", "Y29n" },
+ { "col", "Y29s" },
+ { "con", "Y29u" },
+ { "coo", "Y29v" },
+ { "cop", "Y29w" },
+ { "cos", "Y29z" },
+ { "cot", "Y290" },
+ { "cow", "Y293" },
+ { "cox", "Y294" },
+ { "coy", "Y295" },
+ { "CPA", "Q1BB" },
+ { "cpu", "Y3B1" },
+ { "CRT", "Q1JU" },
+ { "cry", "Y3J5" },
+ { "c's", "Yydz" },
+ { "cub", "Y3Vi" },
+ { "cud", "Y3Vk" },
+ { "cue", "Y3Vl" },
+ { "cup", "Y3Vw" },
+ { "cur", "Y3Vy" },
+ { "cut", "Y3V0" },
+ { "dab", "ZGFi" },
+ { "dad", "ZGFk" },
+ { "dam", "ZGFt" },
+ { "Dan", "RGFu" },
+ { "Dar", "RGFy" },
+ { "day", "ZGF5" },
+ { "Dec", "RGVj" },
+ { "Dee", "RGVl" },
+ { "Del", "RGVs" },
+ { "den", "ZGVu" },
+ { "Des", "RGVz" },
+ { "dew", "ZGV3" },
+ { "dey", "ZGV5" },
+ { "did", "ZGlk" },
+ { "die", "ZGll" },
+ { "dig", "ZGln" },
+ { "dim", "ZGlt" },
+ { "din", "ZGlu" },
+ { "dip", "ZGlw" },
+ { "Dis", "RGlz" },
+ { "DNA", "RE5B" },
+ { "DOD", "RE9E" },
+ { "doe", "ZG9l" },
+ { "dog", "ZG9n" },
+ { "don", "ZG9u" },
+ { "dot", "ZG90" },
+ { "Dow", "RG93" },
+ { "dry", "ZHJ5" },
+ { "d's", "ZCdz" },
+ { "dub", "ZHVi" },
+ { "dud", "ZHVk" },
+ { "due", "ZHVl" },
+ { "dug", "ZHVn" },
+ { "dun", "ZHVu" },
+ { "dye", "ZHll" },
+ { "ear", "ZWFy" },
+ { "eat", "ZWF0" },
+ { "ebb", "ZWJi" },
+ { "EDT", "RURU" },
+ { "eel", "ZWVs" },
+ { "eft", "ZWZ0" },
+ { "e.g", "ZS5n" },
+ { "egg", "ZWdn" },
+ { "ego", "ZWdv" },
+ { "eke", "ZWtl" },
+ { "Eli", "RWxp" },
+ { "elk", "ZWxr" },
+ { "ell", "ZWxs" },
+ { "elm", "ZWxt" },
+ { "Ely", "RWx5" },
+ { "end", "ZW5k" },
+ { "Eng", "RW5n" },
+ { "EPA", "RVBB" },
+ { "era", "ZXJh" },
+ { "ere", "ZXJl" },
+ { "erg", "ZXJn" },
+ { "err", "ZXJy" },
+ { "e's", "ZSdz" },
+ { "EST", "RVNU" },
+ { "eta", "ZXRh" },
+ { "etc", "ZXRj" },
+ { "Eva", "RXZh" },
+ { "eve", "ZXZl" },
+ { "ewe", "ZXdl" },
+ { "eye", "ZXll" },
+ { "FAA", "RkFB" },
+ { "fad", "ZmFk" },
+ { "fag", "ZmFn" },
+ { "fan", "ZmFu" },
+ { "far", "ZmFy" },
+ { "fat", "ZmF0" },
+ { "fay", "ZmF5" },
+ { "FBI", "RkJJ" },
+ { "FCC", "RkND" },
+ { "FDA", "RkRB" },
+ { "Feb", "RmVi" },
+ { "fed", "ZmVk" },
+ { "fee", "ZmVl" },
+ { "few", "ZmV3" },
+ { "fib", "Zmli" },
+ { "fig", "Zmln" },
+ { "fin", "Zmlu" },
+ { "fir", "Zmly" },
+ { "fit", "Zml0" },
+ { "fix", "Zml4" },
+ { "Flo", "Rmxv" },
+ { "flu", "Zmx1" },
+ { "fly", "Zmx5" },
+ { "FMC", "Rk1D" },
+ { "fob", "Zm9i" },
+ { "foe", "Zm9l" },
+ { "fog", "Zm9n" },
+ { "fop", "Zm9w" },
+ { "for", "Zm9y" },
+ { "fox", "Zm94" },
+ { "FPC", "RlBD" },
+ { "fro", "ZnJv" },
+ { "fry", "ZnJ5" },
+ { "f's", "Zidz" },
+ { "FTC", "RlRD" },
+ { "fum", "ZnVt" },
+ { "fun", "ZnVu" },
+ { "fur", "ZnVy" },
+ { "gab", "Z2Fi" },
+ { "gad", "Z2Fk" },
+ { "gag", "Z2Fn" },
+ { "gal", "Z2Fs" },
+ { "gam", "Z2Ft" },
+ { "GAO", "R0FP" },
+ { "gap", "Z2Fw" },
+ { "gar", "Z2Fy" },
+ { "gas", "Z2Fz" },
+ { "gay", "Z2F5" },
+ { "gee", "Z2Vl" },
+ { "gel", "Z2Vs" },
+ { "gem", "Z2Vt" },
+ { "get", "Z2V0" },
+ { "gig", "Z2ln" },
+ { "Gil", "R2ls" },
+ { "gin", "Z2lu" },
+ { "GMT", "R01U" },
+ { "GNP", "R05Q" },
+ { "gnu", "Z251" },
+ { "Goa", "R29h" },
+ { "gob", "Z29i" },
+ { "god", "Z29k" },
+ { "gog", "Z29n" },
+ { "GOP", "R09Q" },
+ { "got", "Z290" },
+ { "GPO", "R1BP" },
+ { "g's", "Zydz" },
+ { "GSA", "R1NB" },
+ { "gum", "Z3Vt" },
+ { "gun", "Z3Vu" },
+ { "Gus", "R3Vz" },
+ { "gut", "Z3V0" },
+ { "guy", "Z3V5" },
+ { "gym", "Z3lt" },
+ { "gyp", "Z3lw" },
+ { "had", "aGFk" },
+ { "Hal", "SGFs" },
+ { "ham", "aGFt" },
+ { "Han", "SGFu" },
+ { "hap", "aGFw" },
+ { "hat", "aGF0" },
+ { "haw", "aGF3" },
+ { "hay", "aGF5" },
+ { "hem", "aGVt" },
+ { "hen", "aGVu" },
+ { "her", "aGVy" },
+ { "hew", "aGV3" },
+ { "hex", "aGV4" },
+ { "hey", "aGV5" },
+ { "hid", "aGlk" },
+ { "him", "aGlt" },
+ { "hip", "aGlw" },
+ { "his", "aGlz" },
+ { "hit", "aGl0" },
+ { "hob", "aG9i" },
+ { "hoc", "aG9j" },
+ { "hoe", "aG9l" },
+ { "hog", "aG9n" },
+ { "hoi", "aG9p" },
+ { "Hom", "SG9t" },
+ { "hop", "aG9w" },
+ { "hot", "aG90" },
+ { "how", "aG93" },
+ { "hoy", "aG95" },
+ { "h's", "aCdz" },
+ { "hub", "aHVi" },
+ { "hue", "aHVl" },
+ { "hug", "aHVn" },
+ { "huh", "aHVo" },
+ { "hum", "aHVt" },
+ { "Hun", "SHVu" },
+ { "hut", "aHV0" },
+ { "Ian", "SWFu" },
+ { "IBM", "SUJN" },
+ { "Ibn", "SWJu" },
+ { "ICC", "SUND" },
+ { "ice", "aWNl" },
+ { "icy", "aWN5" },
+ { "I'd", "SSdk" },
+ { "Ida", "SWRh" },
+ { "i.e", "aS5l" },
+ { "iii", "aWlp" },
+ { "Ike", "SWtl" },
+ { "ill", "aWxs" },
+ { "I'm", "SSdt" },
+ { "imp", "aW1w" },
+ { "Inc", "SW5j" },
+ { "ink", "aW5r" },
+ { "inn", "aW5u" },
+ { "ion", "aW9u" },
+ { "Ira", "SXJh" },
+ { "ire", "aXJl" },
+ { "irk", "aXJr" },
+ { "IRS", "SVJT" },
+ { "i's", "aSdz" },
+ { "Ito", "SXRv" },
+ { "ITT", "SVRU" },
+ { "ivy", "aXZ5" },
+ { "jab", "amFi" },
+ { "jag", "amFn" },
+ { "jam", "amFt" },
+ { "Jan", "SmFu" },
+ { "jar", "amFy" },
+ { "jaw", "amF3" },
+ { "jay", "amF5" },
+ { "Jed", "SmVk" },
+ { "jet", "amV0" },
+ { "Jew", "SmV3" },
+ { "jig", "amln" },
+ { "Jim", "Smlt" },
+ { "job", "am9i" },
+ { "Joe", "Sm9l" },
+ { "jog", "am9n" },
+ { "Jon", "Sm9u" },
+ { "jot", "am90" },
+ { "joy", "am95" },
+ { "j's", "aidz" },
+ { "jug", "anVn" },
+ { "jut", "anV0" },
+ { "Kay", "S2F5" },
+ { "keg", "a2Vn" },
+ { "ken", "a2Vu" },
+ { "key", "a2V5" },
+ { "kid", "a2lk" },
+ { "Kim", "S2lt" },
+ { "kin", "a2lu" },
+ { "kit", "a2l0" },
+ { "k's", "aydz" },
+ { "lab", "bGFi" },
+ { "lac", "bGFj" },
+ { "lad", "bGFk" },
+ { "lag", "bGFn" },
+ { "lam", "bGFt" },
+ { "Lao", "TGFv" },
+ { "lap", "bGFw" },
+ { "law", "bGF3" },
+ { "lax", "bGF4" },
+ { "lay", "bGF5" },
+ { "lea", "bGVh" },
+ { "led", "bGVk" },
+ { "lee", "bGVl" },
+ { "leg", "bGVn" },
+ { "Len", "TGVu" },
+ { "Leo", "TGVv" },
+ { "let", "bGV0" },
+ { "Lev", "TGV2" },
+ { "Lew", "TGV3" },
+ { "lew", "bGV3" },
+ { "lid", "bGlk" },
+ { "lie", "bGll" },
+ { "lim", "bGlt" },
+ { "Lin", "TGlu" },
+ { "lip", "bGlw" },
+ { "lit", "bGl0" },
+ { "Liz", "TGl6" },
+ { "lob", "bG9i" },
+ { "log", "bG9n" },
+ { "lop", "bG9w" },
+ { "Los", "TG9z" },
+ { "lot", "bG90" },
+ { "Lou", "TG91" },
+ { "low", "bG93" },
+ { "loy", "bG95" },
+ { "l's", "bCdz" },
+ { "LSI", "TFNJ" },
+ { "Ltd", "THRk" },
+ { "LTV", "TFRW" },
+ { "lug", "bHVn" },
+ { "lux", "bHV4" },
+ { "lye", "bHll" },
+ { "Mac", "TWFj" },
+ { "mad", "bWFk" },
+ { "Mae", "TWFl" },
+ { "man", "bWFu" },
+ { "Mao", "TWFv" },
+ { "map", "bWFw" },
+ { "mar", "bWFy" },
+ { "mat", "bWF0" },
+ { "maw", "bWF3" },
+ { "Max", "TWF4" },
+ { "max", "bWF4" },
+ { "may", "bWF5" },
+ { "MBA", "TUJB" },
+ { "Meg", "TWVn" },
+ { "Mel", "TWVs" },
+ { "men", "bWVu" },
+ { "met", "bWV0" },
+ { "mew", "bWV3" },
+ { "mid", "bWlk" },
+ { "mig", "bWln" },
+ { "min", "bWlu" },
+ { "MIT", "TUlU" },
+ { "mix", "bWl4" },
+ { "mob", "bW9i" },
+ { "Moe", "TW9l" },
+ { "moo", "bW9v" },
+ { "mop", "bW9w" },
+ { "mot", "bW90" },
+ { "mow", "bW93" },
+ { "MPH", "TVBI" },
+ { "Mrs", "TXJz" },
+ { "m's", "bSdz" },
+ { "mud", "bXVk" },
+ { "mug", "bXVn" },
+ { "mum", "bXVt" },
+ { "nab", "bmFi" },
+ { "nag", "bmFn" },
+ { "Nan", "TmFu" },
+ { "nap", "bmFw" },
+ { "Nat", "TmF0" },
+ { "nay", "bmF5" },
+ { "NBC", "TkJD" },
+ { "NBS", "TkJT" },
+ { "NCO", "TkNP" },
+ { "NCR", "TkNS" },
+ { "Ned", "TmVk" },
+ { "nee", "bmVl" },
+ { "net", "bmV0" },
+ { "new", "bmV3" },
+ { "nib", "bmli" },
+ { "NIH", "TklI" },
+ { "nil", "bmls" },
+ { "nip", "bmlw" },
+ { "nit", "bml0" },
+ { "NNE", "Tk5F" },
+ { "NNW", "Tk5X" },
+ { "nob", "bm9i" },
+ { "nod", "bm9k" },
+ { "non", "bm9u" },
+ { "nor", "bm9y" },
+ { "not", "bm90" },
+ { "Nov", "Tm92" },
+ { "now", "bm93" },
+ { "NRC", "TlJD" },
+ { "n's", "bidz" },
+ { "NSF", "TlNG" },
+ { "nun", "bnVu" },
+ { "nut", "bnV0" },
+ { "NYC", "TllD" },
+ { "NYU", "TllV" },
+ { "oaf", "b2Fm" },
+ { "oak", "b2Fr" },
+ { "oar", "b2Fy" },
+ { "oat", "b2F0" },
+ { "Oct", "T2N0" },
+ { "odd", "b2Rk" },
+ { "ode", "b2Rl" },
+ { "off", "b2Zm" },
+ { "oft", "b2Z0" },
+ { "ohm", "b2ht" },
+ { "oil", "b2ls" },
+ { "old", "b2xk" },
+ { "one", "b25l" },
+ { "opt", "b3B0" },
+ { "orb", "b3Ji" },
+ { "ore", "b3Jl" },
+ { "Orr", "T3Jy" },
+ { "o's", "bydz" },
+ { "Ott", "T3R0" },
+ { "our", "b3Vy" },
+ { "out", "b3V0" },
+ { "ova", "b3Zh" },
+ { "owe", "b3dl" },
+ { "owl", "b3ds" },
+ { "own", "b3du" },
+ { "pad", "cGFk" },
+ { "pal", "cGFs" },
+ { "Pam", "UGFt" },
+ { "pan", "cGFu" },
+ { "pap", "cGFw" },
+ { "par", "cGFy" },
+ { "pat", "cGF0" },
+ { "paw", "cGF3" },
+ { "pax", "cGF4" },
+ { "pay", "cGF5" },
+ { "Paz", "UGF6" },
+ { "PBS", "UEJT" },
+ { "PDP", "UERQ" },
+ { "pea", "cGVh" },
+ { "pee", "cGVl" },
+ { "peg", "cGVn" },
+ { "pen", "cGVu" },
+ { "pep", "cGVw" },
+ { "per", "cGVy" },
+ { "pet", "cGV0" },
+ { "pew", "cGV3" },
+ { "PhD", "UGhE" },
+ { "phi", "cGhp" },
+ { "pie", "cGll" },
+ { "pig", "cGln" },
+ { "pin", "cGlu" },
+ { "pip", "cGlw" },
+ { "pit", "cGl0" },
+ { "ply", "cGx5" },
+ { "pod", "cG9k" },
+ { "Poe", "UG9l" },
+ { "poi", "cG9p" },
+ { "pol", "cG9s" },
+ { "pop", "cG9w" },
+ { "pot", "cG90" },
+ { "pow", "cG93" },
+ { "ppm", "cHBt" },
+ { "pro", "cHJv" },
+ { "pry", "cHJ5" },
+ { "p's", "cCdz" },
+ { "psi", "cHNp" },
+ { "PTA", "UFRB" },
+ { "pub", "cHVi" },
+ { "PUC", "UFVD" },
+ { "pug", "cHVn" },
+ { "pun", "cHVu" },
+ { "pup", "cHVw" },
+ { "pus", "cHVz" },
+ { "put", "cHV0" },
+ { "PVC", "UFZD" },
+ { "QED", "UUVE" },
+ { "q's", "cSdz" },
+ { "qua", "cXVh" },
+ { "quo", "cXVv" },
+ { "Rae", "UmFl" },
+ { "rag", "cmFn" },
+ { "raj", "cmFq" },
+ { "ram", "cmFt" },
+ { "ran", "cmFu" },
+ { "rap", "cmFw" },
+ { "rat", "cmF0" },
+ { "raw", "cmF3" },
+ { "ray", "cmF5" },
+ { "RCA", "UkNB" },
+ { "R&D", "UiZE" },
+ { "reb", "cmVi" },
+ { "red", "cmVk" },
+ { "rep", "cmVw" },
+ { "ret", "cmV0" },
+ { "rev", "cmV2" },
+ { "Rex", "UmV4" },
+ { "rho", "cmhv" },
+ { "rib", "cmli" },
+ { "rid", "cmlk" },
+ { "rig", "cmln" },
+ { "rim", "cmlt" },
+ { "Rio", "Umlv" },
+ { "rip", "cmlw" },
+ { "RNA", "Uk5B" },
+ { "rob", "cm9i" },
+ { "rod", "cm9k" },
+ { "roe", "cm9l" },
+ { "Ron", "Um9u" },
+ { "rot", "cm90" },
+ { "row", "cm93" },
+ { "Roy", "Um95" },
+ { "RPM", "UlBN" },
+ { "r's", "cidz" },
+ { "rub", "cnVi" },
+ { "rue", "cnVl" },
+ { "rug", "cnVn" },
+ { "rum", "cnVt" },
+ { "run", "cnVu" },
+ { "rut", "cnV0" },
+ { "rye", "cnll" },
+ { "sac", "c2Fj" },
+ { "sad", "c2Fk" },
+ { "sag", "c2Fn" },
+ { "Sal", "U2Fs" },
+ { "Sam", "U2Ft" },
+ { "San", "U2Fu" },
+ { "Sao", "U2Fv" },
+ { "sap", "c2Fw" },
+ { "sat", "c2F0" },
+ { "saw", "c2F3" },
+ { "sax", "c2F4" },
+ { "say", "c2F5" },
+ { "Sci", "U2Np" },
+ { "SCM", "U0NN" },
+ { "sea", "c2Vh" },
+ { "sec", "c2Vj" },
+ { "see", "c2Vl" },
+ { "sen", "c2Vu" },
+ { "seq", "c2Vx" },
+ { "set", "c2V0" },
+ { "sew", "c2V3" },
+ { "sex", "c2V4" },
+ { "she", "c2hl" },
+ { "Shu", "U2h1" },
+ { "shy", "c2h5" },
+ { "sib", "c2li" },
+ { "sic", "c2lj" },
+ { "sin", "c2lu" },
+ { "sip", "c2lw" },
+ { "sir", "c2ly" },
+ { "sis", "c2lz" },
+ { "sit", "c2l0" },
+ { "six", "c2l4" },
+ { "ski", "c2tp" },
+ { "sky", "c2t5" },
+ { "sly", "c2x5" },
+ { "sob", "c29i" },
+ { "Soc", "U29j" },
+ { "sod", "c29k" },
+ { "Sol", "U29s" },
+ { "son", "c29u" },
+ { "sop", "c29w" },
+ { "sou", "c291" },
+ { "sow", "c293" },
+ { "soy", "c295" },
+ { "spa", "c3Bh" },
+ { "spy", "c3B5" },
+ { "Sri", "U3Jp" },
+ { "s's", "cydz" },
+ { "SSE", "U1NF" },
+ { "SST", "U1NU" },
+ { "SSW", "U1NX" },
+ { "Stu", "U3R1" },
+ { "sub", "c3Vi" },
+ { "sud", "c3Vk" },
+ { "sue", "c3Vl" },
+ { "sum", "c3Vt" },
+ { "sun", "c3Vu" },
+ { "sup", "c3Vw" },
+ { "Sus", "U3Vz" },
+ { "tab", "dGFi" },
+ { "tad", "dGFk" },
+ { "tag", "dGFn" },
+ { "tam", "dGFt" },
+ { "tan", "dGFu" },
+ { "tao", "dGFv" },
+ { "tap", "dGFw" },
+ { "tar", "dGFy" },
+ { "tat", "dGF0" },
+ { "tau", "dGF1" },
+ { "tax", "dGF4" },
+ { "tea", "dGVh" },
+ { "Ted", "VGVk" },
+ { "ted", "dGVk" },
+ { "tee", "dGVl" },
+ { "Tel", "VGVs" },
+ { "ten", "dGVu" },
+ { "the", "dGhl" },
+ { "thy", "dGh5" },
+ { "tic", "dGlj" },
+ { "tid", "dGlk" },
+ { "tie", "dGll" },
+ { "til", "dGls" },
+ { "Tim", "VGlt" },
+ { "tin", "dGlu" },
+ { "tip", "dGlw" },
+ { "tit", "dGl0" },
+ { "TNT", "VE5U" },
+ { "toe", "dG9l" },
+ { "tog", "dG9n" },
+ { "Tom", "VG9t" },
+ { "ton", "dG9u" },
+ { "too", "dG9v" },
+ { "top", "dG9w" },
+ { "tor", "dG9y" },
+ { "tot", "dG90" },
+ { "tow", "dG93" },
+ { "toy", "dG95" },
+ { "TRW", "VFJX" },
+ { "try", "dHJ5" },
+ { "t's", "dCdz" },
+ { "TTL", "VFRM" },
+ { "TTY", "VFRZ" },
+ { "tub", "dHVi" },
+ { "tug", "dHVn" },
+ { "tum", "dHVt" },
+ { "tun", "dHVu" },
+ { "TVA", "VFZB" },
+ { "TWA", "VFdB" },
+ { "two", "dHdv" },
+ { "TWX", "VFdY" },
+ { "ugh", "dWdo" },
+ { "UHF", "VUhG" },
+ { "Uri", "VXJp" },
+ { "urn", "dXJu" },
+ { "U.S", "VS5T" },
+ { "u's", "dSdz" },
+ { "USA", "VVNB" },
+ { "USC", "VVND" },
+ { "use", "dXNl" },
+ { "USN", "VVNO" },
+ { "van", "dmFu" },
+ { "vat", "dmF0" },
+ { "vee", "dmVl" },
+ { "vet", "dmV0" },
+ { "vex", "dmV4" },
+ { "VHF", "VkhG" },
+ { "via", "dmlh" },
+ { "vie", "dmll" },
+ { "vii", "dmlp" },
+ { "vis", "dmlz" },
+ { "viz", "dml6" },
+ { "von", "dm9u" },
+ { "vow", "dm93" },
+ { "v's", "didz" },
+ { "WAC", "V0FD" },
+ { "wad", "d2Fk" },
+ { "wag", "d2Fn" },
+ { "wah", "d2Fo" },
+ { "wan", "d2Fu" },
+ { "war", "d2Fy" },
+ { "was", "d2Fz" },
+ { "wax", "d2F4" },
+ { "way", "d2F5" },
+ { "web", "d2Vi" },
+ { "wed", "d2Vk" },
+ { "wee", "d2Vl" },
+ { "Wei", "V2Vp" },
+ { "wet", "d2V0" },
+ { "who", "d2hv" },
+ { "why", "d2h5" },
+ { "wig", "d2ln" },
+ { "win", "d2lu" },
+ { "wit", "d2l0" },
+ { "woe", "d29l" },
+ { "wok", "d29r" },
+ { "won", "d29u" },
+ { "woo", "d29v" },
+ { "wop", "d29w" },
+ { "wow", "d293" },
+ { "wry", "d3J5" },
+ { "w's", "dydz" },
+ { "x's", "eCdz" },
+ { "yah", "eWFo" },
+ { "yak", "eWFr" },
+ { "yam", "eWFt" },
+ { "yap", "eWFw" },
+ { "yaw", "eWF3" },
+ { "yea", "eWVh" },
+ { "yen", "eWVu" },
+ { "yet", "eWV0" },
+ { "yin", "eWlu" },
+ { "yip", "eWlw" },
+ { "yon", "eW9u" },
+ { "you", "eW91" },
+ { "yow", "eW93" },
+ { "y's", "eSdz" },
+ { "yuh", "eXVo" },
+ { "zag", "emFn" },
+ { "Zan", "WmFu" },
+ { "zap", "emFw" },
+ { "Zen", "WmVu" },
+ { "zig", "emln" },
+ { "zip", "emlw" },
+ { "Zoe", "Wm9l" },
+ { "zoo", "em9v" },
+ { "z's", "eidz" },
+ /* the false rumors file */
+ { "\"So when I die, the first thing I will see in heaven is a score list?\"",
+ "IlNvIHdoZW4gSSBkaWUsIHRoZSBmaXJzdCB0aGluZyBJIHdpbGwgc2VlIGluIGhlYXZlbiBpcyBhIHNjb3JlIGxpc3Q/Ig==" },
+ { "1st Law of Hacking: leaving is much more difficult than entering.",
+ "MXN0IExhdyBvZiBIYWNraW5nOiBsZWF2aW5nIGlzIG11Y2ggbW9yZSBkaWZmaWN1bHQgdGhhbiBlbnRlcmluZy4=" },
+ { "2nd Law of Hacking: first in, first out.",
+ "Mm5kIExhdyBvZiBIYWNraW5nOiBmaXJzdCBpbiwgZmlyc3Qgb3V0Lg==" },
+ { "3rd Law of Hacking: the last blow counts most.",
+ "M3JkIExhdyBvZiBIYWNraW5nOiB0aGUgbGFzdCBibG93IGNvdW50cyBtb3N0Lg==" },
+ { "4th Law of Hacking: you will find the exit at the entrance.",
+ "NHRoIExhdyBvZiBIYWNraW5nOiB5b3Ugd2lsbCBmaW5kIHRoZSBleGl0IGF0IHRoZSBlbnRyYW5jZS4=" },
+ { "A chameleon imitating a mail daemon often delivers scrolls of fire.",
+ "QSBjaGFtZWxlb24gaW1pdGF0aW5nIGEgbWFpbCBkYWVtb24gb2Z0ZW4gZGVsaXZlcnMgc2Nyb2xscyBvZiBmaXJlLg==" },
+ { "A cockatrice corpse is guaranteed to be untainted!",
+ "QSBjb2NrYXRyaWNlIGNvcnBzZSBpcyBndWFyYW50ZWVkIHRvIGJlIHVudGFpbnRlZCE=" },
+ { "A dead cockatrice is just a dead lizard.",
+ "QSBkZWFkIGNvY2thdHJpY2UgaXMganVzdCBhIGRlYWQgbGl6YXJkLg==" },
+ { "A dragon is just a snake that ate a scroll of fire.",
+ "QSBkcmFnb24gaXMganVzdCBhIHNuYWtlIHRoYXQgYXRlIGEgc2Nyb2xsIG9mIGZpcmUu" },
+ { "A fading corridor enlightens your insight.",
+ "QSBmYWRpbmcgY29ycmlkb3IgZW5saWdodGVucyB5b3VyIGluc2lnaHQu" },
+ { "A glowing potion is too hot to drink.",
+ "QSBnbG93aW5nIHBvdGlvbiBpcyB0b28gaG90IHRvIGRyaW5rLg==" },
+ { "A good amulet may protect you against guards.",
+ "QSBnb29kIGFtdWxldCBtYXkgcHJvdGVjdCB5b3UgYWdhaW5zdCBndWFyZHMu" },
+ { "A lizard corpse is a good thing to turn undead.",
+ "QSBsaXphcmQgY29ycHNlIGlzIGEgZ29vZCB0aGluZyB0byB0dXJuIHVuZGVhZC4=" },
+ { "A long worm can be defined recursively. So how should you attack it?",
+ "QSBsb25nIHdvcm0gY2FuIGJlIGRlZmluZWQgcmVjdXJzaXZlbHkuIFNvIGhvdyBzaG91bGQgeW91IGF0dGFjayBpdD8=" },
+ { "A monstrous mind is a toy forever.",
+ "QSBtb25zdHJvdXMgbWluZCBpcyBhIHRveSBmb3JldmVyLg==" },
+ { "A nymph will be very pleased if you call her by her real name: Lorelei.",
+ "QSBueW1waCB3aWxsIGJlIHZlcnkgcGxlYXNlZCBpZiB5b3UgY2FsbCBoZXIgYnkgaGVyIHJlYWwgbmFtZTogTG9yZWxlaS4=" },
+ { "A ring of dungeon master control is a great find.",
+ "QSByaW5nIG9mIGR1bmdlb24gbWFzdGVyIGNvbnRyb2wgaXMgYSBncmVhdCBmaW5kLg==" },
+ { "A ring of extra ring finger is useless if not enchanted.",
+ "QSByaW5nIG9mIGV4dHJhIHJpbmcgZmluZ2VyIGlzIHVzZWxlc3MgaWYgbm90IGVuY2hhbnRlZC4=" },
+ { "A rope may form a trail in a maze.",
+ "QSByb3BlIG1heSBmb3JtIGEgdHJhaWwgaW4gYSBtYXplLg==" },
+ { "A staff may recharge if you drop it for awhile.",
+ "QSBzdGFmZiBtYXkgcmVjaGFyZ2UgaWYgeW91IGRyb3AgaXQgZm9yIGF3aGlsZS4=" },
+ { "A visit to the Zoo is very educational; you meet interesting animals.",
+ "QSB2aXNpdCB0byB0aGUgWm9vIGlzIHZlcnkgZWR1Y2F0aW9uYWw7IHlvdSBtZWV0IGludGVyZXN0aW5nIGFuaW1hbHMu" },
+ { "A wand of deaf is a more dangerous weapon than a wand of sheep.",
+ "QSB3YW5kIG9mIGRlYWYgaXMgYSBtb3JlIGRhbmdlcm91cyB3ZWFwb24gdGhhbiBhIHdhbmQgb2Ygc2hlZXAu" },
+ { "A wand of vibration might bring the whole cave crashing about your ears.",
+ "QSB3YW5kIG9mIHZpYnJhdGlvbiBtaWdodCBicmluZyB0aGUgd2hvbGUgY2F2ZSBjcmFzaGluZyBhYm91dCB5b3VyIGVhcnMu" },
+ { "A winner never quits. A quitter never wins.",
+ "QSB3aW5uZXIgbmV2ZXIgcXVpdHMuIEEgcXVpdHRlciBuZXZlciB3aW5zLg==" },
+ { "A wish? Okay, make me a fortune cookie!",
+ "QSB3aXNoPyBPa2F5LCBtYWtlIG1lIGEgZm9ydHVuZSBjb29raWUh" },
+ { "Afraid of mimics? Try to wear a ring of true seeing.",
+ "QWZyYWlkIG9mIG1pbWljcz8gVHJ5IHRvIHdlYXIgYSByaW5nIG9mIHRydWUgc2VlaW5nLg==" },
+ { "All monsters are created evil, but some are more evil than others.",
+ "QWxsIG1vbnN0ZXJzIGFyZSBjcmVhdGVkIGV2aWwsIGJ1dCBzb21lIGFyZSBtb3JlIGV2aWwgdGhhbiBvdGhlcnMu" },
+ { "Always attack a floating eye from behind!",
+ "QWx3YXlzIGF0dGFjayBhIGZsb2F0aW5nIGV5ZSBmcm9tIGJlaGluZCE=" },
+ { "An elven cloak is always the height of fashion.",
+ "QW4gZWx2ZW4gY2xvYWsgaXMgYWx3YXlzIHRoZSBoZWlnaHQgb2YgZmFzaGlvbi4=" },
+ { "Any small object that is accidentally dropped will hide under a larger object.",
+ "QW55IHNtYWxsIG9iamVjdCB0aGF0IGlzIGFjY2lkZW50YWxseSBkcm9wcGVkIHdpbGwgaGlkZSB1bmRlciBhIGxhcmdlciBvYmplY3Qu" },
+ { "Balrogs do not appear above level 20.",
+ "QmFscm9ncyBkbyBub3QgYXBwZWFyIGFib3ZlIGxldmVsIDIwLg==" },
+ { "Banana peels work especially well against Keystone Kops.",
+ "QmFuYW5hIHBlZWxzIHdvcmsgZXNwZWNpYWxseSB3ZWxsIGFnYWluc3QgS2V5c3RvbmUgS29wcy4=" },
+ { "Be careful when eating bananas. Monsters might slip on the peels.",
+ "QmUgY2FyZWZ1bCB3aGVuIGVhdGluZyBiYW5hbmFzLiBNb25zdGVycyBtaWdodCBzbGlwIG9uIHRoZSBwZWVscy4=" },
+ { "Better leave the dungeon; otherwise you might get hurt badly.",
+ "QmV0dGVyIGxlYXZlIHRoZSBkdW5nZW9uOyBvdGhlcndpc2UgeW91IG1pZ2h0IGdldCBodXJ0IGJhZGx5Lg==" },
+ { "Beware of the potion of nitroglycerin -- it's not for the weak of heart.",
+ "QmV3YXJlIG9mIHRoZSBwb3Rpb24gb2Ygbml0cm9nbHljZXJpbiAtLSBpdCdzIG5vdCBmb3IgdGhlIHdlYWsgb2YgaGVhcnQu" },
+ { "Beware: there's always a chance that your wand explodes as you try to zap it!",
+ "QmV3YXJlOiB0aGVyZSdzIGFsd2F5cyBhIGNoYW5jZSB0aGF0IHlvdXIgd2FuZCBleHBsb2RlcyBhcyB5b3UgdHJ5IHRvIHphcCBpdCE=" },
+ { "Beyond the 23rd level lies a happy retirement in a room of your own.",
+ "QmV5b25kIHRoZSAyM3JkIGxldmVsIGxpZXMgYSBoYXBweSByZXRpcmVtZW50IGluIGEgcm9vbSBvZiB5b3VyIG93bi4=" },
+ { "Changing your suit without dropping your sword? You must be kidding!",
+ "Q2hhbmdpbmcgeW91ciBzdWl0IHdpdGhvdXQgZHJvcHBpbmcgeW91ciBzd29yZD8gWW91IG11c3QgYmUga2lkZGluZyE=" },
+ { "Cockatrices might turn themselves to stone faced with a mirror.",
+ "Q29ja2F0cmljZXMgbWlnaHQgdHVybiB0aGVtc2VsdmVzIHRvIHN0b25lIGZhY2VkIHdpdGggYSBtaXJyb3Iu" },
+ { "Consumption of home-made food is strictly forbidden in this dungeon.",
+ "Q29uc3VtcHRpb24gb2YgaG9tZS1tYWRlIGZvb2QgaXMgc3RyaWN0bHkgZm9yYmlkZGVuIGluIHRoaXMgZHVuZ2Vvbi4=" },
+ { "Dark room? Your chance to develop your photographs!",
+ "RGFyayByb29tPyBZb3VyIGNoYW5jZSB0byBkZXZlbG9wIHlvdXIgcGhvdG9ncmFwaHMh" },
+ { "Dark rooms are not *completely* dark: just wait and let your eyes adjust...",
+ "RGFyayByb29tcyBhcmUgbm90ICpjb21wbGV0ZWx5KiBkYXJrOiBqdXN0IHdhaXQgYW5kIGxldCB5b3VyIGV5ZXMgYWRqdXN0Li4u" },
+ { "David London sez, \"Hey guys, *WIELD* a lizard corpse against a cockatrice!\"",
+ "RGF2aWQgTG9uZG9uIHNleiwgIkhleSBndXlzLCAqV0lFTEQqIGEgbGl6YXJkIGNvcnBzZSBhZ2FpbnN0IGEgY29ja2F0cmljZSEi" },
+ { "Death is just life's way of telling you you've been fired.",
+ "RGVhdGggaXMganVzdCBsaWZlJ3Mgd2F5IG9mIHRlbGxpbmcgeW91IHlvdSd2ZSBiZWVuIGZpcmVkLg==" },
+ { "Demi-gods don't need any help from the gods.",
+ "RGVtaS1nb2RzIGRvbid0IG5lZWQgYW55IGhlbHAgZnJvbSB0aGUgZ29kcy4=" },
+ { "Demons *HATE* Priests and Priestesses.",
+ "RGVtb25zICpIQVRFKiBQcmllc3RzIGFuZCBQcmllc3Rlc3Nlcy4=" },
+ { "Didn't you forget to pay?",
+ "RGlkbid0IHlvdSBmb3JnZXQgdG8gcGF5Pw==" },
+ { "Didn't your mother tell you not to eat food off the floor?",
+ "RGlkbid0IHlvdXIgbW90aGVyIHRlbGwgeW91IG5vdCB0byBlYXQgZm9vZCBvZmYgdGhlIGZsb29yPw==" },
+ { "Direct a direct hit on your direct opponent, directing in the right direction.",
+ "RGlyZWN0IGEgZGlyZWN0IGhpdCBvbiB5b3VyIGRpcmVjdCBvcHBvbmVudCwgZGlyZWN0aW5nIGluIHRoZSByaWdodCBkaXJlY3Rpb24u" },
+ { "Do you want to make more money? Sure, we all do! Join the Fort Ludios guard!",
+ "RG8geW91IHdhbnQgdG8gbWFrZSBtb3JlIG1vbmV5PyBTdXJlLCB3ZSBhbGwgZG8hIEpvaW4gdGhlIEZvcnQgTHVkaW9zIGd1YXJkIQ==" },
+ { "Don't eat too much: you might start hiccoughing!",
+ "RG9uJ3QgZWF0IHRvbyBtdWNoOiB5b3UgbWlnaHQgc3RhcnQgaGljY291Z2hpbmch" },
+ { "Don't play hack at your work; your boss might hit you!",
+ "RG9uJ3QgcGxheSBoYWNrIGF0IHlvdXIgd29yazsgeW91ciBib3NzIG1pZ2h0IGhpdCB5b3Uh" },
+ { "Don't tell a soul you found a secret door, otherwise it isn't a secret anymore.",
+ "RG9uJ3QgdGVsbCBhIHNvdWwgeW91IGZvdW5kIGEgc2VjcmV0IGRvb3IsIG90aGVyd2lzZSBpdCBpc24ndCBhIHNlY3JldCBhbnltb3JlLg==" },
+ { "Drinking potions of booze may land you in jail if you are under 21.",
+ "RHJpbmtpbmcgcG90aW9ucyBvZiBib296ZSBtYXkgbGFuZCB5b3UgaW4gamFpbCBpZiB5b3UgYXJlIHVuZGVyIDIxLg==" },
+ { "Drop your vanity and get rid of your jewels! Pickpockets about!",
+ "RHJvcCB5b3VyIHZhbml0eSBhbmQgZ2V0IHJpZCBvZiB5b3VyIGpld2VscyEgUGlja3BvY2tldHMgYWJvdXQh" },
+ { "Eat 10 cloves of garlic and keep all humans at a two-square distance.",
+ "RWF0IDEwIGNsb3ZlcyBvZiBnYXJsaWMgYW5kIGtlZXAgYWxsIGh1bWFucyBhdCBhIHR3by1zcXVhcmUgZGlzdGFuY2Uu" },
+ { "Eels hide under mud. Use a unicorn to clear the water and make them visible.",
+ "RWVscyBoaWRlIHVuZGVyIG11ZC4gVXNlIGEgdW5pY29ybiB0byBjbGVhciB0aGUgd2F0ZXIgYW5kIG1ha2UgdGhlbSB2aXNpYmxlLg==" },
+ { "Engrave your wishes with a wand of wishing.",
+ "RW5ncmF2ZSB5b3VyIHdpc2hlcyB3aXRoIGEgd2FuZCBvZiB3aXNoaW5nLg==" },
+ { "Eventually you will come to admire the swift elegance of a retreating nymph.",
+ "RXZlbnR1YWxseSB5b3Ugd2lsbCBjb21lIHRvIGFkbWlyZSB0aGUgc3dpZnQgZWxlZ2FuY2Ugb2YgYSByZXRyZWF0aW5nIG55bXBoLg==" },
+ { "Ever heard hissing outside? I *knew* you hadn't!",
+ "RXZlciBoZWFyZCBoaXNzaW5nIG91dHNpZGU/IEkgKmtuZXcqIHlvdSBoYWRuJ3Qh" },
+ { "Ever lifted a dragon corpse?",
+ "RXZlciBsaWZ0ZWQgYSBkcmFnb24gY29ycHNlPw==" },
+ { "Ever seen a leocrotta dancing the tengu?",
+ "RXZlciBzZWVuIGEgbGVvY3JvdHRhIGRhbmNpbmcgdGhlIHRlbmd1Pw==" },
+ { "Ever seen your weapon glow plaid?",
+ "RXZlciBzZWVuIHlvdXIgd2VhcG9uIGdsb3cgcGxhaWQ/" },
+ { "Ever tamed a shopkeeper?",
+ "RXZlciB0YW1lZCBhIHNob3BrZWVwZXI/" },
+ { "Ever tried digging through a Vault Guard?",
+ "RXZlciB0cmllZCBkaWdnaW5nIHRocm91Z2ggYSBWYXVsdCBHdWFyZD8=" },
+ { "Ever tried enchanting a rope?",
+ "RXZlciB0cmllZCBlbmNoYW50aW5nIGEgcm9wZT8=" },
+ { "Floating eyes can't stand Hawaiian shirts.",
+ "RmxvYXRpbmcgZXllcyBjYW4ndCBzdGFuZCBIYXdhaWlhbiBzaGlydHMu" },
+ { "For any remedy there is a misery.",
+ "Rm9yIGFueSByZW1lZHkgdGhlcmUgaXMgYSBtaXNlcnku" },
+ { "Giant bats turn into giant vampires.",
+ "R2lhbnQgYmF0cyB0dXJuIGludG8gZ2lhbnQgdmFtcGlyZXMu" },
+ { "Good day for overcoming obstacles. Try a steeplechase.",
+ "R29vZCBkYXkgZm9yIG92ZXJjb21pbmcgb2JzdGFjbGVzLiBUcnkgYSBzdGVlcGxlY2hhc2Uu" },
+ { "Half Moon tonight. (At least it's better than no Moon at all.)",
+ "SGFsZiBNb29uIHRvbmlnaHQuIChBdCBsZWFzdCBpdCdzIGJldHRlciB0aGFuIG5vIE1vb24gYXQgYWxsLik=" },
+ { "Help! I'm being held prisoner in a fortune cookie factory!",
+ "SGVscCEgSSdtIGJlaW5nIGhlbGQgcHJpc29uZXIgaW4gYSBmb3J0dW5lIGNvb2tpZSBmYWN0b3J5IQ==" },
+ { "Housecats have nine lives, kittens only one.",
+ "SG91c2VjYXRzIGhhdmUgbmluZSBsaXZlcywga2l0dGVucyBvbmx5IG9uZS4=" },
+ { "How long can you tread water?",
+ "SG93IGxvbmcgY2FuIHlvdSB0cmVhZCB3YXRlcj8=" },
+ { "Hungry? There is an abundance of food on the next level.",
+ "SHVuZ3J5PyBUaGVyZSBpcyBhbiBhYnVuZGFuY2Ugb2YgZm9vZCBvbiB0aGUgbmV4dCBsZXZlbC4=" },
+ { "I guess you've never hit a mail daemon with the Amulet of Yendor...",
+ "SSBndWVzcyB5b3UndmUgbmV2ZXIgaGl0IGEgbWFpbCBkYWVtb24gd2l0aCB0aGUgQW11bGV0IG9mIFllbmRvci4uLg==" },
+ { "If you are the shopkeeper, you can take things for free.",
+ "SWYgeW91IGFyZSB0aGUgc2hvcGtlZXBlciwgeW91IGNhbiB0YWtlIHRoaW5ncyBmb3IgZnJlZS4=" },
+ { "If you can't learn to do it well, learn to enjoy doing it badly.",
+ "SWYgeW91IGNhbid0IGxlYXJuIHRvIGRvIGl0IHdlbGwsIGxlYXJuIHRvIGVuam95IGRvaW5nIGl0IGJhZGx5Lg==" },
+ { "If you thought the Wizard was bad, just wait till you meet the Warlord!",
+ "SWYgeW91IHRob3VnaHQgdGhlIFdpemFyZCB3YXMgYmFkLCBqdXN0IHdhaXQgdGlsbCB5b3UgbWVldCB0aGUgV2FybG9yZCE=" },
+ { "If you turn blind, don't expect your dog to be turned into a seeing-eye dog.",
+ "SWYgeW91IHR1cm4gYmxpbmQsIGRvbid0IGV4cGVjdCB5b3VyIGRvZyB0byBiZSB0dXJuZWQgaW50byBhIHNlZWluZy1leWUgZG9nLg==" },
+ { "If you want to feel great, you must eat something real big.",
+ "SWYgeW91IHdhbnQgdG8gZmVlbCBncmVhdCwgeW91IG11c3QgZWF0IHNvbWV0aGluZyByZWFsIGJpZy4=" },
+ { "If you want to float, you'd better eat a floating eye.",
+ "SWYgeW91IHdhbnQgdG8gZmxvYXQsIHlvdSdkIGJldHRlciBlYXQgYSBmbG9hdGluZyBleWUu" },
+ { "If your ghost kills a player, it increases your score.",
+ "SWYgeW91ciBnaG9zdCBraWxscyBhIHBsYXllciwgaXQgaW5jcmVhc2VzIHlvdXIgc2NvcmUu" },
+ { "Increase mindpower: Tame your own ghost!",
+ "SW5jcmVhc2UgbWluZHBvd2VyOiBUYW1lIHlvdXIgb3duIGdob3N0IQ==" },
+ { "It furthers one to see the great man.",
+ "SXQgZnVydGhlcnMgb25lIHRvIHNlZSB0aGUgZ3JlYXQgbWFuLg==" },
+ { "It's easy to overlook a monster in a wood.",
+ "SXQncyBlYXN5IHRvIG92ZXJsb29rIGEgbW9uc3RlciBpbiBhIHdvb2Qu" },
+ { "Just below any trapdoor there may be another one. Just keep falling!",
+ "SnVzdCBiZWxvdyBhbnkgdHJhcGRvb3IgdGhlcmUgbWF5IGJlIGFub3RoZXIgb25lLiBKdXN0IGtlZXAgZmFsbGluZyE=" },
+ { "Katanas are very sharp; watch you don't cut yourself.",
+ "S2F0YW5hcyBhcmUgdmVyeSBzaGFycDsgd2F0Y2ggeW91IGRvbid0IGN1dCB5b3Vyc2VsZi4=" },
+ { "Keep a clear mind: quaff clear potions.",
+ "S2VlcCBhIGNsZWFyIG1pbmQ6IHF1YWZmIGNsZWFyIHBvdGlvbnMu" },
+ { "Kicking the terminal doesn't hurt the monsters.",
+ "S2lja2luZyB0aGUgdGVybWluYWwgZG9lc24ndCBodXJ0IHRoZSBtb25zdGVycy4=" },
+ { "Killer bees keep appearing till you kill their queen.",
+ "S2lsbGVyIGJlZXMga2VlcCBhcHBlYXJpbmcgdGlsbCB5b3Uga2lsbCB0aGVpciBxdWVlbi4=" },
+ { "Killer bunnies can be tamed with carrots only.",
+ "S2lsbGVyIGJ1bm5pZXMgY2FuIGJlIHRhbWVkIHdpdGggY2Fycm90cyBvbmx5Lg==" },
+ { "Latest news? Put `rec.games.roguelike.nethack' in your .newsrc!",
+ "TGF0ZXN0IG5ld3M/IFB1dCBgcmVjLmdhbWVzLnJvZ3VlbGlrZS5uZXRoYWNrJyBpbiB5b3VyIC5uZXdzcmMh" },
+ { "Learn how to spell. Play NetHack!",
+ "TGVhcm4gaG93IHRvIHNwZWxsLiBQbGF5IE5ldEhhY2sh" },
+ { "Leprechauns hide their gold in a secret room.",
+ "TGVwcmVjaGF1bnMgaGlkZSB0aGVpciBnb2xkIGluIGEgc2VjcmV0IHJvb20u" },
+ { "Let your fingers do the walking on the yulkjhnb keys.",
+ "TGV0IHlvdXIgZmluZ2VycyBkbyB0aGUgd2Fsa2luZyBvbiB0aGUgeXVsa2pobmIga2V5cy4=" },
+ { "Let's face it: this time you're not going to win.",
+ "TGV0J3MgZmFjZSBpdDogdGhpcyB0aW1lIHlvdSdyZSBub3QgZ29pbmcgdG8gd2luLg==" },
+ { "Let's have a party, drink a lot of booze.",
+ "TGV0J3MgaGF2ZSBhIHBhcnR5LCBkcmluayBhIGxvdCBvZiBib296ZS4=" },
+ { "Liquor sellers do not drink; they hate to see you twice.",
+ "TGlxdW9yIHNlbGxlcnMgZG8gbm90IGRyaW5rOyB0aGV5IGhhdGUgdG8gc2VlIHlvdSB0d2ljZS4=" },
+ { "Lunar eclipse tonight. May as well quit now!",
+ "THVuYXIgZWNsaXBzZSB0b25pZ2h0LiBNYXkgYXMgd2VsbCBxdWl0IG5vdyE=" },
+ { "Meeting your own ghost decreases your luck considerably!",
+ "TWVldGluZyB5b3VyIG93biBnaG9zdCBkZWNyZWFzZXMgeW91ciBsdWNrIGNvbnNpZGVyYWJseSE=" },
+ { "Money to invest? Take it to the local branch of the Magic Memory Vault!",
+ "TW9uZXkgdG8gaW52ZXN0PyBUYWtlIGl0IHRvIHRoZSBsb2NhbCBicmFuY2ggb2YgdGhlIE1hZ2ljIE1lbW9yeSBWYXVsdCE=" },
+ { "Monsters come from nowhere to hit you everywhere.",
+ "TW9uc3RlcnMgY29tZSBmcm9tIG5vd2hlcmUgdG8gaGl0IHlvdSBldmVyeXdoZXJlLg==" },
+ { "Monsters sleep because you are boring, not because they ever get tired.",
+ "TW9uc3RlcnMgc2xlZXAgYmVjYXVzZSB5b3UgYXJlIGJvcmluZywgbm90IGJlY2F1c2UgdGhleSBldmVyIGdldCB0aXJlZC4=" },
+ { "Most monsters prefer minced meat. That's why they are hitting you!",
+ "TW9zdCBtb25zdGVycyBwcmVmZXIgbWluY2VkIG1lYXQuIFRoYXQncyB3aHkgdGhleSBhcmUgaGl0dGluZyB5b3Uh" },
+ { "Most of the bugs in NetHack are on the floor.",
+ "TW9zdCBvZiB0aGUgYnVncyBpbiBOZXRIYWNrIGFyZSBvbiB0aGUgZmxvb3Iu" },
+ { "Much ado Nothing Happens.",
+ "TXVjaCBhZG8gTm90aGluZyBIYXBwZW5zLg==" },
+ { "Multi-player NetHack is a myth.",
+ "TXVsdGktcGxheWVyIE5ldEhhY2sgaXMgYSBteXRoLg==" },
+ { "NetHack is addictive. Too late, you're already hooked.",
+ "TmV0SGFjayBpcyBhZGRpY3RpdmUuIFRvbyBsYXRlLCB5b3UncmUgYWxyZWFkeSBob29rZWQu" },
+ { "Never ask a shopkeeper for a price list.",
+ "TmV2ZXIgYXNrIGEgc2hvcGtlZXBlciBmb3IgYSBwcmljZSBsaXN0Lg==" },
+ { "Never burn a tree, unless you like getting whacked with a +5 shovel.",
+ "TmV2ZXIgYnVybiBhIHRyZWUsIHVubGVzcyB5b3UgbGlrZSBnZXR0aW5nIHdoYWNrZWQgd2l0aCBhICs1IHNob3ZlbC4=" },
+ { "Never eat with glowing hands!",
+ "TmV2ZXIgZWF0IHdpdGggZ2xvd2luZyBoYW5kcyE=" },
+ { "Never mind the monsters hitting you: they just replace the charwomen.",
+ "TmV2ZXIgbWluZCB0aGUgbW9uc3RlcnMgaGl0dGluZyB5b3U6IHRoZXkganVzdCByZXBsYWNlIHRoZSBjaGFyd29tZW4u" },
+ { "Never play leapfrog with a unicorn.",
+ "TmV2ZXIgcGxheSBsZWFwZnJvZyB3aXRoIGEgdW5pY29ybi4=" },
+ { "Never step on a cursed engraving.",
+ "TmV2ZXIgc3RlcCBvbiBhIGN1cnNlZCBlbmdyYXZpbmcu" },
+ { "Never swim with a camera: there's nothing to take pictures of.",
+ "TmV2ZXIgc3dpbSB3aXRoIGEgY2FtZXJhOiB0aGVyZSdzIG5vdGhpbmcgdG8gdGFrZSBwaWN0dXJlcyBvZi4=" },
+ { "Never teach your pet rust monster to fetch.",
+ "TmV2ZXIgdGVhY2ggeW91ciBwZXQgcnVzdCBtb25zdGVyIHRvIGZldGNoLg==" },
+ { "Never trust a random generator in magic fields.",
+ "TmV2ZXIgdHJ1c3QgYSByYW5kb20gZ2VuZXJhdG9yIGluIG1hZ2ljIGZpZWxkcy4=" },
+ { "Never use a wand of death.",
+ "TmV2ZXIgdXNlIGEgd2FuZCBvZiBkZWF0aC4=" },
+ { "No level contains two shops. The maze is no level. So...",
+ "Tm8gbGV2ZWwgY29udGFpbnMgdHdvIHNob3BzLiBUaGUgbWF6ZSBpcyBubyBsZXZlbC4gU28uLi4=" },
+ { "No part of this fortune may be reproduced, stored in a retrieval system, ...",
+ "Tm8gcGFydCBvZiB0aGlzIGZvcnR1bmUgbWF5IGJlIHJlcHJvZHVjZWQsIHN0b3JlZCBpbiBhIHJldHJpZXZhbCBzeXN0ZW0sIC4uLg==" },
+ { "Not all rumors are as misleading as this one.",
+ "Tm90IGFsbCBydW1vcnMgYXJlIGFzIG1pc2xlYWRpbmcgYXMgdGhpcyBvbmUu" },
+ { "Nymphs and nurses like beautiful rings.",
+ "TnltcGhzIGFuZCBudXJzZXMgbGlrZSBiZWF1dGlmdWwgcmluZ3Mu" },
+ { "Nymphs are blondes. Are you a gentleman?",
+ "TnltcGhzIGFyZSBibG9uZGVzLiBBcmUgeW91IGEgZ2VudGxlbWFuPw==" },
+ { "Offering a unicorn a worthless piece of glass might prove to be fatal!",
+ "T2ZmZXJpbmcgYSB1bmljb3JuIGEgd29ydGhsZXNzIHBpZWNlIG9mIGdsYXNzIG1pZ2h0IHByb3ZlIHRvIGJlIGZhdGFsIQ==" },
+ { "Old hackers never die: young ones do.",
+ "T2xkIGhhY2tlcnMgbmV2ZXIgZGllOiB5b3VuZyBvbmVzIGRvLg==" },
+ { "One has to leave shops before closing time.",
+ "T25lIGhhcyB0byBsZWF2ZSBzaG9wcyBiZWZvcmUgY2xvc2luZyB0aW1lLg==" },
+ { "One homunculus a day keeps the doctor away.",
+ "T25lIGhvbXVuY3VsdXMgYSBkYXkga2VlcHMgdGhlIGRvY3RvciBhd2F5Lg==" },
+ { "One level further down somebody is getting killed, right now.",
+ "T25lIGxldmVsIGZ1cnRoZXIgZG93biBzb21lYm9keSBpcyBnZXR0aW5nIGtpbGxlZCwgcmlnaHQgbm93Lg==" },
+ { "Only a wizard can use a magic whistle.",
+ "T25seSBhIHdpemFyZCBjYW4gdXNlIGEgbWFnaWMgd2hpc3RsZS4=" },
+ { "Only adventurers of evil alignment think of killing their dog.",
+ "T25seSBhZHZlbnR1cmVycyBvZiBldmlsIGFsaWdubWVudCB0aGluayBvZiBraWxsaW5nIHRoZWlyIGRvZy4=" },
+ { "Only chaotic evils kill sleeping monsters.",
+ "T25seSBjaGFvdGljIGV2aWxzIGtpbGwgc2xlZXBpbmcgbW9uc3RlcnMu" },
+ { "Only real trappers escape traps.",
+ "T25seSByZWFsIHRyYXBwZXJzIGVzY2FwZSB0cmFwcy4=" },
+ { "Only real wizards can write scrolls.",
+ "T25seSByZWFsIHdpemFyZHMgY2FuIHdyaXRlIHNjcm9sbHMu" },
+ { "Operation OVERKILL has started now.",
+ "T3BlcmF0aW9uIE9WRVJLSUxMIGhhcyBzdGFydGVkIG5vdy4=" },
+ { "PLEASE ignore previous rumor.",
+ "UExFQVNFIGlnbm9yZSBwcmV2aW91cyBydW1vci4=" },
+ { "Polymorph into an ettin; meet your opponents face to face to face.",
+ "UG9seW1vcnBoIGludG8gYW4gZXR0aW47IG1lZXQgeW91ciBvcHBvbmVudHMgZmFjZSB0byBmYWNlIHRvIGZhY2Uu" },
+ { "Praying will frighten demons.",
+ "UHJheWluZyB3aWxsIGZyaWdodGVuIGRlbW9ucy4=" },
+ { "Row (3x) that boat gently down the stream, Charon (4x), death is but a dream.",
+ "Um93ICgzeCkgdGhhdCBib2F0IGdlbnRseSBkb3duIHRoZSBzdHJlYW0sIENoYXJvbiAoNHgpLCBkZWF0aCBpcyBidXQgYSBkcmVhbS4=" },
+ { "Running is good for your legs.",
+ "UnVubmluZyBpcyBnb29kIGZvciB5b3VyIGxlZ3Mu" },
+ { "Screw up your courage! You've screwed up everything else.",
+ "U2NyZXcgdXAgeW91ciBjb3VyYWdlISBZb3UndmUgc2NyZXdlZCB1cCBldmVyeXRoaW5nIGVsc2Uu" },
+ { "Seepage? Leaky pipes? Rising damp? Summon the plumber!",
+ "U2VlcGFnZT8gTGVha3kgcGlwZXM/IFJpc2luZyBkYW1wPyBTdW1tb24gdGhlIHBsdW1iZXIh" },
+ { "Segmentation fault (core dumped).",
+ "U2VnbWVudGF0aW9uIGZhdWx0IChjb3JlIGR1bXBlZCku" },
+ { "Shopkeepers sometimes die from old age.",
+ "U2hvcGtlZXBlcnMgc29tZXRpbWVzIGRpZSBmcm9tIG9sZCBhZ2Uu" },
+ { "Some mazes (especially small ones) have no solutions, says man 6 maze.",
+ "U29tZSBtYXplcyAoZXNwZWNpYWxseSBzbWFsbCBvbmVzKSBoYXZlIG5vIHNvbHV0aW9ucywgc2F5cyBtYW4gNiBtYXplLg==" },
+ { "Some questions the Sphynx asks just *don't* have any answers.",
+ "U29tZSBxdWVzdGlvbnMgdGhlIFNwaHlueCBhc2tzIGp1c3QgKmRvbid0KiBoYXZlIGFueSBhbnN3ZXJzLg==" },
+ { "Sometimes \"mu\" is the answer.",
+ "U29tZXRpbWVzICJtdSIgaXMgdGhlIGFuc3dlci4=" },
+ { "Sorry, no fortune this time. Better luck next cookie!",
+ "U29ycnksIG5vIGZvcnR1bmUgdGhpcyB0aW1lLiBCZXR0ZXIgbHVjayBuZXh0IGNvb2tpZSE=" },
+ { "Spare your scrolls of make-edible until it's really necessary!",
+ "U3BhcmUgeW91ciBzY3JvbGxzIG9mIG1ha2UtZWRpYmxlIHVudGlsIGl0J3MgcmVhbGx5IG5lY2Vzc2FyeSE=" },
+ { "Suddenly, the dungeon will collapse...",
+ "U3VkZGVubHksIHRoZSBkdW5nZW9uIHdpbGwgY29sbGFwc2UuLi4=" },
+ { "Taming a mail daemon may cause a system security violation.",
+ "VGFtaW5nIGEgbWFpbCBkYWVtb24gbWF5IGNhdXNlIGEgc3lzdGVtIHNlY3VyaXR5IHZpb2xhdGlvbi4=" },
+ { "The crowd was so tough, the Stooges won't play the Dungeon anymore, nyuk nyuk.",
+ "VGhlIGNyb3dkIHdhcyBzbyB0b3VnaCwgdGhlIFN0b29nZXMgd29uJ3QgcGxheSB0aGUgRHVuZ2VvbiBhbnltb3JlLCBueXVrIG55dWsu" },
+ { "The leprechauns hide their treasure in a small hidden room.",
+ "VGhlIGxlcHJlY2hhdW5zIGhpZGUgdGhlaXIgdHJlYXN1cmUgaW4gYSBzbWFsbCBoaWRkZW4gcm9vbS4=" },
+ { "The longer the wand the better.",
+ "VGhlIGxvbmdlciB0aGUgd2FuZCB0aGUgYmV0dGVyLg==" },
+ { "The magic word is \"XYZZY\".",
+ "VGhlIG1hZ2ljIHdvcmQgaXMgIlhZWlpZIi4=" },
+ { "The meek shall inherit your bones files.",
+ "VGhlIG1lZWsgc2hhbGwgaW5oZXJpdCB5b3VyIGJvbmVzIGZpbGVzLg==" },
+ { "The mines are dark and deep, and I have levels to go before I sleep.",
+ "VGhlIG1pbmVzIGFyZSBkYXJrIGFuZCBkZWVwLCBhbmQgSSBoYXZlIGxldmVscyB0byBnbyBiZWZvcmUgSSBzbGVlcC4=" },
+ { "The use of dynamite is dangerous.",
+ "VGhlIHVzZSBvZiBkeW5hbWl0ZSBpcyBkYW5nZXJvdXMu" },
+ { "There are no worms in the UNIX version.",
+ "VGhlcmUgYXJlIG5vIHdvcm1zIGluIHRoZSBVTklYIHZlcnNpb24u" },
+ { "There is a trap on this level!",
+ "VGhlcmUgaXMgYSB0cmFwIG9uIHRoaXMgbGV2ZWwh" },
+ { "They say that Demogorgon, Asmodeus, Orcus, Yeenoghu & Juiblex is no law firm.",
+ "VGhleSBzYXkgdGhhdCBEZW1vZ29yZ29uLCBBc21vZGV1cywgT3JjdXMsIFllZW5vZ2h1ICYgSnVpYmxleCBpcyBubyBsYXcgZmlybS4=" },
+ { "They say that Geryon has an evil twin, beware!",
+ "VGhleSBzYXkgdGhhdCBHZXJ5b24gaGFzIGFuIGV2aWwgdHdpbiwgYmV3YXJlIQ==" },
+ { "They say that Medusa would make a terrible pet.",
+ "VGhleSBzYXkgdGhhdCBNZWR1c2Egd291bGQgbWFrZSBhIHRlcnJpYmxlIHBldC4=" },
+ { "They say that NetHack bugs are Seldon planned.",
+ "VGhleSBzYXkgdGhhdCBOZXRIYWNrIGJ1Z3MgYXJlIFNlbGRvbiBwbGFubmVkLg==" },
+ { "They say that NetHack comes in 256 flavors.",
+ "VGhleSBzYXkgdGhhdCBOZXRIYWNrIGNvbWVzIGluIDI1NiBmbGF2b3JzLg==" },
+ { "They say that NetHack is just a computer game.",
+ "VGhleSBzYXkgdGhhdCBOZXRIYWNrIGlzIGp1c3QgYSBjb21wdXRlciBnYW1lLg==" },
+ { "They say that NetHack is more than just a computer game.",
+ "VGhleSBzYXkgdGhhdCBOZXRIYWNrIGlzIG1vcmUgdGhhbiBqdXN0IGEgY29tcHV0ZXIgZ2FtZS4=" },
+ { "They say that NetHack is never what it used to be.",
+ "VGhleSBzYXkgdGhhdCBOZXRIYWNrIGlzIG5ldmVyIHdoYXQgaXQgdXNlZCB0byBiZS4=" },
+ { "They say that a baby dragon is too small to hurt or help you.",
+ "VGhleSBzYXkgdGhhdCBhIGJhYnkgZHJhZ29uIGlzIHRvbyBzbWFsbCB0byBodXJ0IG9yIGhlbHAgeW91Lg==" },
+ { "They say that a black pudding is simply a brown pudding gone bad.",
+ "VGhleSBzYXkgdGhhdCBhIGJsYWNrIHB1ZGRpbmcgaXMgc2ltcGx5IGEgYnJvd24gcHVkZGluZyBnb25lIGJhZC4=" },
+ { "They say that a black sheep has 3 bags full of wool.",
+ "VGhleSBzYXkgdGhhdCBhIGJsYWNrIHNoZWVwIGhhcyAzIGJhZ3MgZnVsbCBvZiB3b29sLg==" },
+ { "They say that a blank scroll is like a blank check.",
+ "VGhleSBzYXkgdGhhdCBhIGJsYW5rIHNjcm9sbCBpcyBsaWtlIGEgYmxhbmsgY2hlY2su" },
+ { "They say that a cat named Morris has nine lives.",
+ "VGhleSBzYXkgdGhhdCBhIGNhdCBuYW1lZCBNb3JyaXMgaGFzIG5pbmUgbGl2ZXMu" },
+ { "They say that a desperate shopper might pay any price in a shop.",
+ "VGhleSBzYXkgdGhhdCBhIGRlc3BlcmF0ZSBzaG9wcGVyIG1pZ2h0IHBheSBhbnkgcHJpY2UgaW4gYSBzaG9wLg==" },
+ { "They say that a diamond dog is everybody's best friend.",
+ "VGhleSBzYXkgdGhhdCBhIGRpYW1vbmQgZG9nIGlzIGV2ZXJ5Ym9keSdzIGJlc3QgZnJpZW5kLg==" },
+ { "They say that a dwarf lord can carry a pick-axe because his armor is light.",
+ "VGhleSBzYXkgdGhhdCBhIGR3YXJmIGxvcmQgY2FuIGNhcnJ5IGEgcGljay1heGUgYmVjYXVzZSBoaXMgYXJtb3IgaXMgbGlnaHQu" },
+ { "They say that a floating eye can defeat Medusa.",
+ "VGhleSBzYXkgdGhhdCBhIGZsb2F0aW5nIGV5ZSBjYW4gZGVmZWF0IE1lZHVzYS4=" },
+ { "They say that a fortune only has 1 line and you can't read between it.",
+ "VGhleSBzYXkgdGhhdCBhIGZvcnR1bmUgb25seSBoYXMgMSBsaW5lIGFuZCB5b3UgY2FuJ3QgcmVhZCBiZXR3ZWVuIGl0Lg==" },
+ { "They say that a fortune only has 1 line, but you can read between it.",
+ "VGhleSBzYXkgdGhhdCBhIGZvcnR1bmUgb25seSBoYXMgMSBsaW5lLCBidXQgeW91IGNhbiByZWFkIGJldHdlZW4gaXQu" },
+ { "They say that a fountain looks nothing like a regularly erupting geyser.",
+ "VGhleSBzYXkgdGhhdCBhIGZvdW50YWluIGxvb2tzIG5vdGhpbmcgbGlrZSBhIHJlZ3VsYXJseSBlcnVwdGluZyBnZXlzZXIu" },
+ { "They say that a gold doubloon is worth more than its weight in gold.",
+ "VGhleSBzYXkgdGhhdCBhIGdvbGQgZG91Ymxvb24gaXMgd29ydGggbW9yZSB0aGFuIGl0cyB3ZWlnaHQgaW4gZ29sZC4=" },
+ { "They say that a grid bug won't pay a shopkeeper for zapping you in a shop.",
+ "VGhleSBzYXkgdGhhdCBhIGdyaWQgYnVnIHdvbid0IHBheSBhIHNob3BrZWVwZXIgZm9yIHphcHBpbmcgeW91IGluIGEgc2hvcC4=" },
+ { "They say that a gypsy could tell your fortune for a price.",
+ "VGhleSBzYXkgdGhhdCBhIGd5cHN5IGNvdWxkIHRlbGwgeW91ciBmb3J0dW5lIGZvciBhIHByaWNlLg==" },
+ { "They say that a hacker named Alice once level teleported by using a mirror.",
+ "VGhleSBzYXkgdGhhdCBhIGhhY2tlciBuYW1lZCBBbGljZSBvbmNlIGxldmVsIHRlbGVwb3J0ZWQgYnkgdXNpbmcgYSBtaXJyb3Iu" },
+ { "They say that a hacker named David once slew a giant with a sling and a rock.",
+ "VGhleSBzYXkgdGhhdCBhIGhhY2tlciBuYW1lZCBEYXZpZCBvbmNlIHNsZXcgYSBnaWFudCB3aXRoIGEgc2xpbmcgYW5kIGEgcm9jay4=" },
+ { "They say that a hacker named Dorothy once rode a fog cloud to Oz.",
+ "VGhleSBzYXkgdGhhdCBhIGhhY2tlciBuYW1lZCBEb3JvdGh5IG9uY2Ugcm9kZSBhIGZvZyBjbG91ZCB0byBPei4=" },
+ { "They say that a hacker named Mary once lost a white sheep in the mazes.",
+ "VGhleSBzYXkgdGhhdCBhIGhhY2tlciBuYW1lZCBNYXJ5IG9uY2UgbG9zdCBhIHdoaXRlIHNoZWVwIGluIHRoZSBtYXplcy4=" },
+ { "They say that a helm of brilliance is not to be taken lightly.",
+ "VGhleSBzYXkgdGhhdCBhIGhlbG0gb2YgYnJpbGxpYW5jZSBpcyBub3QgdG8gYmUgdGFrZW4gbGlnaHRseS4=" },
+ { "They say that a hot dog and a hell hound are the same thing.",
+ "VGhleSBzYXkgdGhhdCBhIGhvdCBkb2cgYW5kIGEgaGVsbCBob3VuZCBhcmUgdGhlIHNhbWUgdGhpbmcu" },
+ { "They say that a lamp named Aladdin's Lamp contains a djinni with 3 wishes.",
+ "VGhleSBzYXkgdGhhdCBhIGxhbXAgbmFtZWQgQWxhZGRpbidzIExhbXAgY29udGFpbnMgYSBkamlubmkgd2l0aCAzIHdpc2hlcy4=" },
+ { "They say that a large dog named Lassie will lead you to the amulet.",
+ "VGhleSBzYXkgdGhhdCBhIGxhcmdlIGRvZyBuYW1lZCBMYXNzaWUgd2lsbCBsZWFkIHlvdSB0byB0aGUgYW11bGV0Lg==" },
+ { "They say that a long sword is not a light sword.",
+ "VGhleSBzYXkgdGhhdCBhIGxvbmcgc3dvcmQgaXMgbm90IGEgbGlnaHQgc3dvcmQu" },
+ { "They say that a manes won't mince words with you.",
+ "VGhleSBzYXkgdGhhdCBhIG1hbmVzIHdvbid0IG1pbmNlIHdvcmRzIHdpdGggeW91Lg==" },
+ { "They say that a mind is a terrible thing to waste.",
+ "VGhleSBzYXkgdGhhdCBhIG1pbmQgaXMgYSB0ZXJyaWJsZSB0aGluZyB0byB3YXN0ZS4=" },
+ { "They say that a plain nymph will only wear a wire ring in one ear.",
+ "VGhleSBzYXkgdGhhdCBhIHBsYWluIG55bXBoIHdpbGwgb25seSB3ZWFyIGEgd2lyZSByaW5nIGluIG9uZSBlYXIu" },
+ { "They say that a plumed hat could be a previously used crested helmet.",
+ "VGhleSBzYXkgdGhhdCBhIHBsdW1lZCBoYXQgY291bGQgYmUgYSBwcmV2aW91c2x5IHVzZWQgY3Jlc3RlZCBoZWxtZXQu" },
+ { "They say that a potion of oil is difficult to grasp.",
+ "VGhleSBzYXkgdGhhdCBhIHBvdGlvbiBvZiBvaWwgaXMgZGlmZmljdWx0IHRvIGdyYXNwLg==" },
+ { "They say that a potion of yogurt is a cancelled potion of sickness.",
+ "VGhleSBzYXkgdGhhdCBhIHBvdGlvbiBvZiB5b2d1cnQgaXMgYSBjYW5jZWxsZWQgcG90aW9uIG9mIHNpY2tuZXNzLg==" },
+ { "They say that a purple worm is not a baby purple dragon.",
+ "VGhleSBzYXkgdGhhdCBhIHB1cnBsZSB3b3JtIGlzIG5vdCBhIGJhYnkgcHVycGxlIGRyYWdvbi4=" },
+ { "They say that a quivering blob tastes different than a gelatinous cube.",
+ "VGhleSBzYXkgdGhhdCBhIHF1aXZlcmluZyBibG9iIHRhc3RlcyBkaWZmZXJlbnQgdGhhbiBhIGdlbGF0aW5vdXMgY3ViZS4=" },
+ { "They say that a runed broadsword named Stormbringer attracts vortices.",
+ "VGhleSBzYXkgdGhhdCBhIHJ1bmVkIGJyb2Fkc3dvcmQgbmFtZWQgU3Rvcm1icmluZ2VyIGF0dHJhY3RzIHZvcnRpY2VzLg==" },
+ { "They say that a scroll of summoning has other names.",
+ "VGhleSBzYXkgdGhhdCBhIHNjcm9sbCBvZiBzdW1tb25pbmcgaGFzIG90aGVyIG5hbWVzLg==" },
+ { "They say that a shaman can bestow blessings but usually doesn't.",
+ "VGhleSBzYXkgdGhhdCBhIHNoYW1hbiBjYW4gYmVzdG93IGJsZXNzaW5ncyBidXQgdXN1YWxseSBkb2Vzbid0Lg==" },
+ { "They say that a shaman will bless you for an eye of newt and wing of bat.",
+ "VGhleSBzYXkgdGhhdCBhIHNoYW1hbiB3aWxsIGJsZXNzIHlvdSBmb3IgYW4gZXllIG9mIG5ld3QgYW5kIHdpbmcgb2YgYmF0Lg==" },
+ { "They say that a shimmering gold shield is not a polished silver shield.",
+ "VGhleSBzYXkgdGhhdCBhIHNoaW1tZXJpbmcgZ29sZCBzaGllbGQgaXMgbm90IGEgcG9saXNoZWQgc2lsdmVyIHNoaWVsZC4=" },
+ { "They say that a spear will hit a neo-otyugh. (Do YOU know what that is?)",
+ "VGhleSBzYXkgdGhhdCBhIHNwZWFyIHdpbGwgaGl0IGEgbmVvLW90eXVnaC4gKERvIFlPVSBrbm93IHdoYXQgdGhhdCBpcz8p" },
+ { "They say that a spotted dragon is the ultimate shape changer.",
+ "VGhleSBzYXkgdGhhdCBhIHNwb3R0ZWQgZHJhZ29uIGlzIHRoZSB1bHRpbWF0ZSBzaGFwZSBjaGFuZ2VyLg==" },
+ { "They say that a stethoscope is no good if you can only hear your heartbeat.",
+ "VGhleSBzYXkgdGhhdCBhIHN0ZXRob3Njb3BlIGlzIG5vIGdvb2QgaWYgeW91IGNhbiBvbmx5IGhlYXIgeW91ciBoZWFydGJlYXQu" },
+ { "They say that a succubus named Suzy will sometimes warn you of danger.",
+ "VGhleSBzYXkgdGhhdCBhIHN1Y2N1YnVzIG5hbWVkIFN1enkgd2lsbCBzb21ldGltZXMgd2FybiB5b3Ugb2YgZGFuZ2VyLg==" },
+ { "They say that a wand of cancellation is not like a wand of polymorph.",
+ "VGhleSBzYXkgdGhhdCBhIHdhbmQgb2YgY2FuY2VsbGF0aW9uIGlzIG5vdCBsaWtlIGEgd2FuZCBvZiBwb2x5bW9ycGgu" },
+ { "They say that a wood golem named Pinocchio would be easy to control.",
+ "VGhleSBzYXkgdGhhdCBhIHdvb2QgZ29sZW0gbmFtZWQgUGlub2NjaGlvIHdvdWxkIGJlIGVhc3kgdG8gY29udHJvbC4=" },
+ { "They say that after killing a dragon it's time for a change of scenery.",
+ "VGhleSBzYXkgdGhhdCBhZnRlciBraWxsaW5nIGEgZHJhZ29uIGl0J3MgdGltZSBmb3IgYSBjaGFuZ2Ugb2Ygc2NlbmVyeS4=" },
+ { "They say that an amulet of strangulation is worse than ring around the collar.",
+ "VGhleSBzYXkgdGhhdCBhbiBhbXVsZXQgb2Ygc3RyYW5ndWxhdGlvbiBpcyB3b3JzZSB0aGFuIHJpbmcgYXJvdW5kIHRoZSBjb2xsYXIu" },
+ { "They say that an attic is the best place to hide your toys.",
+ "VGhleSBzYXkgdGhhdCBhbiBhdHRpYyBpcyB0aGUgYmVzdCBwbGFjZSB0byBoaWRlIHlvdXIgdG95cy4=" },
+ { "They say that an axe named Cleaver once belonged to a hacker named Beaver.",
+ "VGhleSBzYXkgdGhhdCBhbiBheGUgbmFtZWQgQ2xlYXZlciBvbmNlIGJlbG9uZ2VkIHRvIGEgaGFja2VyIG5hbWVkIEJlYXZlci4=" },
+ { "They say that an eye of newt and a wing of bat are double the trouble.",
+ "VGhleSBzYXkgdGhhdCBhbiBleWUgb2YgbmV3dCBhbmQgYSB3aW5nIG9mIGJhdCBhcmUgZG91YmxlIHRoZSB0cm91YmxlLg==" },
+ { "They say that an incubus named Izzy sometimes makes women feel sensitive.",
+ "VGhleSBzYXkgdGhhdCBhbiBpbmN1YnVzIG5hbWVkIEl6enkgc29tZXRpbWVzIG1ha2VzIHdvbWVuIGZlZWwgc2Vuc2l0aXZlLg==" },
+ { "They say that an opulent throne room is rarely a place to wish you'd be in.",
+ "VGhleSBzYXkgdGhhdCBhbiBvcHVsZW50IHRocm9uZSByb29tIGlzIHJhcmVseSBhIHBsYWNlIHRvIHdpc2ggeW91J2QgYmUgaW4u" },
+ { "They say that an unlucky hacker once had a nose bleed at an altar and died.",
+ "VGhleSBzYXkgdGhhdCBhbiB1bmx1Y2t5IGhhY2tlciBvbmNlIGhhZCBhIG5vc2UgYmxlZWQgYXQgYW4gYWx0YXIgYW5kIGRpZWQu" },
+ { "They say that and they say this but they never say never, never!",
+ "VGhleSBzYXkgdGhhdCBhbmQgdGhleSBzYXkgdGhpcyBidXQgdGhleSBuZXZlciBzYXkgbmV2ZXIsIG5ldmVyIQ==" },
+ { "They say that any quantum mechanic knows that speed kills.",
+ "VGhleSBzYXkgdGhhdCBhbnkgcXVhbnR1bSBtZWNoYW5pYyBrbm93cyB0aGF0IHNwZWVkIGtpbGxzLg==" },
+ { "They say that applying a unicorn horn means you've missed the point.",
+ "VGhleSBzYXkgdGhhdCBhcHBseWluZyBhIHVuaWNvcm4gaG9ybiBtZWFucyB5b3UndmUgbWlzc2VkIHRoZSBwb2ludC4=" },
+ { "They say that blue stones are radioactive, beware.",
+ "VGhleSBzYXkgdGhhdCBibHVlIHN0b25lcyBhcmUgcmFkaW9hY3RpdmUsIGJld2FyZS4=" },
+ { "They say that building a dungeon is a team effort.",
+ "VGhleSBzYXkgdGhhdCBidWlsZGluZyBhIGR1bmdlb24gaXMgYSB0ZWFtIGVmZm9ydC4=" },
+ { "They say that chaotic characters never get a kick out of altars.",
+ "VGhleSBzYXkgdGhhdCBjaGFvdGljIGNoYXJhY3RlcnMgbmV2ZXIgZ2V0IGEga2ljayBvdXQgb2YgYWx0YXJzLg==" },
+ { "They say that collapsing a dungeon often creates a panic.",
+ "VGhleSBzYXkgdGhhdCBjb2xsYXBzaW5nIGEgZHVuZ2VvbiBvZnRlbiBjcmVhdGVzIGEgcGFuaWMu" },
+ { "They say that counting your eggs before they hatch shows that you care.",
+ "VGhleSBzYXkgdGhhdCBjb3VudGluZyB5b3VyIGVnZ3MgYmVmb3JlIHRoZXkgaGF0Y2ggc2hvd3MgdGhhdCB5b3UgY2FyZS4=" },
+ { "They say that dipping a bag of tricks in a fountain won't make it an icebox.",
+ "VGhleSBzYXkgdGhhdCBkaXBwaW5nIGEgYmFnIG9mIHRyaWNrcyBpbiBhIGZvdW50YWluIHdvbid0IG1ha2UgaXQgYW4gaWNlYm94Lg==" },
+ { "They say that dipping an eel and brown mold in hot water makes bouillabaisse.",
+ "VGhleSBzYXkgdGhhdCBkaXBwaW5nIGFuIGVlbCBhbmQgYnJvd24gbW9sZCBpbiBob3Qgd2F0ZXIgbWFrZXMgYm91aWxsYWJhaXNzZS4=" },
+ { "They say that donating a doubloon is extremely pious charity.",
+ "VGhleSBzYXkgdGhhdCBkb25hdGluZyBhIGRvdWJsb29uIGlzIGV4dHJlbWVseSBwaW91cyBjaGFyaXR5Lg==" },
+ { "They say that eating royal jelly attracts grizzly owlbears.",
+ "VGhleSBzYXkgdGhhdCBlYXRpbmcgcm95YWwgamVsbHkgYXR0cmFjdHMgZ3JpenpseSBvd2xiZWFycy4=" },
+ { "They say that eggs, pancakes and juice are just a mundane breakfast.",
+ "VGhleSBzYXkgdGhhdCBlZ2dzLCBwYW5jYWtlcyBhbmQganVpY2UgYXJlIGp1c3QgYSBtdW5kYW5lIGJyZWFrZmFzdC4=" },
+ { "They say that everyone knows why Medusa stands alone in the dark.",
+ "VGhleSBzYXkgdGhhdCBldmVyeW9uZSBrbm93cyB3aHkgTWVkdXNhIHN0YW5kcyBhbG9uZSBpbiB0aGUgZGFyay4=" },
+ { "They say that everyone wanted rec.games.hack to undergo a name change.",
+ "VGhleSBzYXkgdGhhdCBldmVyeW9uZSB3YW50ZWQgcmVjLmdhbWVzLmhhY2sgdG8gdW5kZXJnbyBhIG5hbWUgY2hhbmdlLg==" },
+ { "They say that finding a winning strategy is a deliberate move on your part.",
+ "VGhleSBzYXkgdGhhdCBmaW5kaW5nIGEgd2lubmluZyBzdHJhdGVneSBpcyBhIGRlbGliZXJhdGUgbW92ZSBvbiB5b3VyIHBhcnQu" },
+ { "They say that finding worthless glass is worth something.",
+ "VGhleSBzYXkgdGhhdCBmaW5kaW5nIHdvcnRobGVzcyBnbGFzcyBpcyB3b3J0aCBzb21ldGhpbmcu" },
+ { "They say that fortune cookies are food for thought.",
+ "VGhleSBzYXkgdGhhdCBmb3J0dW5lIGNvb2tpZXMgYXJlIGZvb2QgZm9yIHRob3VnaHQu" },
+ { "They say that gold is only wasted on a pet dragon.",
+ "VGhleSBzYXkgdGhhdCBnb2xkIGlzIG9ubHkgd2FzdGVkIG9uIGEgcGV0IGRyYWdvbi4=" },
+ { "They say that good things come to those that wait.",
+ "VGhleSBzYXkgdGhhdCBnb29kIHRoaW5ncyBjb21lIHRvIHRob3NlIHRoYXQgd2FpdC4=" },
+ { "They say that greased objects will slip out of monsters' hands.",
+ "VGhleSBzYXkgdGhhdCBncmVhc2VkIG9iamVjdHMgd2lsbCBzbGlwIG91dCBvZiBtb25zdGVycycgaGFuZHMu" },
+ { "They say that if you can't spell then you'll wish you had a spell book.",
+ "VGhleSBzYXkgdGhhdCBpZiB5b3UgY2FuJ3Qgc3BlbGwgdGhlbiB5b3UnbGwgd2lzaCB5b3UgaGFkIGEgc3BlbGwgYm9vay4=" },
+ { "They say that if you live by the sword, you'll die by the sword.",
+ "VGhleSBzYXkgdGhhdCBpZiB5b3UgbGl2ZSBieSB0aGUgc3dvcmQsIHlvdSdsbCBkaWUgYnkgdGhlIHN3b3JkLg==" },
+ { "They say that if you play like a monster you'll have a better game.",
+ "VGhleSBzYXkgdGhhdCBpZiB5b3UgcGxheSBsaWtlIGEgbW9uc3RlciB5b3UnbGwgaGF2ZSBhIGJldHRlciBnYW1lLg==" },
+ { "They say that if you sleep with a demon you might awake with a headache.",
+ "VGhleSBzYXkgdGhhdCBpZiB5b3Ugc2xlZXAgd2l0aCBhIGRlbW9uIHlvdSBtaWdodCBhd2FrZSB3aXRoIGEgaGVhZGFjaGUu" },
+ { "They say that if you step on a crack you could break your mother's back.",
+ "VGhleSBzYXkgdGhhdCBpZiB5b3Ugc3RlcCBvbiBhIGNyYWNrIHlvdSBjb3VsZCBicmVhayB5b3VyIG1vdGhlcidzIGJhY2su" },
+ { "They say that if you're invisible you can still be heard!",
+ "VGhleSBzYXkgdGhhdCBpZiB5b3UncmUgaW52aXNpYmxlIHlvdSBjYW4gc3RpbGwgYmUgaGVhcmQh" },
+ { "They say that if you're lucky you can feel the runes on a scroll.",
+ "VGhleSBzYXkgdGhhdCBpZiB5b3UncmUgbHVja3kgeW91IGNhbiBmZWVsIHRoZSBydW5lcyBvbiBhIHNjcm9sbC4=" },
+ { "They say that in the big picture gold is only small change.",
+ "VGhleSBzYXkgdGhhdCBpbiB0aGUgYmlnIHBpY3R1cmUgZ29sZCBpcyBvbmx5IHNtYWxsIGNoYW5nZS4=" },
+ { "They say that in the dungeon it's not what you know that really matters.",
+ "VGhleSBzYXkgdGhhdCBpbiB0aGUgZHVuZ2VvbiBpdCdzIG5vdCB3aGF0IHlvdSBrbm93IHRoYXQgcmVhbGx5IG1hdHRlcnMu" },
+ { "They say that in the dungeon moon rocks are really dilithium crystals.",
+ "VGhleSBzYXkgdGhhdCBpbiB0aGUgZHVuZ2VvbiBtb29uIHJvY2tzIGFyZSByZWFsbHkgZGlsaXRoaXVtIGNyeXN0YWxzLg==" },
+ { "They say that in the dungeon the boorish customer is never right.",
+ "VGhleSBzYXkgdGhhdCBpbiB0aGUgZHVuZ2VvbiB0aGUgYm9vcmlzaCBjdXN0b21lciBpcyBuZXZlciByaWdodC4=" },
+ { "They say that in the dungeon you don't need a watch to tell time.",
+ "VGhleSBzYXkgdGhhdCBpbiB0aGUgZHVuZ2VvbiB5b3UgZG9uJ3QgbmVlZCBhIHdhdGNoIHRvIHRlbGwgdGltZS4=" },
+ { "They say that in the dungeon you need something old, new, burrowed and blue.",
+ "VGhleSBzYXkgdGhhdCBpbiB0aGUgZHVuZ2VvbiB5b3UgbmVlZCBzb21ldGhpbmcgb2xkLCBuZXcsIGJ1cnJvd2VkIGFuZCBibHVlLg==" },
+ { "They say that in the dungeon you should always count your blessings.",
+ "VGhleSBzYXkgdGhhdCBpbiB0aGUgZHVuZ2VvbiB5b3Ugc2hvdWxkIGFsd2F5cyBjb3VudCB5b3VyIGJsZXNzaW5ncy4=" },
+ { "They say that iron golem plate mail isn't worth wishing for.",
+ "VGhleSBzYXkgdGhhdCBpcm9uIGdvbGVtIHBsYXRlIG1haWwgaXNuJ3Qgd29ydGggd2lzaGluZyBmb3Iu" },
+ { "They say that it takes four quarterstaffs to make one staff.",
+ "VGhleSBzYXkgdGhhdCBpdCB0YWtlcyBmb3VyIHF1YXJ0ZXJzdGFmZnMgdG8gbWFrZSBvbmUgc3RhZmYu" },
+ { "They say that it's not over till the fat ladies sing.",
+ "VGhleSBzYXkgdGhhdCBpdCdzIG5vdCBvdmVyIHRpbGwgdGhlIGZhdCBsYWRpZXMgc2luZy4=" },
+ { "They say that it's not over till the fat lady shouts `Off with its head'.",
+ "VGhleSBzYXkgdGhhdCBpdCdzIG5vdCBvdmVyIHRpbGwgdGhlIGZhdCBsYWR5IHNob3V0cyBgT2ZmIHdpdGggaXRzIGhlYWQnLg==" },
+ { "They say that kicking a heavy statue is really a dumb move.",
+ "VGhleSBzYXkgdGhhdCBraWNraW5nIGEgaGVhdnkgc3RhdHVlIGlzIHJlYWxseSBhIGR1bWIgbW92ZS4=" },
+ { "They say that kicking a valuable gem doesn't seem to make sense.",
+ "VGhleSBzYXkgdGhhdCBraWNraW5nIGEgdmFsdWFibGUgZ2VtIGRvZXNuJ3Qgc2VlbSB0byBtYWtlIHNlbnNlLg==" },
+ { "They say that leprechauns know Latin and you should too.",
+ "VGhleSBzYXkgdGhhdCBsZXByZWNoYXVucyBrbm93IExhdGluIGFuZCB5b3Ugc2hvdWxkIHRvby4=" },
+ { "They say that minotaurs get lost outside of the mazes.",
+ "VGhleSBzYXkgdGhhdCBtaW5vdGF1cnMgZ2V0IGxvc3Qgb3V0c2lkZSBvZiB0aGUgbWF6ZXMu" },
+ { "They say that most trolls are born again.",
+ "VGhleSBzYXkgdGhhdCBtb3N0IHRyb2xscyBhcmUgYm9ybiBhZ2Fpbi4=" },
+ { "They say that naming your cat Garfield will make you more attractive.",
+ "VGhleSBzYXkgdGhhdCBuYW1pbmcgeW91ciBjYXQgR2FyZmllbGQgd2lsbCBtYWtlIHlvdSBtb3JlIGF0dHJhY3RpdmUu" },
+ { "They say that no one knows everything about everything in the dungeon.",
+ "VGhleSBzYXkgdGhhdCBubyBvbmUga25vd3MgZXZlcnl0aGluZyBhYm91dCBldmVyeXRoaW5nIGluIHRoZSBkdW5nZW9uLg==" },
+ { "They say that no one plays NetHack just for the fun of it.",
+ "VGhleSBzYXkgdGhhdCBubyBvbmUgcGxheXMgTmV0SGFjayBqdXN0IGZvciB0aGUgZnVuIG9mIGl0Lg==" },
+ { "They say that no one really subscribes to rec.games.roguelike.nethack.",
+ "VGhleSBzYXkgdGhhdCBubyBvbmUgcmVhbGx5IHN1YnNjcmliZXMgdG8gcmVjLmdhbWVzLnJvZ3VlbGlrZS5uZXRoYWNrLg==" },
+ { "They say that no one will admit to starting a rumor.",
+ "VGhleSBzYXkgdGhhdCBubyBvbmUgd2lsbCBhZG1pdCB0byBzdGFydGluZyBhIHJ1bW9yLg==" },
+ { "They say that nurses sometimes carry scalpels and never use them.",
+ "VGhleSBzYXkgdGhhdCBudXJzZXMgc29tZXRpbWVzIGNhcnJ5IHNjYWxwZWxzIGFuZCBuZXZlciB1c2UgdGhlbS4=" },
+ { "They say that once you've met one wizard you've met them all.",
+ "VGhleSBzYXkgdGhhdCBvbmNlIHlvdSd2ZSBtZXQgb25lIHdpemFyZCB5b3UndmUgbWV0IHRoZW0gYWxsLg==" },
+ { "They say that one troll is worth 10,000 newts.",
+ "VGhleSBzYXkgdGhhdCBvbmUgdHJvbGwgaXMgd29ydGggMTAsMDAwIG5ld3RzLg==" },
+ { "They say that only David can find the zoo!",
+ "VGhleSBzYXkgdGhhdCBvbmx5IERhdmlkIGNhbiBmaW5kIHRoZSB6b28h" },
+ { "They say that only angels play their harps for their pets.",
+ "VGhleSBzYXkgdGhhdCBvbmx5IGFuZ2VscyBwbGF5IHRoZWlyIGhhcnBzIGZvciB0aGVpciBwZXRzLg==" },
+ { "They say that only big spenders carry gold.",
+ "VGhleSBzYXkgdGhhdCBvbmx5IGJpZyBzcGVuZGVycyBjYXJyeSBnb2xkLg==" },
+ { "They say that orc shamans are healthy, wealthy and wise.",
+ "VGhleSBzYXkgdGhhdCBvcmMgc2hhbWFucyBhcmUgaGVhbHRoeSwgd2VhbHRoeSBhbmQgd2lzZS4=" },
+ { "They say that playing NetHack is like walking into a death trap.",
+ "VGhleSBzYXkgdGhhdCBwbGF5aW5nIE5ldEhhY2sgaXMgbGlrZSB3YWxraW5nIGludG8gYSBkZWF0aCB0cmFwLg==" },
+ { "They say that problem breathing is best treated by a proper diet.",
+ "VGhleSBzYXkgdGhhdCBwcm9ibGVtIGJyZWF0aGluZyBpcyBiZXN0IHRyZWF0ZWQgYnkgYSBwcm9wZXIgZGlldC4=" },
+ { "They say that quaffing many potions of levitation can give you a headache.",
+ "VGhleSBzYXkgdGhhdCBxdWFmZmluZyBtYW55IHBvdGlvbnMgb2YgbGV2aXRhdGlvbiBjYW4gZ2l2ZSB5b3UgYSBoZWFkYWNoZS4=" },
+ { "They say that queen bees get that way by eating royal jelly.",
+ "VGhleSBzYXkgdGhhdCBxdWVlbiBiZWVzIGdldCB0aGF0IHdheSBieSBlYXRpbmcgcm95YWwgamVsbHku" },
+ { "They say that reading a scare monster scroll is the same as saying Elbereth.",
+ "VGhleSBzYXkgdGhhdCByZWFkaW5nIGEgc2NhcmUgbW9uc3RlciBzY3JvbGwgaXMgdGhlIHNhbWUgYXMgc2F5aW5nIEVsYmVyZXRoLg==" },
+ { "They say that real hackers always are controlled.",
+ "VGhleSBzYXkgdGhhdCByZWFsIGhhY2tlcnMgYWx3YXlzIGFyZSBjb250cm9sbGVkLg==" },
+ { "They say that real hackers never sleep.",
+ "VGhleSBzYXkgdGhhdCByZWFsIGhhY2tlcnMgbmV2ZXIgc2xlZXAu" },
+ { "They say that shopkeepers are insured by Croesus himself!",
+ "VGhleSBzYXkgdGhhdCBzaG9wa2VlcGVycyBhcmUgaW5zdXJlZCBieSBDcm9lc3VzIGhpbXNlbGYh" },
+ { "They say that shopkeepers never carry more than 20 gold pieces, at night.",
+ "VGhleSBzYXkgdGhhdCBzaG9wa2VlcGVycyBuZXZlciBjYXJyeSBtb3JlIHRoYW4gMjAgZ29sZCBwaWVjZXMsIGF0IG5pZ2h0Lg==" },
+ { "They say that shopkeepers never sell blessed potions of invisibility.",
+ "VGhleSBzYXkgdGhhdCBzaG9wa2VlcGVycyBuZXZlciBzZWxsIGJsZXNzZWQgcG90aW9ucyBvZiBpbnZpc2liaWxpdHku" },
+ { "They say that soldiers wear kid gloves and silly helmets.",
+ "VGhleSBzYXkgdGhhdCBzb2xkaWVycyB3ZWFyIGtpZCBnbG92ZXMgYW5kIHNpbGx5IGhlbG1ldHMu" },
+ { "They say that some Kops are on the take.",
+ "VGhleSBzYXkgdGhhdCBzb21lIEtvcHMgYXJlIG9uIHRoZSB0YWtlLg==" },
+ { "They say that some guards' palms can be greased.",
+ "VGhleSBzYXkgdGhhdCBzb21lIGd1YXJkcycgcGFsbXMgY2FuIGJlIGdyZWFzZWQu" },
+ { "They say that some monsters may kiss your boots to stop your drum playing.",
+ "VGhleSBzYXkgdGhhdCBzb21lIG1vbnN0ZXJzIG1heSBraXNzIHlvdXIgYm9vdHMgdG8gc3RvcCB5b3VyIGRydW0gcGxheWluZy4=" },
+ { "They say that sometimes you can be the hit of the party when playing a horn.",
+ "VGhleSBzYXkgdGhhdCBzb21ldGltZXMgeW91IGNhbiBiZSB0aGUgaGl0IG9mIHRoZSBwYXJ0eSB3aGVuIHBsYXlpbmcgYSBob3JuLg==" },
+ { "They say that the NetHack gods generally welcome your sacrifices.",
+ "VGhleSBzYXkgdGhhdCB0aGUgTmV0SGFjayBnb2RzIGdlbmVyYWxseSB3ZWxjb21lIHlvdXIgc2FjcmlmaWNlcy4=" },
+ { "They say that the Three Rings are named Vilya, Nenya and Narya.",
+ "VGhleSBzYXkgdGhhdCB0aGUgVGhyZWUgUmluZ3MgYXJlIG5hbWVkIFZpbHlhLCBOZW55YSBhbmQgTmFyeWEu" },
+ { "They say that the Wizard of Yendor has a death wish.",
+ "VGhleSBzYXkgdGhhdCB0aGUgV2l6YXJkIG9mIFllbmRvciBoYXMgYSBkZWF0aCB3aXNoLg==" },
+ { "They say that the `hair of the dog' is sometimes an effective remedy.",
+ "VGhleSBzYXkgdGhhdCB0aGUgYGhhaXIgb2YgdGhlIGRvZycgaXMgc29tZXRpbWVzIGFuIGVmZmVjdGl2ZSByZW1lZHku" },
+ { "They say that the best time to save your game is now before its too late.",
+ "VGhleSBzYXkgdGhhdCB0aGUgYmVzdCB0aW1lIHRvIHNhdmUgeW91ciBnYW1lIGlzIG5vdyBiZWZvcmUgaXRzIHRvbyBsYXRlLg==" },
+ { "They say that the biggest obstacle in NetHack is your mind.",
+ "VGhleSBzYXkgdGhhdCB0aGUgYmlnZ2VzdCBvYnN0YWNsZSBpbiBOZXRIYWNrIGlzIHlvdXIgbWluZC4=" },
+ { "They say that the gods are angry when they hit you with objects.",
+ "VGhleSBzYXkgdGhhdCB0aGUgZ29kcyBhcmUgYW5ncnkgd2hlbiB0aGV5IGhpdCB5b3Ugd2l0aCBvYmplY3RzLg==" },
+ { "They say that the priesthood are specially favored by the gods.",
+ "VGhleSBzYXkgdGhhdCB0aGUgcHJpZXN0aG9vZCBhcmUgc3BlY2lhbGx5IGZhdm9yZWQgYnkgdGhlIGdvZHMu" },
+ { "They say that the way to make a unicorn happy is to give it what it wants.",
+ "VGhleSBzYXkgdGhhdCB0aGUgd2F5IHRvIG1ha2UgYSB1bmljb3JuIGhhcHB5IGlzIHRvIGdpdmUgaXQgd2hhdCBpdCB3YW50cy4=" },
+ { "They say that there are no black or white stones, only gray.",
+ "VGhleSBzYXkgdGhhdCB0aGVyZSBhcmUgbm8gYmxhY2sgb3Igd2hpdGUgc3RvbmVzLCBvbmx5IGdyYXku" },
+ { "They say that there are no skeletons hence there are no skeleton keys.",
+ "VGhleSBzYXkgdGhhdCB0aGVyZSBhcmUgbm8gc2tlbGV0b25zIGhlbmNlIHRoZXJlIGFyZSBubyBza2VsZXRvbiBrZXlzLg==" },
+ { "They say that there is a clever rogue in every hacker just dying to escape.",
+ "VGhleSBzYXkgdGhhdCB0aGVyZSBpcyBhIGNsZXZlciByb2d1ZSBpbiBldmVyeSBoYWNrZXIganVzdCBkeWluZyB0byBlc2NhcGUu" },
+ { "They say that there is no such thing as free advice.",
+ "VGhleSBzYXkgdGhhdCB0aGVyZSBpcyBubyBzdWNoIHRoaW5nIGFzIGZyZWUgYWR2aWNlLg==" },
+ { "They say that there is only one way to win at NetHack.",
+ "VGhleSBzYXkgdGhhdCB0aGVyZSBpcyBvbmx5IG9uZSB3YXkgdG8gd2luIGF0IE5ldEhhY2su" },
+ { "They say that there once was a fearsome chaotic samurai named Luk No.",
+ "VGhleSBzYXkgdGhhdCB0aGVyZSBvbmNlIHdhcyBhIGZlYXJzb21lIGNoYW90aWMgc2FtdXJhaSBuYW1lZCBMdWsgTm8u" },
+ { "They say that there was a time when cursed holy water wasn't water.",
+ "VGhleSBzYXkgdGhhdCB0aGVyZSB3YXMgYSB0aW1lIHdoZW4gY3Vyc2VkIGhvbHkgd2F0ZXIgd2Fzbid0IHdhdGVyLg==" },
+ { "They say that there's no point in crying over a gray ooze.",
+ "VGhleSBzYXkgdGhhdCB0aGVyZSdzIG5vIHBvaW50IGluIGNyeWluZyBvdmVyIGEgZ3JheSBvb3plLg==" },
+ { "They say that there's only hope left after you've opened Pandora's box.",
+ "VGhleSBzYXkgdGhhdCB0aGVyZSdzIG9ubHkgaG9wZSBsZWZ0IGFmdGVyIHlvdSd2ZSBvcGVuZWQgUGFuZG9yYSdzIGJveC4=" },
+ { "They say that trapdoors should always be marked `Caution: Trap Door'.",
+ "VGhleSBzYXkgdGhhdCB0cmFwZG9vcnMgc2hvdWxkIGFsd2F5cyBiZSBtYXJrZWQgYENhdXRpb246IFRyYXAgRG9vcicu" },
+ { "They say that using an amulet of change isn't a difficult operation.",
+ "VGhleSBzYXkgdGhhdCB1c2luZyBhbiBhbXVsZXQgb2YgY2hhbmdlIGlzbid0IGEgZGlmZmljdWx0IG9wZXJhdGlvbi4=" },
+ { "They say that water walking boots are better if you are fast like Hermes.",
+ "VGhleSBzYXkgdGhhdCB3YXRlciB3YWxraW5nIGJvb3RzIGFyZSBiZXR0ZXIgaWYgeW91IGFyZSBmYXN0IGxpa2UgSGVybWVzLg==" },
+ { "They say that when you wear a circular amulet you might resemble a troll.",
+ "VGhleSBzYXkgdGhhdCB3aGVuIHlvdSB3ZWFyIGEgY2lyY3VsYXIgYW11bGV0IHlvdSBtaWdodCByZXNlbWJsZSBhIHRyb2xsLg==" },
+ { "They say that when you're hungry you can get a pizza in 30 moves or it's free.",
+ "VGhleSBzYXkgdGhhdCB3aGVuIHlvdSdyZSBodW5ncnkgeW91IGNhbiBnZXQgYSBwaXp6YSBpbiAzMCBtb3ZlcyBvciBpdCdzIGZyZWUu" },
+ { "They say that when your god is angry you should try another one.",
+ "VGhleSBzYXkgdGhhdCB3aGVuIHlvdXIgZ29kIGlzIGFuZ3J5IHlvdSBzaG91bGQgdHJ5IGFub3RoZXIgb25lLg==" },
+ { "They say that wielding a unicorn horn takes strength.",
+ "VGhleSBzYXkgdGhhdCB3aWVsZGluZyBhIHVuaWNvcm4gaG9ybiB0YWtlcyBzdHJlbmd0aC4=" },
+ { "They say that with speed boots you never worry about hit and run accidents.",
+ "VGhleSBzYXkgdGhhdCB3aXRoIHNwZWVkIGJvb3RzIHlvdSBuZXZlciB3b3JyeSBhYm91dCBoaXQgYW5kIHJ1biBhY2NpZGVudHMu" },
+ { "They say that you can defeat a killer bee with a unicorn horn.",
+ "VGhleSBzYXkgdGhhdCB5b3UgY2FuIGRlZmVhdCBhIGtpbGxlciBiZWUgd2l0aCBhIHVuaWNvcm4gaG9ybi4=" },
+ { "They say that you can only cross the River Styx in Charon's boat.",
+ "VGhleSBzYXkgdGhhdCB5b3UgY2FuIG9ubHkgY3Jvc3MgdGhlIFJpdmVyIFN0eXggaW4gQ2hhcm9uJ3MgYm9hdC4=" },
+ { "They say that you can only kill a lich once and then you'd better be careful.",
+ "VGhleSBzYXkgdGhhdCB5b3UgY2FuIG9ubHkga2lsbCBhIGxpY2ggb25jZSBhbmQgdGhlbiB5b3UnZCBiZXR0ZXIgYmUgY2FyZWZ1bC4=" },
+ { "They say that you can only wish for things you've already had.",
+ "VGhleSBzYXkgdGhhdCB5b3UgY2FuIG9ubHkgd2lzaCBmb3IgdGhpbmdzIHlvdSd2ZSBhbHJlYWR5IGhhZC4=" },
+ { "They say that you can train a cat by talking gently to it.",
+ "VGhleSBzYXkgdGhhdCB5b3UgY2FuIHRyYWluIGEgY2F0IGJ5IHRhbGtpbmcgZ2VudGx5IHRvIGl0Lg==" },
+ { "They say that you can train a dog by talking firmly to it.",
+ "VGhleSBzYXkgdGhhdCB5b3UgY2FuIHRyYWluIGEgZG9nIGJ5IHRhbGtpbmcgZmlybWx5IHRvIGl0Lg==" },
+ { "They say that you can trust your gold with the king.",
+ "VGhleSBzYXkgdGhhdCB5b3UgY2FuIHRydXN0IHlvdXIgZ29sZCB3aXRoIHRoZSBraW5nLg==" },
+ { "They say that you can't wipe your greasy bare hands on a blank scroll.",
+ "VGhleSBzYXkgdGhhdCB5b3UgY2FuJ3Qgd2lwZSB5b3VyIGdyZWFzeSBiYXJlIGhhbmRzIG9uIGEgYmxhbmsgc2Nyb2xsLg==" },
+ { "They say that you cannot trust scrolls of rumor.",
+ "VGhleSBzYXkgdGhhdCB5b3UgY2Fubm90IHRydXN0IHNjcm9sbHMgb2YgcnVtb3Iu" },
+ { "They say that you could fall head over heels for an energy vortex.",
+ "VGhleSBzYXkgdGhhdCB5b3UgY291bGQgZmFsbCBoZWFkIG92ZXIgaGVlbHMgZm9yIGFuIGVuZXJneSB2b3J0ZXgu" },
+ { "They say that you need a key in order to open locked doors.",
+ "VGhleSBzYXkgdGhhdCB5b3UgbmVlZCBhIGtleSBpbiBvcmRlciB0byBvcGVuIGxvY2tlZCBkb29ycy4=" },
+ { "They say that you need a mirror to notice a mimic in an antique shop.",
+ "VGhleSBzYXkgdGhhdCB5b3UgbmVlZCBhIG1pcnJvciB0byBub3RpY2UgYSBtaW1pYyBpbiBhbiBhbnRpcXVlIHNob3Au" },
+ { "They say that you really can use a pick-axe unless you really can't.",
+ "VGhleSBzYXkgdGhhdCB5b3UgcmVhbGx5IGNhbiB1c2UgYSBwaWNrLWF4ZSB1bmxlc3MgeW91IHJlYWxseSBjYW4ndC4=" },
+ { "They say that you should always store your tools in the cellar.",
+ "VGhleSBzYXkgdGhhdCB5b3Ugc2hvdWxkIGFsd2F5cyBzdG9yZSB5b3VyIHRvb2xzIGluIHRoZSBjZWxsYXIu" },
+ { "They say that you should be careful while climbing the ladder to success.",
+ "VGhleSBzYXkgdGhhdCB5b3Ugc2hvdWxkIGJlIGNhcmVmdWwgd2hpbGUgY2xpbWJpbmcgdGhlIGxhZGRlciB0byBzdWNjZXNzLg==" },
+ { "They say that you should call your armor `rustproof'.",
+ "VGhleSBzYXkgdGhhdCB5b3Ugc2hvdWxkIGNhbGwgeW91ciBhcm1vciBgcnVzdHByb29mJy4=" },
+ { "They say that you should name your dog Spuds to have a cool pet.",
+ "VGhleSBzYXkgdGhhdCB5b3Ugc2hvdWxkIG5hbWUgeW91ciBkb2cgU3B1ZHMgdG8gaGF2ZSBhIGNvb2wgcGV0Lg==" },
+ { "They say that you should name your weapon after your first monster kill.",
+ "VGhleSBzYXkgdGhhdCB5b3Ugc2hvdWxkIG5hbWUgeW91ciB3ZWFwb24gYWZ0ZXIgeW91ciBmaXJzdCBtb25zdGVyIGtpbGwu" },
+ { "They say that you should never introduce a rope golem to a succubus.",
+ "VGhleSBzYXkgdGhhdCB5b3Ugc2hvdWxkIG5ldmVyIGludHJvZHVjZSBhIHJvcGUgZ29sZW0gdG8gYSBzdWNjdWJ1cy4=" },
+ { "They say that you should never sleep near invisible ring wraiths.",
+ "VGhleSBzYXkgdGhhdCB5b3Ugc2hvdWxkIG5ldmVyIHNsZWVwIG5lYXIgaW52aXNpYmxlIHJpbmcgd3JhaXRocy4=" },
+ { "They say that you should never try to leave the dungeon with a bag of gems.",
+ "VGhleSBzYXkgdGhhdCB5b3Ugc2hvdWxkIG5ldmVyIHRyeSB0byBsZWF2ZSB0aGUgZHVuZ2VvbiB3aXRoIGEgYmFnIG9mIGdlbXMu" },
+ { "They say that you should remove your armor before sitting on a throne.",
+ "VGhleSBzYXkgdGhhdCB5b3Ugc2hvdWxkIHJlbW92ZSB5b3VyIGFybW9yIGJlZm9yZSBzaXR0aW5nIG9uIGEgdGhyb25lLg==" },
+ { "This fortune cookie is copy protected.",
+ "VGhpcyBmb3J0dW5lIGNvb2tpZSBpcyBjb3B5IHByb3RlY3RlZC4=" },
+ { "This fortune cookie is the property of Fortune Cookies, Inc.",
+ "VGhpcyBmb3J0dW5lIGNvb2tpZSBpcyB0aGUgcHJvcGVydHkgb2YgRm9ydHVuZSBDb29raWVzLCBJbmMu" },
+ { "Tired? Try a scroll of charging on yourself.",
+ "VGlyZWQ/IFRyeSBhIHNjcm9sbCBvZiBjaGFyZ2luZyBvbiB5b3Vyc2VsZi4=" },
+ { "To achieve the next higher rating, you need 3 more points.",
+ "VG8gYWNoaWV2ZSB0aGUgbmV4dCBoaWdoZXIgcmF0aW5nLCB5b3UgbmVlZCAzIG1vcmUgcG9pbnRzLg==" },
+ { "To reach heaven, escape the dungeon while wearing a ring of levitation.",
+ "VG8gcmVhY2ggaGVhdmVuLCBlc2NhcGUgdGhlIGR1bmdlb24gd2hpbGUgd2VhcmluZyBhIHJpbmcgb2YgbGV2aXRhdGlvbi4=" },
+ { "Tourists wear shirts loud enough to wake the dead.",
+ "VG91cmlzdHMgd2VhciBzaGlydHMgbG91ZCBlbm91Z2ggdG8gd2FrZSB0aGUgZGVhZC4=" },
+ { "Try calling your katana Moulinette.",
+ "VHJ5IGNhbGxpbmcgeW91ciBrYXRhbmEgTW91bGluZXR0ZS4=" },
+ { "Ulch! That meat was painted!",
+ "VWxjaCEgVGhhdCBtZWF0IHdhcyBwYWludGVkIQ==" },
+ { "Unfortunately, this message was left intentionally blank.",
+ "VW5mb3J0dW5hdGVseSwgdGhpcyBtZXNzYWdlIHdhcyBsZWZ0IGludGVudGlvbmFsbHkgYmxhbmsu" },
+ { "Using a morning star in the evening has no effect.",
+ "VXNpbmcgYSBtb3JuaW5nIHN0YXIgaW4gdGhlIGV2ZW5pbmcgaGFzIG5vIGVmZmVjdC4=" },
+ { "Want a hint? Zap a wand of make invisible on your weapon!",
+ "V2FudCBhIGhpbnQ/IFphcCBhIHdhbmQgb2YgbWFrZSBpbnZpc2libGUgb24geW91ciB3ZWFwb24h" },
+ { "Want to ascend in a hurry? Apply at Gizmonic Institute.",
+ "V2FudCB0byBhc2NlbmQgaW4gYSBodXJyeT8gQXBwbHkgYXQgR2l6bW9uaWMgSW5zdGl0dXRlLg==" },
+ { "Wanted: shopkeepers. Send a scroll of mail to Mage of Yendor/Level 35/Dungeon.",
+ "V2FudGVkOiBzaG9wa2VlcGVycy4gU2VuZCBhIHNjcm9sbCBvZiBtYWlsIHRvIE1hZ2Ugb2YgWWVuZG9yL0xldmVsIDM1L0R1bmdlb24u" },
+ { "Warning: fortune reading can be hazardous to your health.",
+ "V2FybmluZzogZm9ydHVuZSByZWFkaW5nIGNhbiBiZSBoYXphcmRvdXMgdG8geW91ciBoZWFsdGgu" },
+ { "We have new ways of detecting treachery...",
+ "V2UgaGF2ZSBuZXcgd2F5cyBvZiBkZXRlY3RpbmcgdHJlYWNoZXJ5Li4u" },
+ { "Wet towels make great weapons!",
+ "V2V0IHRvd2VscyBtYWtlIGdyZWF0IHdlYXBvbnMh" },
+ { "What a pity, you cannot read it!",
+ "V2hhdCBhIHBpdHksIHlvdSBjYW5ub3QgcmVhZCBpdCE=" },
+ { "When a piercer drops in on you, you will be tempted to hit the ceiling!",
+ "V2hlbiBhIHBpZXJjZXIgZHJvcHMgaW4gb24geW91LCB5b3Ugd2lsbCBiZSB0ZW1wdGVkIHRvIGhpdCB0aGUgY2VpbGluZyE=" },
+ { "When in a maze follow the right wall and you will never get lost.",
+ "V2hlbiBpbiBhIG1hemUgZm9sbG93IHRoZSByaWdodCB3YWxsIGFuZCB5b3Ugd2lsbCBuZXZlciBnZXQgbG9zdC4=" },
+ { "When you have a key, you don't have to wait for the guard.",
+ "V2hlbiB5b3UgaGF2ZSBhIGtleSwgeW91IGRvbid0IGhhdmUgdG8gd2FpdCBmb3IgdGhlIGd1YXJkLg==" },
+ { "Why are you wasting time reading fortunes?",
+ "V2h5IGFyZSB5b3Ugd2FzdGluZyB0aW1lIHJlYWRpbmcgZm9ydHVuZXM/" },
+ { "Wish for a master key and open the Magic Memory Vault!",
+ "V2lzaCBmb3IgYSBtYXN0ZXIga2V5IGFuZCBvcGVuIHRoZSBNYWdpYyBNZW1vcnkgVmF1bHQh" },
+ { "Wizard expects every monster to do its duty.",
+ "V2l6YXJkIGV4cGVjdHMgZXZlcnkgbW9uc3RlciB0byBkbyBpdHMgZHV0eS4=" },
+ { "Wow! You could've had a potion of fruit juice!",
+ "V293ISBZb3UgY291bGQndmUgaGFkIGEgcG90aW9uIG9mIGZydWl0IGp1aWNlIQ==" },
+ { "Yet Another Silly Message (YASM).",
+ "WWV0IEFub3RoZXIgU2lsbHkgTWVzc2FnZSAoWUFTTSku" },
+ { "You are destined to be misled by a fortune.",
+ "WW91IGFyZSBkZXN0aW5lZCB0byBiZSBtaXNsZWQgYnkgYSBmb3J0dW5lLg==" },
+ { "You can get a genuine Amulet of Yendor by doing the following: --More--",
+ "WW91IGNhbiBnZXQgYSBnZW51aW5lIEFtdWxldCBvZiBZZW5kb3IgYnkgZG9pbmcgdGhlIGZvbGxvd2luZzogLS1Nb3JlLS0=" },
+ { "You can protect yourself from black dragons by doing the following: --More--",
+ "WW91IGNhbiBwcm90ZWN0IHlvdXJzZWxmIGZyb20gYmxhY2sgZHJhZ29ucyBieSBkb2luZyB0aGUgZm9sbG93aW5nOiAtLU1vcmUtLQ==" },
+ { "You can't get by the snake.",
+ "WW91IGNhbid0IGdldCBieSB0aGUgc25ha2Uu" },
+ { "You feel like someone is pulling your leg.",
+ "WW91IGZlZWwgbGlrZSBzb21lb25lIGlzIHB1bGxpbmcgeW91ciBsZWcu" },
+ { "You have to outwit the Sphynx or pay her.",
+ "WW91IGhhdmUgdG8gb3V0d2l0IHRoZSBTcGh5bnggb3IgcGF5IGhlci4=" },
+ { "You hear the fortune cookie's hissing!",
+ "WW91IGhlYXIgdGhlIGZvcnR1bmUgY29va2llJ3MgaGlzc2luZyE=" },
+ { "You may get rich selling letters, but beware of being blackmailed!",
+ "WW91IG1heSBnZXQgcmljaCBzZWxsaW5nIGxldHRlcnMsIGJ1dCBiZXdhcmUgb2YgYmVpbmcgYmxhY2ttYWlsZWQh" },
+ { "You offend Shai-Hulud by sheathing your crysknife without having drawn blood.",
+ "WW91IG9mZmVuZCBTaGFpLUh1bHVkIGJ5IHNoZWF0aGluZyB5b3VyIGNyeXNrbmlmZSB3aXRob3V0IGhhdmluZyBkcmF3biBibG9vZC4=" },
+ { "You swallowed the fortune!",
+ "WW91IHN3YWxsb3dlZCB0aGUgZm9ydHVuZSE=" },
+ { "You want to regain strength? Two levels ahead is a guesthouse!",
+ "WW91IHdhbnQgdG8gcmVnYWluIHN0cmVuZ3RoPyBUd28gbGV2ZWxzIGFoZWFkIGlzIGEgZ3Vlc3Rob3VzZSE=" },
+ { "You will encounter a tall, dark, and gruesome creature...",
+ "WW91IHdpbGwgZW5jb3VudGVyIGEgdGFsbCwgZGFyaywgYW5kIGdydWVzb21lIGNyZWF0dXJlLi4u" },
+
+ { "The End", "VGhlIEVuZA==" }
+ };
+
+/* PL_Base64Encode, random strings */
+PRBool test_004(void)
+{
+ int i;
+ char result[ 4096 ];
+
+ printf("Test 004 (PL_Base64Encode, random strings) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ PRUint32 plen = PL_strlen(array[i].plaintext);
+ PRUint32 clen = ((plen + 2)/3)*4;
+
+ char *rv = PL_Base64Encode(array[i].plaintext, plen, result);
+
+ if( rv != result )
+ {
+ printf("FAIL\n\t(%d): return value\n", i);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strncmp(result, array[i].cyphertext, clen) )
+ {
+ printf("FAIL\n\t(%d, \"%s\"): expected \n\"%s,\" got \n\"%.*s.\"\n",
+ i, array[i].plaintext, array[i].cyphertext, clen, result);
+ return PR_FALSE;
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Encode, single characters, malloc */
+PRBool test_005(void)
+{
+ PRUint32 a, b;
+ unsigned char plain[ 4 ];
+ unsigned char cypher[ 5 ];
+ char *rv;
+
+ printf("Test 005 (PL_Base64Encode, single characters, malloc) ..."); fflush(stdout);
+
+ plain[1] = plain[2] = plain[3] = (unsigned char)0;
+ cypher[2] = cypher[3] = (unsigned char)'=';
+ cypher[4] = (unsigned char)0;
+
+ for( a = 0; a < 64; a++ )
+ {
+ cypher[0] = base[a];
+
+ for( b = 0; b < 4; b++ )
+ {
+ plain[0] = (unsigned char)(a * 4 + b);
+ cypher[1] = base[(b * 16)];
+
+ rv = PL_Base64Encode((char *)plain, 1, (char *)0);
+ if( (char *)0 == rv )
+ {
+ printf("FAIL\n\t(%d, %d): no return value\n", a, b);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strcmp((char *)cypher, rv) )
+ {
+ printf("FAIL\n\t(%d, %d): expected \"%s,\" got \"%s.\"\n",
+ a, b, cypher, rv);
+ PR_DELETE(rv);
+ return PR_FALSE;
+ }
+
+ PR_DELETE(rv);
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Encode, double characters, malloc */
+PRBool test_006(void)
+{
+ PRUint32 a, b, c, d;
+ unsigned char plain[ 4 ];
+ unsigned char cypher[ 5 ];
+ char *rv;
+
+ printf("Test 006 (PL_Base64Encode, double characters, malloc) ..."); fflush(stdout);
+
+ plain[2] = plain[3] = (unsigned char)0;
+ cypher[3] = (unsigned char)'=';
+ cypher[4] = (unsigned char)0;
+
+ for( a = 0; a < 64; a++ )
+ {
+ cypher[0] = base[a];
+ for( b = 0; b < 4; b++ )
+ {
+ plain[0] = (a*4) + b;
+ for( c = 0; c < 16; c++ )
+ {
+ cypher[1] = base[b*16 + c];
+ for( d = 0; d < 16; d++ )
+ {
+ plain[1] = c*16 + d;
+ cypher[2] = base[d*4];
+
+ rv = PL_Base64Encode((char *)plain, 2, (char *)0);
+ if( (char *)0 == rv )
+ {
+ printf("FAIL\n\t(%d, %d, %d, %d): no return value\n", a, b, c, d);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strcmp((char *)cypher, rv) )
+ {
+ printf("FAIL\n\t(%d, %d, %d, %d): expected \"%s,\" got \"%s.\"\n",
+ a, b, c, d, cypher, rv);
+ PR_DELETE(rv);
+ return PR_FALSE;
+ }
+
+ PR_DELETE(rv);
+ }
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Encode, triple characters, malloc */
+PRBool test_007(void)
+{
+ PRUint32 a, b, c, d, e, f;
+ unsigned char plain[ 4 ];
+ unsigned char cypher[ 5 ];
+ char *rv;
+
+ printf("Test 007 (PL_Base64Encode, triple characters, malloc) ..."); fflush(stdout);
+
+ cypher[4] = (unsigned char)0;
+
+ for( a = 0; a < 64; a++ )
+ {
+ cypher[0] = base[a];
+ for( b = 0; b < 4; b++ )
+ {
+ plain[0] = (a*4) + b;
+ for( c = 0; c < 16; c++ )
+ {
+ cypher[1] = base[b*16 + c];
+ for( d = 0; d < 16; d++ )
+ {
+ plain[1] = c*16 + d;
+ for( e = 0; e < 4; e++ )
+ {
+ cypher[2] = base[d*4 + e];
+ for( f = 0; f < 64; f++ )
+ {
+ plain[2] = e * 64 + f;
+ cypher[3] = base[f];
+
+ rv = PL_Base64Encode((char *)plain, 3, (char *)0);
+ if( (char *)0 == rv )
+ {
+ printf("FAIL\n\t(%d, %d, %d, %d, %d, %d): no return value\n", a, b, c, d, e, f);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strcmp((char *)cypher, rv) )
+ {
+ printf("FAIL\n\t(%d, %d, %d, %d, %d, %d): expected \"%s,\" got \"%.4s.\"\n",
+ a, b, c, d, e, f, cypher, rv);
+ PR_DELETE(rv);
+ return PR_FALSE;
+ }
+
+ PR_DELETE(rv);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Encode, random strings, malloc */
+PRBool test_008(void)
+{
+ int i;
+
+ printf("Test 008 (PL_Base64Encode, random strings, malloc) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ PRUint32 plen = PL_strlen(array[i].plaintext);
+ PRUint32 clen = ((plen + 2)/3)*4;
+
+ char *rv = PL_Base64Encode(array[i].plaintext, plen, (char *)0);
+
+ if( (char *)0 == rv )
+ {
+ printf("FAIL\n\t(%d): no return value\n", i);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strcmp(rv, array[i].cyphertext) )
+ {
+ printf("FAIL\n\t(%d, \"%s\"): expected \n\"%s,\" got \n\"%s.\"\n",
+ i, array[i].plaintext, array[i].cyphertext, rv);
+ return PR_FALSE;
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Decode, single characters */
+PRBool test_009(void)
+{
+ PRUint32 a, b;
+ unsigned char plain[ 4 ];
+ unsigned char cypher[ 5 ];
+ char result[ 8 ];
+ char *rv;
+
+ printf("Test 009 (PL_Base64Decode, single characters, equals) ..."); fflush(stdout);
+
+ plain[1] = plain[2] = plain[3] = (unsigned char)0;
+ cypher[2] = cypher[3] = (unsigned char)'=';
+ cypher[4] = (unsigned char)0;
+
+ for( a = 0; a < 64; a++ )
+ {
+ cypher[0] = base[a];
+
+ for( b = 0; b < 4; b++ )
+ {
+ plain[0] = (unsigned char)(a * 4 + b);
+ cypher[1] = base[(b * 16)];
+
+ rv = PL_Base64Decode((char *)cypher, 4, result);
+ if( rv != result )
+ {
+ printf("FAIL\n\t(%d, %d): return value\n", a, b);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strncmp((char *)plain, result, 1) )
+ {
+ printf("FAIL\n\t(%d, %d): expected \"%s,\" got \"%.1s.\"\n",
+ a, b, plain, result);
+ return PR_FALSE;
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Decode, single characters */
+PRBool test_010(void)
+{
+ PRUint32 a, b;
+ unsigned char plain[ 4 ];
+ unsigned char cypher[ 5 ];
+ char result[ 8 ];
+ char *rv;
+
+ printf("Test 010 (PL_Base64Decode, single characters, no equals) ..."); fflush(stdout);
+
+ plain[1] = plain[2] = plain[3] = (unsigned char)0;
+ cypher[2] = cypher[3] = (unsigned char)0;
+ cypher[4] = (unsigned char)0;
+
+ for( a = 0; a < 64; a++ )
+ {
+ cypher[0] = base[a];
+
+ for( b = 0; b < 4; b++ )
+ {
+ plain[0] = (unsigned char)(a * 4 + b);
+ cypher[1] = base[(b * 16)];
+
+ rv = PL_Base64Decode((char *)cypher, 2, result);
+ if( rv != result )
+ {
+ printf("FAIL\n\t(%d, %d): return value\n", a, b);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strncmp((char *)plain, result, 1) )
+ {
+ printf("FAIL\n\t(%d, %d): expected \"%s,\" got \"%.1s.\"\n",
+ a, b, plain, result);
+ return PR_FALSE;
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Decode, double characters */
+PRBool test_011(void)
+{
+ PRUint32 a, b, c, d;
+ unsigned char plain[ 4 ];
+ unsigned char cypher[ 5 ];
+ char result[ 8 ];
+ char *rv;
+
+ printf("Test 011 (PL_Base64Decode, double characters, equals) ..."); fflush(stdout);
+
+ plain[2] = plain[3] = (unsigned char)0;
+ cypher[3] = (unsigned char)'=';
+ cypher[4] = (unsigned char)0;
+
+ for( a = 0; a < 64; a++ )
+ {
+ cypher[0] = base[a];
+ for( b = 0; b < 4; b++ )
+ {
+ plain[0] = (a*4) + b;
+ for( c = 0; c < 16; c++ )
+ {
+ cypher[1] = base[b*16 + c];
+ for( d = 0; d < 16; d++ )
+ {
+ plain[1] = c*16 + d;
+ cypher[2] = base[d*4];
+
+ rv = PL_Base64Decode((char *)cypher, 4, result);
+ if( rv != result )
+ {
+ printf("FAIL\n\t(%d, %d, %d, %d): return value\n", a, b, c, d);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strncmp((char *)plain, result, 2) )
+ {
+ printf("FAIL\n\t(%d, %d, %d, %d): expected \"%s,\" got \"%.2s.\"\n",
+ a, b, c, d, plain, result);
+ return PR_FALSE;
+ }
+ }
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Decode, double characters */
+PRBool test_012(void)
+{
+ PRUint32 a, b, c, d;
+ unsigned char plain[ 4 ];
+ unsigned char cypher[ 5 ];
+ char result[ 8 ];
+ char *rv;
+
+ printf("Test 012 (PL_Base64Decode, double characters, no equals) ..."); fflush(stdout);
+
+ plain[2] = plain[3] = (unsigned char)0;
+ cypher[3] = (unsigned char)0;
+ cypher[4] = (unsigned char)0;
+
+ for( a = 0; a < 64; a++ )
+ {
+ cypher[0] = base[a];
+ for( b = 0; b < 4; b++ )
+ {
+ plain[0] = (a*4) + b;
+ for( c = 0; c < 16; c++ )
+ {
+ cypher[1] = base[b*16 + c];
+ for( d = 0; d < 16; d++ )
+ {
+ plain[1] = c*16 + d;
+ cypher[2] = base[d*4];
+
+ rv = PL_Base64Decode((char *)cypher, 3, result);
+ if( rv != result )
+ {
+ printf("FAIL\n\t(%d, %d, %d, %d): return value\n", a, b, c, d);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strncmp((char *)plain, result, 2) )
+ {
+ printf("FAIL\n\t(%d, %d, %d, %d): expected \"%s,\" got \"%.2s.\"\n",
+ a, b, c, d, cypher, result);
+ return PR_FALSE;
+ }
+ }
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Decode, triple characters */
+PRBool test_013(void)
+{
+ PRUint32 a, b, c, d, e, f;
+ unsigned char plain[ 4 ];
+ unsigned char cypher[ 5 ];
+ char result[ 8 ];
+ char *rv;
+
+ printf("Test 013 (PL_Base64Decode, triple characters) ..."); fflush(stdout);
+
+ cypher[4] = (unsigned char)0;
+
+ for( a = 0; a < 64; a++ )
+ {
+ cypher[0] = base[a];
+ for( b = 0; b < 4; b++ )
+ {
+ plain[0] = (a*4) + b;
+ for( c = 0; c < 16; c++ )
+ {
+ cypher[1] = base[b*16 + c];
+ for( d = 0; d < 16; d++ )
+ {
+ plain[1] = c*16 + d;
+ for( e = 0; e < 4; e++ )
+ {
+ cypher[2] = base[d*4 + e];
+ for( f = 0; f < 64; f++ )
+ {
+ plain[2] = e * 64 + f;
+ cypher[3] = base[f];
+
+ rv = PL_Base64Decode((char *)cypher, 4, result);
+ if( rv != result )
+ {
+ printf("FAIL\n\t(%d, %d, %d, %d, %d, %d): return value\n", a, b, c, d, e, f);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strncmp((char *)plain, result, 3) )
+ {
+ printf("FAIL\n\t(%d, %d, %d, %d, %d, %d): expected \"%s,\" got \"%.3s.\"\n",
+ a, b, c, d, e, f, plain, result);
+ return PR_FALSE;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Decode, random strings */
+PRBool test_014(void)
+{
+ int i;
+ char result[ 4096 ];
+
+ printf("Test 014 (PL_Base64Decode, random strings, equals) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ PRUint32 clen = PL_strlen(array[i].cyphertext);
+ PRUint32 plen = (clen * 3) / 4;
+
+ char *rv = PL_Base64Decode(array[i].cyphertext, clen, result);
+
+ if( rv != result )
+ {
+ printf("FAIL\n\t(%d): return value\n", i);
+ return PR_FALSE;
+ }
+
+ if( 0 == (clen & 3) )
+ {
+ if( '=' == array[i].cyphertext[clen-1] )
+ {
+ if( '=' == array[i].cyphertext[clen-2] )
+ {
+ plen -= 2;
+ }
+ else
+ {
+ plen -= 1;
+ }
+ }
+ }
+
+ if( 0 != PL_strncmp(result, array[i].plaintext, plen) )
+ {
+ printf("FAIL\n\t(%d, \"%s\"): expected \n\"%s,\" got \n\"%.*s.\"\n",
+ i, array[i].cyphertext, array[i].plaintext, plen, result);
+ return PR_FALSE;
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Decode, random strings */
+PRBool test_015(void)
+{
+ int i;
+ char buffer[ 4096 ];
+ char result[ 4096 ];
+ char *rv;
+
+ printf("Test 015 (PL_Base64Decode, random strings, no equals) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ PRUint32 clen, plen;
+
+ PL_strcpy(buffer, array[i].cyphertext);
+ clen = PL_strlen(buffer);
+
+ if( 0 == (clen & 3) )
+ {
+ if( '=' == buffer[clen-1] )
+ {
+ if( '=' == buffer[clen-2] )
+ {
+ buffer[clen-2] = buffer[clen-1] = (char)0;
+ clen -= 2;
+ }
+ else
+ {
+ buffer[clen-1] = (char)0;
+ clen -= 1;
+ }
+ }
+ }
+
+ plen = (clen * 3) / 4;
+
+ rv = PL_Base64Decode(buffer, clen, result);
+
+ if( rv != result )
+ {
+ printf("FAIL\n\t(%d): return value\n", i);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strncmp(result, array[i].plaintext, plen) )
+ {
+ printf("FAIL\n\t(%d, \"%s\"): expected \n\"%s,\" got \n\"%.*s.\"\n",
+ i, array[i].cyphertext, array[i].plaintext, plen, result);
+ return PR_FALSE;
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Decode, single characters, malloc */
+PRBool test_016(void)
+{
+ PRUint32 a, b;
+ unsigned char plain[ 4 ];
+ unsigned char cypher[ 5 ];
+ char *rv;
+
+ printf("Test 016 (PL_Base64Decode, single characters, equals, malloc) ..."); fflush(stdout);
+
+ plain[1] = plain[2] = plain[3] = (unsigned char)0;
+ cypher[2] = cypher[3] = (unsigned char)'=';
+ cypher[4] = (unsigned char)0;
+
+ for( a = 0; a < 64; a++ )
+ {
+ cypher[0] = base[a];
+
+ for( b = 0; b < 4; b++ )
+ {
+ plain[0] = (unsigned char)(a * 4 + b);
+ cypher[1] = base[(b * 16)];
+
+ rv = PL_Base64Decode((char *)cypher, 4, (char *)0);
+ if( (char *)0 == rv )
+ {
+ printf("FAIL\n\t(%d, %d): no return value\n", a, b);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strcmp((char *)plain, rv) )
+ {
+ printf("FAIL\n\t(%d, %d): expected \"%s,\" got \"%s.\"\n",
+ a, b, plain, rv);
+ PR_DELETE(rv);
+ return PR_FALSE;
+ }
+
+ PR_DELETE(rv);
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Decode, single characters, malloc */
+PRBool test_017(void)
+{
+ PRUint32 a, b;
+ unsigned char plain[ 4 ];
+ unsigned char cypher[ 5 ];
+ char *rv;
+
+ printf("Test 017 (PL_Base64Decode, single characters, no equals, malloc) ..."); fflush(stdout);
+
+ plain[1] = plain[2] = plain[3] = (unsigned char)0;
+ cypher[2] = cypher[3] = (unsigned char)0;
+ cypher[4] = (unsigned char)0;
+
+ for( a = 0; a < 64; a++ )
+ {
+ cypher[0] = base[a];
+
+ for( b = 0; b < 4; b++ )
+ {
+ plain[0] = (unsigned char)(a * 4 + b);
+ cypher[1] = base[(b * 16)];
+
+ rv = PL_Base64Decode((char *)cypher, 2, (char *)0);
+ if( (char *)0 == rv )
+ {
+ printf("FAIL\n\t(%d, %d): no return value\n", a, b);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strcmp((char *)plain, rv) )
+ {
+ printf("FAIL\n\t(%d, %d): expected \"%s,\" got \"%s.\"\n",
+ a, b, plain, rv);
+ PR_DELETE(rv);
+ return PR_FALSE;
+ }
+
+ PR_DELETE(rv);
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Decode, double characters, malloc */
+PRBool test_018(void)
+{
+ PRUint32 a, b, c, d;
+ unsigned char plain[ 4 ];
+ unsigned char cypher[ 5 ];
+ char *rv;
+
+ printf("Test 018 (PL_Base64Decode, double characters, equals, malloc) ..."); fflush(stdout);
+
+ plain[2] = plain[3] = (unsigned char)0;
+ cypher[3] = (unsigned char)'=';
+ cypher[4] = (unsigned char)0;
+
+ for( a = 0; a < 64; a++ )
+ {
+ cypher[0] = base[a];
+ for( b = 0; b < 4; b++ )
+ {
+ plain[0] = (a*4) + b;
+ for( c = 0; c < 16; c++ )
+ {
+ cypher[1] = base[b*16 + c];
+ for( d = 0; d < 16; d++ )
+ {
+ plain[1] = c*16 + d;
+ cypher[2] = base[d*4];
+
+ rv = PL_Base64Decode((char *)cypher, 4, (char *)0);
+ if( (char *)0 == rv )
+ {
+ printf("FAIL\n\t(%d, %d, %d, %d): no return value\n", a, b, c, d);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strcmp((char *)plain, rv) )
+ {
+ printf("FAIL\n\t(%d, %d, %d, %d): expected \"%s,\" got \"%s.\"\n",
+ a, b, c, d, plain, rv);
+ PR_DELETE(rv);
+ return PR_FALSE;
+ }
+
+ PR_DELETE(rv);
+ }
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Decode, double characters, malloc */
+PRBool test_019(void)
+{
+ PRUint32 a, b, c, d;
+ unsigned char plain[ 4 ];
+ unsigned char cypher[ 5 ];
+ char *rv;
+
+ printf("Test 019 (PL_Base64Decode, double characters, no equals, malloc) ..."); fflush(stdout);
+
+ plain[2] = plain[3] = (unsigned char)0;
+ cypher[3] = (unsigned char)0;
+ cypher[4] = (unsigned char)0;
+
+ for( a = 0; a < 64; a++ )
+ {
+ cypher[0] = base[a];
+ for( b = 0; b < 4; b++ )
+ {
+ plain[0] = (a*4) + b;
+ for( c = 0; c < 16; c++ )
+ {
+ cypher[1] = base[b*16 + c];
+ for( d = 0; d < 16; d++ )
+ {
+ plain[1] = c*16 + d;
+ cypher[2] = base[d*4];
+
+ rv = PL_Base64Decode((char *)cypher, 3, (char *)0);
+ if( (char *)0 == rv )
+ {
+ printf("FAIL\n\t(%d, %d, %d, %d): no return value\n", a, b, c, d);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strcmp((char *)plain, rv) )
+ {
+ printf("FAIL\n\t(%d, %d, %d, %d): expected \"%s,\" got \"%s.\"\n",
+ a, b, c, d, cypher, rv);
+ PR_DELETE(rv);
+ return PR_FALSE;
+ }
+
+ PR_DELETE(rv);
+ }
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Decode, triple characters, malloc */
+PRBool test_020(void)
+{
+ PRUint32 a, b, c, d, e, f;
+ unsigned char plain[ 4 ];
+ unsigned char cypher[ 5 ];
+ char *rv;
+
+ printf("Test 020 (PL_Base64Decode, triple characters, malloc) ..."); fflush(stdout);
+
+ cypher[4] = (unsigned char)0;
+ plain[3] = (unsigned char)0;
+
+ for( a = 0; a < 64; a++ )
+ {
+ cypher[0] = base[a];
+ for( b = 0; b < 4; b++ )
+ {
+ plain[0] = (a*4) + b;
+ for( c = 0; c < 16; c++ )
+ {
+ cypher[1] = base[b*16 + c];
+ for( d = 0; d < 16; d++ )
+ {
+ plain[1] = c*16 + d;
+ for( e = 0; e < 4; e++ )
+ {
+ cypher[2] = base[d*4 + e];
+ for( f = 0; f < 64; f++ )
+ {
+ plain[2] = e * 64 + f;
+ cypher[3] = base[f];
+
+ rv = PL_Base64Decode((char *)cypher, 4, (char *)0);
+ if( (char *)0 == rv )
+ {
+ printf("FAIL\n\t(%d, %d, %d, %d, %d, %d): no return value\n", a, b, c, d, e, f);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strcmp((char *)plain, rv) )
+ {
+ printf("FAIL\n\t(%d, %d, %d, %d, %d, %d): expected \"%s,\" got \"%.3s.\"\n",
+ a, b, c, d, e, f, plain, rv);
+ PR_DELETE(rv);
+ return PR_FALSE;
+ }
+
+ PR_DELETE(rv);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Decode, random strings, malloc */
+PRBool test_021(void)
+{
+ int i;
+
+ printf("Test 021 (PL_Base64Decode, random strings, equals, malloc) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ PRUint32 clen = PL_strlen(array[i].cyphertext);
+
+ char *rv = PL_Base64Decode(array[i].cyphertext, clen, (char *)0);
+
+ if( (char *)0 == rv )
+ {
+ printf("FAIL\n\t(%d): no return value\n", i);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strcmp(rv, array[i].plaintext) )
+ {
+ printf("FAIL\n\t(%d, \"%s\"): expected \n\"%s,\" got \n\"%s.\"\n",
+ i, array[i].cyphertext, array[i].plaintext, rv);
+ PR_DELETE(rv);
+ return PR_FALSE;
+ }
+
+ PR_DELETE(rv);
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Encode, random strings, malloc */
+PRBool test_022(void)
+{
+ int i;
+ char buffer[ 4096 ];
+ char *rv;
+
+ printf("Test 022 (PL_Base64Decode, random strings, no equals, malloc) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ PRUint32 clen;
+
+ PL_strcpy(buffer, array[i].cyphertext);
+ clen = PL_strlen(buffer);
+
+ if( 0 == (clen & 3) )
+ {
+ if( '=' == buffer[clen-1] )
+ {
+ if( '=' == buffer[clen-2] )
+ {
+ buffer[clen-2] = buffer[clen-1] = (char)0;
+ clen -= 2;
+ }
+ else
+ {
+ buffer[clen-1] = (char)0;
+ clen -= 1;
+ }
+ }
+ }
+
+ rv = PL_Base64Decode(buffer, clen, (char *)0);
+
+ if( (char *)0 == rv )
+ {
+ printf("FAIL\n\t(%d): no return value\n", i);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strcmp(rv, array[i].plaintext) )
+ {
+ printf("FAIL\n\t(%d, \"%s\"): expected \n\"%s,\" got \n\"%s.\"\n",
+ i, array[i].cyphertext, array[i].plaintext, rv);
+ return PR_FALSE;
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Encode, random strings */
+PRBool test_023(void)
+{
+ int i;
+ char result[ 4096 ];
+
+ printf("Test 023 (PL_Base64Encode, random strings, strlen) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ PRUint32 plen = PL_strlen(array[i].plaintext);
+ PRUint32 clen = ((plen + 2)/3)*4;
+
+ char *rv = PL_Base64Encode(array[i].plaintext, 0, result);
+
+ if( rv != result )
+ {
+ printf("FAIL\n\t(%d): return value\n", i);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strncmp(result, array[i].cyphertext, clen) )
+ {
+ printf("FAIL\n\t(%d, \"%s\"): expected \n\"%s,\" got \n\"%.*s.\"\n",
+ i, array[i].plaintext, array[i].cyphertext, clen, result);
+ return PR_FALSE;
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Encode, random strings, malloc */
+PRBool test_024(void)
+{
+ int i;
+
+ printf("Test 024 (PL_Base64Encode, random strings, malloc, strlen) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ PRUint32 plen = PL_strlen(array[i].plaintext);
+ PRUint32 clen = ((plen + 2)/3)*4;
+
+ char *rv = PL_Base64Encode(array[i].plaintext, 0, (char *)0);
+
+ if( (char *)0 == rv )
+ {
+ printf("FAIL\n\t(%d): no return value\n", i);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strcmp(rv, array[i].cyphertext) )
+ {
+ printf("FAIL\n\t(%d, \"%s\"): expected \n\"%s,\" got \n\"%s.\"\n",
+ i, array[i].plaintext, array[i].cyphertext, rv);
+ return PR_FALSE;
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Decode, random strings */
+PRBool test_025(void)
+{
+ int i;
+ char result[ 4096 ];
+
+ printf("Test 025 (PL_Base64Decode, random strings, equals, strlen) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ PRUint32 clen = PL_strlen(array[i].cyphertext);
+ PRUint32 plen = (clen * 3) / 4;
+
+ char *rv = PL_Base64Decode(array[i].cyphertext, 0, result);
+
+ if( rv != result )
+ {
+ printf("FAIL\n\t(%d): return value\n", i);
+ return PR_FALSE;
+ }
+
+ if( 0 == (clen & 3) )
+ {
+ if( '=' == array[i].cyphertext[clen-1] )
+ {
+ if( '=' == array[i].cyphertext[clen-2] )
+ {
+ plen -= 2;
+ }
+ else
+ {
+ plen -= 1;
+ }
+ }
+ }
+
+ if( 0 != PL_strncmp(result, array[i].plaintext, plen) )
+ {
+ printf("FAIL\n\t(%d, \"%s\"): expected \n\"%s,\" got \n\"%.*s.\"\n",
+ i, array[i].cyphertext, array[i].plaintext, plen, result);
+ return PR_FALSE;
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Decode, random strings */
+PRBool test_026(void)
+{
+ int i;
+ char buffer[ 4096 ];
+ char result[ 4096 ];
+ char *rv;
+
+ printf("Test 026 (PL_Base64Decode, random strings, no equals, strlen) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ PRUint32 clen, plen;
+
+ PL_strcpy(buffer, array[i].cyphertext);
+ clen = PL_strlen(buffer);
+
+ if( 0 == (clen & 3) )
+ {
+ if( '=' == buffer[clen-1] )
+ {
+ if( '=' == buffer[clen-2] )
+ {
+ buffer[clen-2] = buffer[clen-1] = (char)0;
+ clen -= 2;
+ }
+ else
+ {
+ buffer[clen-1] = (char)0;
+ clen -= 1;
+ }
+ }
+ }
+
+ plen = (clen * 3) / 4;
+
+ rv = PL_Base64Decode(buffer, 0, result);
+
+ if( rv != result )
+ {
+ printf("FAIL\n\t(%d): return value\n", i);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strncmp(result, array[i].plaintext, plen) )
+ {
+ printf("FAIL\n\t(%d, \"%s\"): expected \n\"%s,\" got \n\"%.*s.\"\n",
+ i, array[i].cyphertext, array[i].plaintext, plen, result);
+ return PR_FALSE;
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Decode, random strings, malloc */
+PRBool test_027(void)
+{
+ int i;
+
+ printf("Test 027 (PL_Base64Decode, random strings, equals, malloc, strlen) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ PRUint32 clen = PL_strlen(array[i].cyphertext);
+
+ char *rv = PL_Base64Decode(array[i].cyphertext, 0, (char *)0);
+
+ if( (char *)0 == rv )
+ {
+ printf("FAIL\n\t(%d): no return value\n", i);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strcmp(rv, array[i].plaintext) )
+ {
+ printf("FAIL\n\t(%d, \"%s\"): expected \n\"%s,\" got \n\"%s.\"\n",
+ i, array[i].cyphertext, array[i].plaintext, rv);
+ PR_DELETE(rv);
+ return PR_FALSE;
+ }
+
+ PR_DELETE(rv);
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_Base64Encode, random strings, malloc */
+PRBool test_028(void)
+{
+ int i;
+ char buffer[ 4096 ];
+ char *rv;
+
+ printf("Test 028 (PL_Base64Decode, random strings, no equals, malloc, strlen) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ PRUint32 clen;
+
+ PL_strcpy(buffer, array[i].cyphertext);
+ clen = PL_strlen(buffer);
+
+ if( 0 == (clen & 3) )
+ {
+ if( '=' == buffer[clen-1] )
+ {
+ if( '=' == buffer[clen-2] )
+ {
+ buffer[clen-2] = buffer[clen-1] = (char)0;
+ clen -= 2;
+ }
+ else
+ {
+ buffer[clen-1] = (char)0;
+ clen -= 1;
+ }
+ }
+ }
+
+ rv = PL_Base64Decode(buffer, 0, (char *)0);
+
+ if( (char *)0 == rv )
+ {
+ printf("FAIL\n\t(%d): no return value\n", i);
+ return PR_FALSE;
+ }
+
+ if( 0 != PL_strcmp(rv, array[i].plaintext) )
+ {
+ printf("FAIL\n\t(%d, \"%s\"): expected \n\"%s,\" got \n\"%s.\"\n",
+ i, array[i].cyphertext, array[i].plaintext, rv);
+ return PR_FALSE;
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+int
+main
+(
+ int argc,
+ char *argv[]
+)
+{
+ printf("Testing the Portable Library base64 functions:\n");
+ printf("(warning: the \"triple characters\" tests are slow)\n");
+
+ if( 1
+ && test_001()
+ && test_002()
+ && test_003()
+ && test_004()
+ && test_005()
+ && test_006()
+ && test_007()
+ && test_008()
+ && test_009()
+ && test_010()
+ && test_011()
+ && test_012()
+ && test_013()
+ && test_014()
+ && test_015()
+ && test_016()
+ && test_017()
+ && test_018()
+ && test_019()
+ && test_020()
+ && test_021()
+ && test_022()
+ && test_023()
+ && test_024()
+ && test_025()
+ && test_026()
+ && test_027()
+ && test_028()
+ )
+ {
+ printf("Suite passed.\n");
+ return 0;
+ }
+ else
+ {
+ printf("Suite failed.\n");
+ return 1;
+ }
+
+ /*NOTREACHED*/
+}
diff --git a/lib/tests/string.c b/lib/tests/string.c
new file mode 100644
index 00000000..f30deb4d
--- /dev/null
+++ b/lib/tests/string.c
@@ -0,0 +1,3043 @@
+/* -*- 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 "plstr.h"
+#include "nspr.h"
+
+#include <stdio.h>
+
+/* PL_strlen */
+PRBool test_001(void)
+{
+ static struct
+ {
+ const char *str;
+ PRUint32 len;
+ } array[] =
+ {
+ { (const char *)0, 0 },
+ { "", 0 },
+ { "a", 1 },
+ { "abcdefg", 7 },
+ { "abcdefg\0hijk", 7 }
+ };
+
+ int i;
+
+ printf("Test 001 (PL_strlen) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ if( PL_strlen(array[i].str) != array[i].len )
+ {
+ printf("FAIL (%d: %s->%d, %d)\n", i,
+ array[i].str ? array[i].str : "(null)",
+ PL_strlen(array[i].str), array[i].len);
+ return PR_FALSE;
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strnlen */
+PRBool test_002(void)
+{
+ static struct
+ {
+ const char *str;
+ PRUint32 max;
+ PRUint32 len;
+ } array[] =
+ {
+ { (const char *)0, 0, 0 },
+ { (const char *)0, 12, 0 },
+ { "", 0, 0 },
+ { "", 12, 0 },
+ { "a", 0, 0 },
+ { "a", 1, 1 },
+ { "a", 12, 1 },
+ { "abcdefg", 0, 0 },
+ { "abcdefg", 1, 1 },
+ { "abcdefg", 7, 7 },
+ { "abcdefg", 12, 7 },
+ { "abcdefg\0hijk", 0, 0 },
+ { "abcdefg\0hijk", 1, 1 },
+ { "abcdefg\0hijk", 7, 7 },
+ { "abcdefg\0hijk", 12, 7 },
+ };
+
+ int i;
+
+ printf("Test 002 (PL_strnlen) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ if( PL_strnlen(array[i].str, array[i].max) != array[i].len )
+ {
+ printf("FAIL (%d: %s,%d->%d, %d)\n", i,
+ array[i].str ? array[i].str : "(null)", array[i].max,
+ PL_strnlen(array[i].str, array[i].max), array[i].len);
+ return PR_FALSE;
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strcpy */
+PRBool test_003(void)
+{
+ static char buffer[ 1024 ];
+
+ static struct
+ {
+ const char *str;
+ char *dest;
+ char *rv;
+ PRBool comp;
+ } array[] =
+ {
+ { (const char *)0, (char *)0, (char *)0, PR_FALSE },
+ { (const char *)0, buffer, (char *)0, PR_FALSE },
+ { "", (char *)0, (char *)0, PR_FALSE },
+ { "", buffer, buffer, PR_TRUE },
+ { "a", (char *)0, (char *)0, PR_FALSE },
+ { "a", buffer, buffer, PR_TRUE },
+ { "abcdefg", (char *)0, (char *)0, PR_FALSE },
+ { "abcdefg", buffer, buffer, PR_TRUE },
+ { "wxyz\0abcdefg", (char *)0, (char *)0, PR_FALSE },
+ { "wxyz\0abcdefg", buffer, buffer, PR_TRUE }
+ };
+
+ int i;
+
+ printf("Test 003 (PL_strcpy) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char *rv;
+ const char *a = array[i].str;
+ const char *b = (const char *)array[i].dest;
+
+ rv = PL_strcpy(array[i].dest, array[i].str);
+ if( array[i].rv != rv )
+ {
+ printf("FAIL %d: (0x%x, %s)->0x%x\n", i, array[i].dest,
+ array[i].str ? array[i].str : "(null)", rv);
+ return PR_FALSE;
+ }
+
+ if( array[i].comp )
+ {
+ while( 1 )
+ {
+ if( *a != *b )
+ {
+ printf("FAIL %d: %s->%.32s\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].dest ? array[i].dest : "(null)");
+ return PR_FALSE;
+ }
+
+ if( (char)0 == *a ) break;
+
+ a++;
+ b++;
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strncpy */
+PRBool test_004(void)
+{
+ static char buffer[ 1024 ];
+
+ static struct
+ {
+ const char *str;
+ PRUint32 len;
+ char *dest;
+ char *rv;
+ PRBool comp;
+ const char *result;
+ PRBool nulled;
+ } array[] =
+ {
+ { (const char *)0, 0, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { (const char *)0, 0, buffer, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { (const char *)0, 1, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { (const char *)0, 7, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { (const char *)0, 1, buffer, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { (const char *)0, 7, buffer, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { "", 0, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { "", 0, buffer, buffer, PR_FALSE, (const char *)0, PR_FALSE },
+ { "", 1, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { "", 7, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { "", 1, buffer, buffer, PR_TRUE, "", PR_TRUE },
+ { "", 7, buffer, buffer, PR_TRUE, "", PR_TRUE },
+ { "a", 0, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { "a", 0, buffer, buffer, PR_FALSE, (const char *)0, PR_FALSE },
+ { "a", 1, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { "a", 7, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { "b", 1, buffer, buffer, PR_TRUE, "b", PR_FALSE },
+ { "c", 7, buffer, buffer, PR_TRUE, "c", PR_TRUE },
+ { "de", 0, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { "de", 0, buffer, buffer, PR_FALSE, (const char *)0, PR_FALSE },
+ { "de", 1, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { "de", 7, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { "fg", 1, buffer, buffer, PR_TRUE, "f", PR_FALSE },
+ { "hi", 7, buffer, buffer, PR_TRUE, "hi", PR_TRUE },
+ { "jklmnopq", 0, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { "jklmnopq", 0, buffer, buffer, PR_FALSE, (const char *)0, PR_FALSE },
+ { "jklmnopq", 1, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { "jklmnopq", 7, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { "rstuvwxy", 1, buffer, buffer, PR_TRUE, "r", PR_FALSE },
+ { "zABCDEFG", 7, buffer, buffer, PR_TRUE, "zABCDEF", PR_FALSE },
+ { "a\0XXX", 0, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { "a\0XXX", 0, buffer, buffer, PR_FALSE, (const char *)0, PR_FALSE },
+ { "a\0XXX", 1, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { "a\0XXX", 7, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { "b\0XXX", 1, buffer, buffer, PR_TRUE, "b", PR_FALSE },
+ { "c\0XXX", 7, buffer, buffer, PR_TRUE, "c", PR_TRUE },
+ { "de\0XXX", 0, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { "de\0XXX", 0, buffer, buffer, PR_FALSE, (const char *)0, PR_FALSE },
+ { "de\0XXX", 1, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { "de\0XXX", 7, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { "fg\0XXX", 1, buffer, buffer, PR_TRUE, "f", PR_FALSE },
+ { "hi\0XXX", 7, buffer, buffer, PR_TRUE, "hi", PR_TRUE },
+ { "jklmnopq\0XXX", 0, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { "jklmnopq\0XXX", 0, buffer, buffer, PR_FALSE, (const char *)0, PR_FALSE },
+ { "jklmnopq\0XXX", 1, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { "jklmnopq\0XXX", 7, (char *)0, (char *)0, PR_FALSE, (const char *)0, PR_FALSE },
+ { "rstuvwxy\0XXX", 1, buffer, buffer, PR_TRUE, "r", PR_FALSE },
+ { "zABCDEFG\0XXX", 7, buffer, buffer, PR_TRUE, "zABCDEF", PR_FALSE },
+ };
+
+ int i;
+
+ printf("Test 004 (PL_strncpy) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char *rv;
+ int j;
+
+ for( j = 0; j < sizeof(buffer); j++ )
+ buffer[j] = '-';
+
+ rv = PL_strncpy(array[i].dest, array[i].str, array[i].len);
+ if( array[i].rv != rv )
+ {
+ printf("FAIL %d: (0x%x, %s, %lu)->0x%x\n", i, array[i].dest,
+ array[i].str ? array[i].str : "(null)", array[i].len, rv);
+ return PR_FALSE;
+ }
+
+ if( array[i].comp )
+ {
+ const char *a = array[i].result;
+ const char *b = array[i].dest;
+
+ while( *a )
+ {
+ if( *a != *b )
+ {
+ printf("FAIL %d: %s != %.32s\n", i,
+ array[i].result, array[i].dest);
+ return PR_FALSE;
+ }
+
+ a++;
+ b++;
+ }
+
+ if( array[i].nulled )
+ {
+ if( *b != '\0' )
+ {
+ printf("FAIL %d: not terminated\n", i);
+ return PR_FALSE;
+ }
+ }
+ else
+ {
+ if( *b != '-' )
+ {
+ printf("FAIL %d: overstepped\n", i);
+ return PR_FALSE;
+ }
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strncpyz */
+PRBool test_005(void)
+{
+ static char buffer[ 1024 ];
+
+ static struct
+ {
+ const char *str;
+ PRUint32 len;
+ char *dest;
+ char *rv;
+ PRBool comp;
+ const char *result;
+ } array[] =
+ {
+ { (const char *)0, 0, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { (const char *)0, 0, buffer, (char *)0, PR_FALSE, (const char *)0 },
+ { (const char *)0, 1, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { (const char *)0, 7, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { (const char *)0, 1, buffer, (char *)0, PR_FALSE, (const char *)0 },
+ { (const char *)0, 7, buffer, (char *)0, PR_FALSE, (const char *)0 },
+ { "", 0, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { "", 0, buffer, (char *)0, PR_FALSE, (const char *)0 },
+ { "", 1, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { "", 7, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { "", 1, buffer, buffer, PR_TRUE, "" },
+ { "", 7, buffer, buffer, PR_TRUE, "" },
+ { "a", 0, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { "a", 0, buffer, (char *)0, PR_FALSE, (const char *)0 },
+ { "a", 1, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { "a", 7, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { "b", 1, buffer, buffer, PR_TRUE, "" },
+ { "c", 7, buffer, buffer, PR_TRUE, "c" },
+ { "de", 0, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { "de", 0, buffer, (char *)0, PR_FALSE, (const char *)0 },
+ { "de", 1, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { "de", 7, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { "fg", 1, buffer, buffer, PR_TRUE, "" },
+ { "hi", 7, buffer, buffer, PR_TRUE, "hi" },
+ { "jklmnopq", 0, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { "jklmnopq", 0, buffer, (char *)0, PR_FALSE, (const char *)0 },
+ { "jklmnopq", 1, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { "jklmnopq", 7, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { "rstuvwxy", 1, buffer, buffer, PR_TRUE, "" },
+ { "zABCDEFG", 7, buffer, buffer, PR_TRUE, "zABCDE" },
+ { "a\0XXX", 0, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { "a\0XXX", 0, buffer, (char *)0, PR_FALSE, (const char *)0 },
+ { "a\0XXX", 1, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { "a\0XXX", 7, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { "b\0XXX", 1, buffer, buffer, PR_TRUE, "" },
+ { "c\0XXX", 7, buffer, buffer, PR_TRUE, "c" },
+ { "de\0XXX", 0, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { "de\0XXX", 0, buffer, (char *)0, PR_FALSE, (const char *)0 },
+ { "de\0XXX", 1, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { "de\0XXX", 7, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { "fg\0XXX", 1, buffer, buffer, PR_TRUE, "" },
+ { "hi\0XXX", 7, buffer, buffer, PR_TRUE, "hi" },
+ { "jklmnopq\0XXX", 0, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { "jklmnopq\0XXX", 0, buffer, (char *)0, PR_FALSE, (const char *)0 },
+ { "jklmnopq\0XXX", 1, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { "jklmnopq\0XXX", 7, (char *)0, (char *)0, PR_FALSE, (const char *)0 },
+ { "rstuvwxy\0XXX", 1, buffer, buffer, PR_TRUE, "" },
+ { "zABCDEFG\0XXX", 7, buffer, buffer, PR_TRUE, "zABCDE" },
+ };
+
+ int i;
+
+ printf("Test 005 (PL_strncpyz) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char *rv;
+ int j;
+
+ for( j = 0; j < sizeof(buffer); j++ )
+ buffer[j] = '-';
+
+ rv = PL_strncpyz(array[i].dest, array[i].str, array[i].len);
+ if( array[i].rv != rv )
+ {
+ printf("FAIL %d: (0x%x, %s, %lu)->0x%x\n", i, array[i].dest,
+ array[i].str ? array[i].str : "(null)", array[i].len, rv);
+ return PR_FALSE;
+ }
+
+ if( array[i].comp )
+ {
+ const char *a = array[i].result;
+ const char *b = array[i].dest;
+
+ while( 1 )
+ {
+ if( *a != *b )
+ {
+ printf("FAIL %d: %s != %.32s\n", i,
+ array[i].result, array[i].dest);
+ return PR_FALSE;
+ }
+
+ if( (char)0 == *a ) break;
+
+ a++;
+ b++;
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strdup */
+PRBool test_006(void)
+{
+ static const char *array[] =
+ {
+ (const char *)0,
+ "",
+ "a",
+ "abcdefg"
+ };
+
+ int i;
+
+ printf("Test 006 (PL_strdup) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char *rv = PL_strdup(array[i]);
+
+ if( (char *)0 == rv )
+ {
+ printf("FAIL %d: 0x%x -> 0\n", i, array[i]);
+ return PR_FALSE;
+ }
+
+ if( (const char *)0 == array[i] )
+ {
+ if( (char)0 != *rv )
+ {
+ printf("FAIL %d: (const char *)0 -> %.32s\n", i, rv);
+ return PR_FALSE;
+ }
+ }
+ else
+ {
+ const char *a = array[i];
+ const char *b = (const char *)rv;
+
+ while( 1 )
+ {
+ if( *a != *b )
+ {
+ printf("FAIL %d: %s != %.32s\n", i, array[i], rv);
+ return PR_FALSE;
+ }
+
+ if( (char)0 == *a ) break;
+
+ a++;
+ b++;
+ }
+
+ free(rv);
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strndup */
+PRBool test_007(void)
+{
+ static struct
+ {
+ const char *str;
+ PRUint32 len;
+ const char *result;
+ } array[] =
+ {
+ { (const char *)0, 0, "" },
+ { (const char *)0, 1, "" },
+ { (const char *)0, 7, "" },
+ { "", 0, "" },
+ { "", 1, "" },
+ { "", 7, "" },
+ { "a", 0, "" },
+ { "a", 1, "a" },
+ { "a", 7, "a" },
+ { "ab", 0, "" },
+ { "ab", 1, "a" },
+ { "ab", 7, "ab" },
+ { "abcdefg", 0, "" },
+ { "abcdefg", 1, "a" },
+ { "abcdefg", 7, "abcdefg" },
+ { "abcdefghijk", 0, "" },
+ { "abcdefghijk", 1, "a" },
+ { "abcdefghijk", 7, "abcdefg" },
+ { "abcdef\0ghijk", 0, "" },
+ { "abcdef\0ghijk", 1, "a" },
+ { "abcdef\0ghijk", 7, "abcdef" }
+ };
+
+ int i;
+
+ printf("Test 007 (PL_strndup) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char *rv = PL_strndup(array[i].str, array[i].len);
+ const char *a;
+ const char *b;
+
+ if( (char *)0 == rv )
+ {
+ printf("FAIL %d: %s,%lu -> 0\n", i,
+ array[i].str ? array[i].str : "(null)", array[i].len);
+ return PR_FALSE;
+ }
+
+ a = array[i].result;
+ b = (const char *)rv;
+
+ while( 1 )
+ {
+ if( *a != *b )
+ {
+ printf("FAIL %d: %s != %.32s\n", i, array[i].result, rv);
+ return PR_FALSE;
+ }
+
+ if( (char)0 == *a ) break;
+
+ a++;
+ b++;
+ }
+
+ free(rv);
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strcat */
+PRBool test_008(void)
+{
+ static struct
+ {
+ const char *first;
+ const char *second;
+ const char *result;
+ } array[] =
+ {
+ { (const char *)0, (const char *)0, (const char *)0 },
+ { (const char *)0, "xyz", (const char *)0 },
+ { "", (const char *)0, "" },
+ { "", "", "" },
+ { "ab", "", "ab" },
+ { "cd", "ef", "cdef" },
+ { "gh\0X", "", "gh" },
+ { "ij\0X", "kl", "ijkl" },
+ { "mn\0X", "op\0X", "mnop" },
+ { "qr", "st\0X", "qrst" },
+ { "uv\0X", "wx\0X", "uvwx" }
+ };
+
+ int i;
+
+ printf("Test 008 (PL_strcat) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char buffer[ 1024 ];
+ int j;
+ char *rv;
+
+ for( j = 0; j < sizeof(buffer); j++ )
+ buffer[j] = '-';
+
+ if( (const char *)0 != array[i].first )
+ (void)PL_strcpy(buffer, array[i].first);
+
+ rv = PL_strcat(((const char *)0 == array[i].first) ? (char *)0 : buffer,
+ array[i].second);
+
+ if( (const char *)0 == array[i].result )
+ {
+ if( (char *)0 != rv )
+ {
+ printf("FAIL %d: %s+%s -> %.32s, not zero\n", i,
+ array[i].first ? array[i].first : "(null)",
+ array[i].second ? array[i].second : "(null)",
+ rv);
+ return PR_FALSE;
+ }
+ }
+ else
+ {
+ if( (char *)0 == rv )
+ {
+ printf("FAIL %d: %s+%s -> null, not %s\n", i,
+ array[i].first ? array[i].first : "(null)",
+ array[i].second ? array[i].second : "(null)",
+ array[i].result);
+ return PR_FALSE;
+ }
+ else
+ {
+ const char *a = array[i].result;
+ const char *b = (const char *)rv;
+
+ while( 1 )
+ {
+ if( *a != *b )
+ {
+ printf("FAIL %d: %s+%s -> %.32s, not %s\n", i,
+ array[i].first ? array[i].first : "(null)",
+ array[i].second ? array[i].second : "(null)",
+ rv, array[i].result);
+ return PR_FALSE;
+ }
+
+ if( (char)0 == *a ) break;
+
+ a++;
+ b++;
+ }
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strncat */
+PRBool test_009(void)
+{
+ static struct
+ {
+ const char *first;
+ const char *second;
+ PRUint32 length;
+ PRBool nulled;
+ const char *result;
+ } array[] =
+ {
+ { (const char *)0, (const char *)0, 0, PR_FALSE, (const char *)0 },
+ { (const char *)0, (const char *)0, 1, PR_FALSE, (const char *)0 },
+ { (const char *)0, (const char *)0, 7, PR_FALSE, (const char *)0 },
+ { (const char *)0, "", 0, PR_FALSE, (const char *)0 },
+ { (const char *)0, "", 1, PR_FALSE, (const char *)0 },
+ { (const char *)0, "", 7, PR_FALSE, (const char *)0 },
+ { (const char *)0, "stuff", 0, PR_FALSE, (const char *)0 },
+ { (const char *)0, "stuff", 1, PR_FALSE, (const char *)0 },
+ { (const char *)0, "stuff", 7, PR_FALSE, (const char *)0 },
+ { "", (const char *)0, 0, PR_TRUE, "" },
+ { "", (const char *)0, 1, PR_TRUE, "" },
+ { "", (const char *)0, 7, PR_TRUE, "" },
+ { "", "", 0, PR_TRUE, "" },
+ { "", "", 1, PR_TRUE, "" },
+ { "", "", 7, PR_TRUE, "" },
+ { "", "abcdefgh", 0, PR_TRUE, "" },
+ { "", "abcdefgh", 1, PR_FALSE, "a" },
+ { "", "abcdefgh", 7, PR_FALSE, "abcdefg" },
+ { "xyz", (const char *)0, 0, PR_TRUE, "xyz" },
+ { "xyz", (const char *)0, 1, PR_TRUE, "xyz" },
+ { "xyz", (const char *)0, 7, PR_TRUE, "xyz" },
+ { "xyz", "", 0, PR_TRUE, "xyz" },
+ { "xyz", "", 1, PR_TRUE, "xyz" },
+ { "xyz", "", 7, PR_TRUE, "xyz" },
+ { "xyz", "abcdefgh", 0, PR_TRUE, "xyz" },
+ { "xyz", "abcdefgh", 1, PR_FALSE, "xyza" },
+ { "xyz", "abcdefgh", 7, PR_FALSE, "xyzabcdefg" }
+ };
+
+ int i;
+
+ printf("Test 009 (PL_strncat) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char buffer[ 1024 ];
+ int j;
+ char *rv;
+
+ for( j = 0; j < sizeof(buffer); j++ )
+ buffer[j] = '-';
+
+ if( (const char *)0 != array[i].first )
+ (void)PL_strcpy(buffer, array[i].first);
+
+ rv = PL_strncat(((const char *)0 == array[i].first) ? (char *)0 : buffer,
+ array[i].second, array[i].length);
+
+ if( (const char *)0 == array[i].result )
+ {
+ if( (char *)0 != rv )
+ {
+ printf("FAIL %d: %s+%s/%lu -> %.32s, not zero\n", i,
+ array[i].first ? array[i].first : "(null)",
+ array[i].second ? array[i].second : "(null)",
+ array[i].length, rv);
+ return PR_FALSE;
+ }
+ }
+ else
+ {
+ if( (char *)0 == rv )
+ {
+ printf("FAIL %d: %s+%s/%lu -> null, not %s\n", i,
+ array[i].first ? array[i].first : "(null)",
+ array[i].second ? array[i].second : "(null)",
+ array[i].length, array[i].result);
+ return PR_FALSE;
+ }
+ else
+ {
+ const char *a = array[i].result;
+ const char *b = (const char *)rv;
+
+ while( *a )
+ {
+ if( *a != *b )
+ {
+ printf("FAIL %d: %s+%s/%lu -> %.32s, not %s\n", i,
+ array[i].first ? array[i].first : "(null)",
+ array[i].second ? array[i].second : "(null)",
+ array[i].length, rv, array[i].result);
+ return PR_FALSE;
+ }
+
+ a++;
+ b++;
+ }
+
+ if( array[i].nulled )
+ {
+ if( (char)0 != *b )
+ {
+ printf("FAIL %d: %s+%s/%lu -> not nulled\n", i,
+ array[i].first ? array[i].first : "(null)",
+ array[i].second ? array[i].second : "(null)",
+ array[i].length);
+ return PR_FALSE;
+ }
+ }
+ else
+ {
+ if( (char)0 == *b )
+ {
+ printf("FAIL %d: %s+%s/%lu -> overrun\n", i,
+ array[i].first ? array[i].first : "(null)",
+ array[i].second ? array[i].second : "(null)",
+ array[i].length);
+ return PR_FALSE;
+ }
+ }
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strcatn */
+PRBool test_010(void)
+{
+ static struct
+ {
+ const char *first;
+ const char *second;
+ PRUint32 length;
+ const char *result;
+ } array[] =
+ {
+ { (const char *)0, (const char *)0, 0, (const char *)0 },
+ { (const char *)0, (const char *)0, 1, (const char *)0 },
+ { (const char *)0, (const char *)0, 7, (const char *)0 },
+ { (const char *)0, "", 0, (const char *)0 },
+ { (const char *)0, "", 1, (const char *)0 },
+ { (const char *)0, "", 7, (const char *)0 },
+ { (const char *)0, "stuff", 0, (const char *)0 },
+ { (const char *)0, "stuff", 1, (const char *)0 },
+ { (const char *)0, "stuff", 7, (const char *)0 },
+ { "", (const char *)0, 0, "" },
+ { "", (const char *)0, 1, "" },
+ { "", (const char *)0, 7, "" },
+ { "", "", 0, "" },
+ { "", "", 1, "" },
+ { "", "", 7, "" },
+ { "", "abcdefgh", 0, "" },
+ { "", "abcdefgh", 1, "" },
+ { "", "abcdefgh", 7, "abcdef" },
+ { "xyz", (const char *)0, 0, "xyz" },
+ { "xyz", (const char *)0, 1, "xyz" },
+ { "xyz", (const char *)0, 7, "xyz" },
+ { "xyz", "", 0, "xyz" },
+ { "xyz", "", 1, "xyz" },
+ { "xyz", "", 7, "xyz" },
+ { "xyz", "abcdefgh", 0, "xyz" },
+ { "xyz", "abcdefgh", 1, "xyz" },
+ { "xyz", "abcdefgh", 7, "xyzabc" }
+ };
+
+ int i;
+
+ printf("Test 010 (PL_strcatn) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char buffer[ 1024 ];
+ int j;
+ char *rv;
+
+ for( j = 0; j < sizeof(buffer); j++ )
+ buffer[j] = '-';
+
+ if( (const char *)0 != array[i].first )
+ (void)PL_strcpy(buffer, array[i].first);
+
+ rv = PL_strcatn(((const char *)0 == array[i].first) ? (char *)0 : buffer,
+ array[i].length, array[i].second);
+
+ if( (const char *)0 == array[i].result )
+ {
+ if( (char *)0 != rv )
+ {
+ printf("FAIL %d: %s+%s/%lu -> %.32s, not zero\n", i,
+ array[i].first ? array[i].first : "(null)",
+ array[i].second ? array[i].second : "(null)",
+ array[i].length, rv);
+ return PR_FALSE;
+ }
+ }
+ else
+ {
+ if( (char *)0 == rv )
+ {
+ printf("FAIL %d: %s+%s/%lu -> null, not %s\n", i,
+ array[i].first ? array[i].first : "(null)",
+ array[i].second ? array[i].second : "(null)",
+ array[i].length, array[i].result);
+ return PR_FALSE;
+ }
+ else
+ {
+ const char *a = array[i].result;
+ const char *b = (const char *)rv;
+
+ while( 1 )
+ {
+ if( *a != *b )
+ {
+ printf("FAIL %d: %s+%s/%lu -> %.32s, not %s\n", i,
+ array[i].first ? array[i].first : "(null)",
+ array[i].second ? array[i].second : "(null)",
+ array[i].length, rv, array[i].result);
+ return PR_FALSE;
+ }
+
+ if( (char)0 == *a ) break;
+
+ a++;
+ b++;
+ }
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strcmp */
+PRBool test_011(void)
+{
+ static struct
+ {
+ const char *one;
+ const char *two;
+ PRIntn sign;
+ } array[] =
+ {
+ { (const char *)0, (const char *)0, 0 },
+ { (const char *)0, "word", -1 },
+ { "word", (const char *)0, 1 },
+ { "word", "word", 0 },
+ { "aZYXVUT", "bZYXVUT", -1 },
+ { "aZYXVUT", "bAAAAAA", -1 },
+ { "a", "aa", -1 },
+ { "a", "a", 0 },
+ { "a", "A", 1 },
+ { "aaaaa", "baaaa", -1 },
+ { "aaaaa", "abaaa", -1 },
+ { "aaaaa", "aabaa", -1 },
+ { "aaaaa", "aaaba", -1 },
+ { "aaaaa", "aaaab", -1 },
+ { "bZYXVUT", "aZYXVUT", 1 },
+ { "bAAAAAA", "aZYXVUT", 1 },
+ { "aa", "a", 1 },
+ { "A", "a", -1 },
+ { "baaaa", "aaaaa", 1 },
+ { "abaaa", "aaaaa", 1 },
+ { "aabaa", "aaaaa", 1 },
+ { "aaaba", "aaaaa", 1 },
+ { "aaaab", "aaaaa", 1 },
+ { "word", "Word", 1 },
+ { "word", "wOrd", 1 },
+ { "word", "woRd", 1 },
+ { "word", "worD", 1 },
+ { "WORD", "wORD", -1 },
+ { "WORD", "WoRD", -1 },
+ { "WORD", "WOrD", -1 },
+ { "WORD", "WORd", -1 }
+ };
+
+ int i;
+
+ printf("Test 011 (PL_strcmp) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ PRIntn rv = PL_strcmp(array[i].one, array[i].two);
+
+ switch( array[i].sign )
+ {
+ case -1:
+ if( rv < 0 ) continue;
+ break;
+ case 1:
+ if( rv > 0 ) continue;
+ break;
+ case 0:
+ if( 0 == rv ) continue;
+ break;
+ default:
+ PR_NOT_REACHED("static data inconsistancy");
+ break;
+ }
+
+ printf("FAIL %d: %s-%s -> %d, not %d\n", i,
+ array[i].one ? array[i].one : "(null)",
+ array[i].two ? array[i].two : "(null)",
+ rv, array[i].sign);
+ return PR_FALSE;
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strncmp */
+PRBool test_012(void)
+{
+ static struct
+ {
+ const char *one;
+ const char *two;
+ PRUint32 max;
+ PRIntn sign;
+ } array[] =
+ {
+ { (const char *)0, (const char *)0, 0, 0 },
+ { (const char *)0, (const char *)0, 1, 0 },
+ { (const char *)0, (const char *)0, 4, 0 },
+ { (const char *)0, "word", 0, -1 },
+ { (const char *)0, "word", 1, -1 },
+ { (const char *)0, "word", 4, -1 },
+ { "word", (const char *)0, 0, 1 },
+ { "word", (const char *)0, 1, 1 },
+ { "word", (const char *)0, 4, 1 },
+ { "word", "word", 0, 0 },
+ { "word", "word", 1, 0 },
+ { "word", "word", 3, 0 },
+ { "word", "word", 5, 0 },
+ { "aZYXVUT", "bZYXVUT", 0, 0 },
+ { "aZYXVUT", "bZYXVUT", 1, -1 },
+ { "aZYXVUT", "bZYXVUT", 4, -1 },
+ { "aZYXVUT", "bZYXVUT", 9, -1 },
+ { "aZYXVUT", "bAAAAAA", 0, 0 },
+ { "aZYXVUT", "bAAAAAA", 1, -1 },
+ { "aZYXVUT", "bAAAAAA", 4, -1 },
+ { "aZYXVUT", "bAAAAAA", 5, -1 },
+ { "a", "aa", 0, 0 },
+ { "a", "aa", 1, 0 },
+ { "a", "aa", 4, -1 },
+ { "a", "a", 0, 0 },
+ { "a", "a", 1, 0 },
+ { "a", "a", 4, 0 },
+ { "a", "A", 0, 0 },
+ { "a", "A", 1, 1 },
+ { "a", "A", 4, 1 },
+ { "aaaaa", "baaaa", 0, 0 },
+ { "aaaaa", "baaaa", 1, -1 },
+ { "aaaaa", "baaaa", 4, -1 },
+ { "aaaaa", "abaaa", 0, 0 },
+ { "aaaaa", "abaaa", 1, 0 },
+ { "aaaaa", "abaaa", 4, -1 },
+ { "aaaaa", "aabaa", 0, 0 },
+ { "aaaaa", "aabaa", 1, 0 },
+ { "aaaaa", "aabaa", 4, -1 },
+ { "aaaaa", "aaaba", 0, 0 },
+ { "aaaaa", "aaaba", 1, 0 },
+ { "aaaaa", "aaaba", 4, -1 },
+ { "aaaaa", "aaaab", 0, 0 },
+ { "aaaaa", "aaaab", 1, 0 },
+ { "aaaaa", "aaaab", 4, 0 },
+ { "bZYXVUT", "aZYXVUT", 0, 0 },
+ { "bZYXVUT", "aZYXVUT", 1, 1 },
+ { "bZYXVUT", "aZYXVUT", 4, 1 },
+ { "bAAAAAA", "aZYXVUT", 0, 0 },
+ { "bAAAAAA", "aZYXVUT", 1, 1 },
+ { "bAAAAAA", "aZYXVUT", 4, 1 },
+ { "aa", "a", 0, 0 },
+ { "aa", "a", 1, 0 },
+ { "aa", "a", 4, 1 },
+ { "A", "a", 0, 0 },
+ { "A", "a", 1, -1 },
+ { "A", "a", 4, -1 },
+ { "baaaa", "aaaaa", 0, 0 },
+ { "baaaa", "aaaaa", 1, 1 },
+ { "baaaa", "aaaaa", 4, 1 },
+ { "abaaa", "aaaaa", 0, 0 },
+ { "abaaa", "aaaaa", 1, 0 },
+ { "abaaa", "aaaaa", 4, 1 },
+ { "aabaa", "aaaaa", 0, 0 },
+ { "aabaa", "aaaaa", 1, 0 },
+ { "aabaa", "aaaaa", 4, 1 },
+ { "aaaba", "aaaaa", 0, 0 },
+ { "aaaba", "aaaaa", 1, 0 },
+ { "aaaba", "aaaaa", 4, 1 },
+ { "aaaab", "aaaaa", 0, 0 },
+ { "aaaab", "aaaaa", 1, 0 },
+ { "aaaab", "aaaaa", 4, 0 },
+ { "word", "Word", 0, 0 },
+ { "word", "Word", 1, 1 },
+ { "word", "Word", 3, 1 },
+ { "word", "wOrd", 0, 0 },
+ { "word", "wOrd", 1, 0 },
+ { "word", "wOrd", 3, 1 },
+ { "word", "woRd", 0, 0 },
+ { "word", "woRd", 1, 0 },
+ { "word", "woRd", 3, 1 },
+ { "word", "worD", 0, 0 },
+ { "word", "worD", 1, 0 },
+ { "word", "worD", 3, 0 },
+ { "WORD", "wORD", 0, 0 },
+ { "WORD", "wORD", 1, -1 },
+ { "WORD", "wORD", 3, -1 },
+ { "WORD", "WoRD", 0, 0 },
+ { "WORD", "WoRD", 1, 0 },
+ { "WORD", "WoRD", 3, -1 },
+ { "WORD", "WOrD", 0, 0 },
+ { "WORD", "WOrD", 1, 0 },
+ { "WORD", "WOrD", 3, -1 },
+ { "WORD", "WORd", 0, 0 },
+ { "WORD", "WORd", 1, 0 },
+ { "WORD", "WORd", 3, 0 }
+
+ };
+
+ int i;
+
+ printf("Test 012 (PL_strncmp) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ PRIntn rv = PL_strncmp(array[i].one, array[i].two, array[i].max);
+
+ switch( array[i].sign )
+ {
+ case -1:
+ if( rv < 0 ) continue;
+ break;
+ case 1:
+ if( rv > 0 ) continue;
+ break;
+ case 0:
+ if( 0 == rv ) continue;
+ break;
+ default:
+ PR_NOT_REACHED("static data inconsistancy");
+ break;
+ }
+
+ printf("FAIL %d: %s-%s/%ld -> %d, not %d\n", i,
+ array[i].one ? array[i].one : "(null)",
+ array[i].two ? array[i].two : "(null)",
+ array[i].max, rv, array[i].sign);
+ return PR_FALSE;
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strcasecmp */
+PRBool test_013(void)
+{
+ static struct
+ {
+ const char *one;
+ const char *two;
+ PRIntn sign;
+ } array[] =
+ {
+ { (const char *)0, (const char *)0, 0 },
+ { (const char *)0, "word", -1 },
+ { "word", (const char *)0, 1 },
+ { "word", "word", 0 },
+ { "aZYXVUT", "bZYXVUT", -1 },
+ { "aZYXVUT", "bAAAAAA", -1 },
+ { "a", "aa", -1 },
+ { "a", "a", 0 },
+ { "a", "A", 0 },
+ { "aaaaa", "baaaa", -1 },
+ { "aaaaa", "abaaa", -1 },
+ { "aaaaa", "aabaa", -1 },
+ { "aaaaa", "aaaba", -1 },
+ { "aaaaa", "aaaab", -1 },
+ { "bZYXVUT", "aZYXVUT", 1 },
+ { "bAAAAAA", "aZYXVUT", 1 },
+ { "aa", "a", 1 },
+ { "A", "a", 0 },
+ { "baaaa", "aaaaa", 1 },
+ { "abaaa", "aaaaa", 1 },
+ { "aabaa", "aaaaa", 1 },
+ { "aaaba", "aaaaa", 1 },
+ { "aaaab", "aaaaa", 1 },
+ { "word", "Word", 0 },
+ { "word", "wOrd", 0 },
+ { "word", "woRd", 0 },
+ { "word", "worD", 0 },
+ { "WORD", "wORD", 0 },
+ { "WORD", "WoRD", 0 },
+ { "WORD", "WOrD", 0 },
+ { "WORD", "WORd", 0 }
+ };
+
+ int i;
+
+ printf("Test 013 (PL_strcasecmp) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ PRIntn rv = PL_strcasecmp(array[i].one, array[i].two);
+
+ switch( array[i].sign )
+ {
+ case -1:
+ if( rv < 0 ) continue;
+ break;
+ case 1:
+ if( rv > 0 ) continue;
+ break;
+ case 0:
+ if( 0 == rv ) continue;
+ break;
+ default:
+ PR_NOT_REACHED("static data inconsistancy");
+ break;
+ }
+
+ printf("FAIL %d: %s-%s -> %d, not %d\n", i,
+ array[i].one ? array[i].one : "(null)",
+ array[i].two ? array[i].two : "(null)",
+ rv, array[i].sign);
+ return PR_FALSE;
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strncasecmp */
+PRBool test_014(void)
+{
+ static struct
+ {
+ const char *one;
+ const char *two;
+ PRUint32 max;
+ PRIntn sign;
+ } array[] =
+ {
+ { (const char *)0, (const char *)0, 0, 0 },
+ { (const char *)0, (const char *)0, 1, 0 },
+ { (const char *)0, (const char *)0, 4, 0 },
+ { (const char *)0, "word", 0, -1 },
+ { (const char *)0, "word", 1, -1 },
+ { (const char *)0, "word", 4, -1 },
+ { "word", (const char *)0, 0, 1 },
+ { "word", (const char *)0, 1, 1 },
+ { "word", (const char *)0, 4, 1 },
+ { "word", "word", 0, 0 },
+ { "word", "word", 1, 0 },
+ { "word", "word", 3, 0 },
+ { "word", "word", 5, 0 },
+ { "aZYXVUT", "bZYXVUT", 0, 0 },
+ { "aZYXVUT", "bZYXVUT", 1, -1 },
+ { "aZYXVUT", "bZYXVUT", 4, -1 },
+ { "aZYXVUT", "bZYXVUT", 9, -1 },
+ { "aZYXVUT", "bAAAAAA", 0, 0 },
+ { "aZYXVUT", "bAAAAAA", 1, -1 },
+ { "aZYXVUT", "bAAAAAA", 4, -1 },
+ { "aZYXVUT", "bAAAAAA", 5, -1 },
+ { "a", "aa", 0, 0 },
+ { "a", "aa", 1, 0 },
+ { "a", "aa", 4, -1 },
+ { "a", "a", 0, 0 },
+ { "a", "a", 1, 0 },
+ { "a", "a", 4, 0 },
+ { "a", "A", 0, 0 },
+ { "a", "A", 1, 0 },
+ { "a", "A", 4, 0 },
+ { "aaaaa", "baaaa", 0, 0 },
+ { "aaaaa", "baaaa", 1, -1 },
+ { "aaaaa", "baaaa", 4, -1 },
+ { "aaaaa", "abaaa", 0, 0 },
+ { "aaaaa", "abaaa", 1, 0 },
+ { "aaaaa", "abaaa", 4, -1 },
+ { "aaaaa", "aabaa", 0, 0 },
+ { "aaaaa", "aabaa", 1, 0 },
+ { "aaaaa", "aabaa", 4, -1 },
+ { "aaaaa", "aaaba", 0, 0 },
+ { "aaaaa", "aaaba", 1, 0 },
+ { "aaaaa", "aaaba", 4, -1 },
+ { "aaaaa", "aaaab", 0, 0 },
+ { "aaaaa", "aaaab", 1, 0 },
+ { "aaaaa", "aaaab", 4, 0 },
+ { "bZYXVUT", "aZYXVUT", 0, 0 },
+ { "bZYXVUT", "aZYXVUT", 1, 1 },
+ { "bZYXVUT", "aZYXVUT", 4, 1 },
+ { "bAAAAAA", "aZYXVUT", 0, 0 },
+ { "bAAAAAA", "aZYXVUT", 1, 1 },
+ { "bAAAAAA", "aZYXVUT", 4, 1 },
+ { "aa", "a", 0, 0 },
+ { "aa", "a", 1, 0 },
+ { "aa", "a", 4, 1 },
+ { "A", "a", 0, 0 },
+ { "A", "a", 1, 0 },
+ { "A", "a", 4, 0 },
+ { "baaaa", "aaaaa", 0, 0 },
+ { "baaaa", "aaaaa", 1, 1 },
+ { "baaaa", "aaaaa", 4, 1 },
+ { "abaaa", "aaaaa", 0, 0 },
+ { "abaaa", "aaaaa", 1, 0 },
+ { "abaaa", "aaaaa", 4, 1 },
+ { "aabaa", "aaaaa", 0, 0 },
+ { "aabaa", "aaaaa", 1, 0 },
+ { "aabaa", "aaaaa", 4, 1 },
+ { "aaaba", "aaaaa", 0, 0 },
+ { "aaaba", "aaaaa", 1, 0 },
+ { "aaaba", "aaaaa", 4, 1 },
+ { "aaaab", "aaaaa", 0, 0 },
+ { "aaaab", "aaaaa", 1, 0 },
+ { "aaaab", "aaaaa", 4, 0 },
+ { "word", "Word", 0, 0 },
+ { "word", "Word", 1, 0 },
+ { "word", "Word", 3, 0 },
+ { "word", "wOrd", 0, 0 },
+ { "word", "wOrd", 1, 0 },
+ { "word", "wOrd", 3, 0 },
+ { "word", "woRd", 0, 0 },
+ { "word", "woRd", 1, 0 },
+ { "word", "woRd", 3, 0 },
+ { "word", "worD", 0, 0 },
+ { "word", "worD", 1, 0 },
+ { "word", "worD", 3, 0 },
+ { "WORD", "wORD", 0, 0 },
+ { "WORD", "wORD", 1, 0 },
+ { "WORD", "wORD", 3, 0 },
+ { "WORD", "WoRD", 0, 0 },
+ { "WORD", "WoRD", 1, 0 },
+ { "WORD", "WoRD", 3, 0 },
+ { "WORD", "WOrD", 0, 0 },
+ { "WORD", "WOrD", 1, 0 },
+ { "WORD", "WOrD", 3, 0 },
+ { "WORD", "WORd", 0, 0 },
+ { "WORD", "WORd", 1, 0 },
+ { "WORD", "WORd", 3, 0 }
+ };
+
+ int i;
+
+ printf("Test 014 (PL_strncasecmp) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ PRIntn rv = PL_strncasecmp(array[i].one, array[i].two, array[i].max);
+
+ switch( array[i].sign )
+ {
+ case -1:
+ if( rv < 0 ) continue;
+ break;
+ case 1:
+ if( rv > 0 ) continue;
+ break;
+ case 0:
+ if( 0 == rv ) continue;
+ break;
+ default:
+ PR_NOT_REACHED("static data inconsistancy");
+ break;
+ }
+
+ printf("FAIL %d: %s-%s/%ld -> %d, not %d\n", i,
+ array[i].one ? array[i].one : "(null)",
+ array[i].two ? array[i].two : "(null)",
+ array[i].max, rv, array[i].sign);
+ return PR_FALSE;
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strchr */
+PRBool test_015(void)
+{
+ static struct
+ {
+ const char *str;
+ char chr;
+ PRBool ret;
+ PRUint32 off;
+ } array[] =
+ {
+ { (const char *)0, 'a', PR_FALSE, 0 },
+ { (const char *)0, '\0', PR_FALSE, 0 },
+ { "abcdefg", 'a', PR_TRUE, 0 },
+ { "abcdefg", 'b', PR_TRUE, 1 },
+ { "abcdefg", 'c', PR_TRUE, 2 },
+ { "abcdefg", 'd', PR_TRUE, 3 },
+ { "abcdefg", 'e', PR_TRUE, 4 },
+ { "abcdefg", 'f', PR_TRUE, 5 },
+ { "abcdefg", 'g', PR_TRUE, 6 },
+ { "abcdefg", 'h', PR_FALSE, 0 },
+ { "abcdefg", '\0', PR_TRUE, 7 },
+ { "abcdefg", 'A', PR_FALSE, 0 },
+ { "abcdefg", 'B', PR_FALSE, 0 },
+ { "abcdefg", 'C', PR_FALSE, 0 },
+ { "abcdefg", 'D', PR_FALSE, 0 },
+ { "abcdefg", 'E', PR_FALSE, 0 },
+ { "abcdefg", 'F', PR_FALSE, 0 },
+ { "abcdefg", 'G', PR_FALSE, 0 },
+ { "abcdefg", 'H', PR_FALSE, 0 },
+ { "abcdefgabcdefg", 'a', PR_TRUE, 0 },
+ { "abcdefgabcdefg", 'b', PR_TRUE, 1 },
+ { "abcdefgabcdefg", 'c', PR_TRUE, 2 },
+ { "abcdefgabcdefg", 'd', PR_TRUE, 3 },
+ { "abcdefgabcdefg", 'e', PR_TRUE, 4 },
+ { "abcdefgabcdefg", 'f', PR_TRUE, 5 },
+ { "abcdefgabcdefg", 'g', PR_TRUE, 6 },
+ { "abcdefgabcdefg", 'h', PR_FALSE, 0 },
+ { "abcdefgabcdefg", '\0', PR_TRUE, 14 }
+ };
+
+ int i;
+
+ printf("Test 015 (PL_strchr) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char *rv = PL_strchr(array[i].str, array[i].chr);
+
+ if( PR_FALSE == array[i].ret )
+ {
+ if( (char *)0 != rv )
+ {
+ printf("FAIL %d: %s,%c -> %.32s, not zero\n", i, array[i].str,
+ array[i].chr, rv);
+ return PR_FALSE;
+ }
+ }
+ else
+ {
+ if( (char *)0 == rv )
+ {
+ printf("FAIL %d: %s,%c -> null, not +%lu\n", i, array[i].str,
+ array[i].chr, array[i].off);
+ return PR_FALSE;
+ }
+
+ if( &array[i].str[ array[i].off ] != rv )
+ {
+ printf("FAIL %d: %s,%c -> 0x%x, not 0x%x+%lu\n", i, array[i].str,
+ array[i].chr, rv, array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strrchr */
+PRBool test_016(void)
+{
+ static struct
+ {
+ const char *str;
+ char chr;
+ PRBool ret;
+ PRUint32 off;
+ } array[] =
+ {
+ { (const char *)0, 'a', PR_FALSE, 0 },
+ { (const char *)0, '\0', PR_FALSE, 0 },
+ { "abcdefg", 'a', PR_TRUE, 0 },
+ { "abcdefg", 'b', PR_TRUE, 1 },
+ { "abcdefg", 'c', PR_TRUE, 2 },
+ { "abcdefg", 'd', PR_TRUE, 3 },
+ { "abcdefg", 'e', PR_TRUE, 4 },
+ { "abcdefg", 'f', PR_TRUE, 5 },
+ { "abcdefg", 'g', PR_TRUE, 6 },
+ { "abcdefg", 'h', PR_FALSE, 0 },
+ { "abcdefg", '\0', PR_TRUE, 7 },
+ { "abcdefg", 'A', PR_FALSE, 0 },
+ { "abcdefg", 'B', PR_FALSE, 0 },
+ { "abcdefg", 'C', PR_FALSE, 0 },
+ { "abcdefg", 'D', PR_FALSE, 0 },
+ { "abcdefg", 'E', PR_FALSE, 0 },
+ { "abcdefg", 'F', PR_FALSE, 0 },
+ { "abcdefg", 'G', PR_FALSE, 0 },
+ { "abcdefg", 'H', PR_FALSE, 0 },
+ { "abcdefgabcdefg", 'a', PR_TRUE, 7 },
+ { "abcdefgabcdefg", 'b', PR_TRUE, 8 },
+ { "abcdefgabcdefg", 'c', PR_TRUE, 9 },
+ { "abcdefgabcdefg", 'd', PR_TRUE, 10 },
+ { "abcdefgabcdefg", 'e', PR_TRUE, 11 },
+ { "abcdefgabcdefg", 'f', PR_TRUE, 12 },
+ { "abcdefgabcdefg", 'g', PR_TRUE, 13 },
+ { "abcdefgabcdefg", 'h', PR_FALSE, 0 },
+ { "abcdefgabcdefg", '\0', PR_TRUE, 14 }
+ };
+
+ int i;
+
+ printf("Test 016 (PL_strrchr) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char *rv = PL_strrchr(array[i].str, array[i].chr);
+
+ if( PR_FALSE == array[i].ret )
+ {
+ if( (char *)0 != rv )
+ {
+ printf("FAIL %d: %s,%c -> %.32s, not zero\n", i, array[i].str,
+ array[i].chr, rv);
+ return PR_FALSE;
+ }
+ }
+ else
+ {
+ if( (char *)0 == rv )
+ {
+ printf("FAIL %d: %s,%c -> null, not +%lu\n", i, array[i].str,
+ array[i].chr, array[i].off);
+ return PR_FALSE;
+ }
+
+ if( &array[i].str[ array[i].off ] != rv )
+ {
+ printf("FAIL %d: %s,%c -> 0x%x, not 0x%x+%lu\n", i, array[i].str,
+ array[i].chr, rv, array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strnchr */
+PRBool test_017(void)
+{
+ static struct
+ {
+ const char *str;
+ char chr;
+ PRUint32 max;
+ PRBool ret;
+ PRUint32 off;
+ } array[] =
+ {
+ { (const char *)0, 'a', 2, PR_FALSE, 0 },
+ { (const char *)0, '\0', 2, PR_FALSE, 0 },
+ { "abcdefg", 'a', 5, PR_TRUE, 0 },
+ { "abcdefg", 'b', 5, PR_TRUE, 1 },
+ { "abcdefg", 'c', 5, PR_TRUE, 2 },
+ { "abcdefg", 'd', 5, PR_TRUE, 3 },
+ { "abcdefg", 'e', 5, PR_TRUE, 4 },
+ { "abcdefg", 'f', 5, PR_FALSE, 0 },
+ { "abcdefg", 'g', 5, PR_FALSE, 0 },
+ { "abcdefg", 'h', 5, PR_FALSE, 0 },
+ { "abcdefg", '\0', 5, PR_FALSE, 0 },
+ { "abcdefg", '\0', 15, PR_TRUE, 7 },
+ { "abcdefg", 'A', 5, PR_FALSE, 0 },
+ { "abcdefg", 'B', 5, PR_FALSE, 0 },
+ { "abcdefg", 'C', 5, PR_FALSE, 0 },
+ { "abcdefg", 'D', 5, PR_FALSE, 0 },
+ { "abcdefg", 'E', 5, PR_FALSE, 0 },
+ { "abcdefg", 'F', 5, PR_FALSE, 0 },
+ { "abcdefg", 'G', 5, PR_FALSE, 0 },
+ { "abcdefg", 'H', 5, PR_FALSE, 0 },
+ { "abcdefgabcdefg", 'a', 10, PR_TRUE, 0 },
+ { "abcdefgabcdefg", 'b', 10, PR_TRUE, 1 },
+ { "abcdefgabcdefg", 'c', 10, PR_TRUE, 2 },
+ { "abcdefgabcdefg", 'd', 10, PR_TRUE, 3 },
+ { "abcdefgabcdefg", 'e', 10, PR_TRUE, 4 },
+ { "abcdefgabcdefg", 'f', 10, PR_TRUE, 5 },
+ { "abcdefgabcdefg", 'g', 10, PR_TRUE, 6 },
+ { "abcdefgabcdefg", 'h', 10, PR_FALSE, 0 },
+ { "abcdefgabcdefg", '\0', 10, PR_FALSE, 0 },
+ { "abcdefgabcdefg", '\0', 14, PR_FALSE, 0 },
+ { "abcdefgabcdefg", '\0', 15, PR_TRUE, 14 }
+ };
+
+ int i;
+
+ printf("Test 017 (PL_strnchr) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char *rv = PL_strnchr(array[i].str, array[i].chr, array[i].max);
+
+ if( PR_FALSE == array[i].ret )
+ {
+ if( (char *)0 != rv )
+ {
+ printf("FAIL %d: %s,%c/%lu -> %.32s, not zero\n", i, array[i].str,
+ array[i].chr, array[i].max, rv);
+ return PR_FALSE;
+ }
+ }
+ else
+ {
+ if( (char *)0 == rv )
+ {
+ printf("FAIL %d: %s,%c/%lu -> null, not +%lu\n", i, array[i].str,
+ array[i].chr, array[i].max, array[i].off);
+ return PR_FALSE;
+ }
+
+ if( &array[i].str[ array[i].off ] != rv )
+ {
+ printf("FAIL %d: %s,%c/%lu -> 0x%x, not 0x%x+%lu\n", i, array[i].str,
+ array[i].chr, array[i].max, rv, array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strnrchr */
+PRBool test_018(void)
+{
+ static struct
+ {
+ const char *str;
+ char chr;
+ PRUint32 max;
+ PRBool ret;
+ PRUint32 off;
+ } array[] =
+ {
+ { (const char *)0, 'a', 2, PR_FALSE, 0 },
+ { (const char *)0, '\0', 2, PR_FALSE, 0 },
+ { "abcdefg", 'a', 5, PR_TRUE, 0 },
+ { "abcdefg", 'b', 5, PR_TRUE, 1 },
+ { "abcdefg", 'c', 5, PR_TRUE, 2 },
+ { "abcdefg", 'd', 5, PR_TRUE, 3 },
+ { "abcdefg", 'e', 5, PR_TRUE, 4 },
+ { "abcdefg", 'f', 5, PR_FALSE, 0 },
+ { "abcdefg", 'g', 5, PR_FALSE, 0 },
+ { "abcdefg", 'h', 5, PR_FALSE, 0 },
+ { "abcdefg", '\0', 5, PR_FALSE, 0 },
+ { "abcdefg", '\0', 15, PR_TRUE, 7 },
+ { "abcdefg", 'A', 5, PR_FALSE, 0 },
+ { "abcdefg", 'B', 5, PR_FALSE, 0 },
+ { "abcdefg", 'C', 5, PR_FALSE, 0 },
+ { "abcdefg", 'D', 5, PR_FALSE, 0 },
+ { "abcdefg", 'E', 5, PR_FALSE, 0 },
+ { "abcdefg", 'F', 5, PR_FALSE, 0 },
+ { "abcdefg", 'G', 5, PR_FALSE, 0 },
+ { "abcdefg", 'H', 5, PR_FALSE, 0 },
+ { "abcdefgabcdefg", 'a', 10, PR_TRUE, 7 },
+ { "abcdefgabcdefg", 'b', 10, PR_TRUE, 8 },
+ { "abcdefgabcdefg", 'c', 10, PR_TRUE, 9 },
+ { "abcdefgabcdefg", 'd', 10, PR_TRUE, 3 },
+ { "abcdefgabcdefg", 'e', 10, PR_TRUE, 4 },
+ { "abcdefgabcdefg", 'f', 10, PR_TRUE, 5 },
+ { "abcdefgabcdefg", 'g', 10, PR_TRUE, 6 },
+ { "abcdefgabcdefg", 'h', 10, PR_FALSE, 0 },
+ { "abcdefgabcdefg", '\0', 10, PR_FALSE, 0 },
+ { "abcdefgabcdefg", '\0', 14, PR_FALSE, 0 },
+ { "abcdefgabcdefg", '\0', 15, PR_TRUE, 14 }
+ };
+
+ int i;
+
+ printf("Test 018 (PL_strnrchr) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char *rv = PL_strnrchr(array[i].str, array[i].chr, array[i].max);
+
+ if( PR_FALSE == array[i].ret )
+ {
+ if( (char *)0 != rv )
+ {
+ printf("FAIL %d: %s,%c/%lu -> %.32s, not zero\n", i, array[i].str,
+ array[i].chr, array[i].max, rv);
+ return PR_FALSE;
+ }
+ }
+ else
+ {
+ if( (char *)0 == rv )
+ {
+ printf("FAIL %d: %s,%c/%lu -> null, not +%lu\n", i, array[i].str,
+ array[i].chr, array[i].max, array[i].off);
+ return PR_FALSE;
+ }
+
+ if( &array[i].str[ array[i].off ] != rv )
+ {
+ printf("FAIL %d: %s,%c/%lu -> 0x%x, not 0x%x+%lu\n", i, array[i].str,
+ array[i].chr, array[i].max, rv, array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strpbrk */
+PRBool test_019(void)
+{
+ static struct
+ {
+ const char *str;
+ const char *chrs;
+ PRBool ret;
+ PRUint32 off;
+ } array[] =
+ {
+ { (const char *)0, (const char *)0, PR_FALSE, 0 },
+ { (const char *)0, "abc", PR_FALSE, 0 },
+ { "abc", (const char *)0, PR_FALSE, 0 },
+ { "abcdefg", "", PR_FALSE, 0 },
+ { "", "aeiou", PR_FALSE, 0 },
+ { "abcdefg", "ae", PR_TRUE, 0 },
+ { "abcdefg", "ei", PR_TRUE, 4 },
+ { "abcdefg", "io", PR_FALSE, 0 },
+ { "abcdefg", "bcd", PR_TRUE, 1 },
+ { "abcdefg", "cbd", PR_TRUE, 1 },
+ { "abcdefg", "dbc", PR_TRUE, 1 },
+ { "abcdefg", "ghi", PR_TRUE, 6 },
+ { "abcdefg", "AE", PR_FALSE, 0 },
+ { "abcdefg", "EI", PR_FALSE, 0 },
+ { "abcdefg", "IO", PR_FALSE, 0 },
+ { "abcdefg", "BCD", PR_FALSE, 0 },
+ { "abcdefg", "CBD", PR_FALSE, 0 },
+ { "abcdefg", "DBC", PR_FALSE, 0 },
+ { "abcdefg", "GHI", PR_FALSE, 0 },
+ { "abcdefgabcdefg", "ae", PR_TRUE, 0 },
+ { "abcdefgabcdefg", "ei", PR_TRUE, 4 },
+ { "abcdefgabcdefg", "io", PR_FALSE, 0 },
+ { "abcdefgabcdefg", "bcd", PR_TRUE, 1 },
+ { "abcdefgabcdefg", "cbd", PR_TRUE, 1 },
+ { "abcdefgabcdefg", "dbc", PR_TRUE, 1 },
+ { "abcdefgabcdefg", "ghi", PR_TRUE, 6 },
+ { "abcdefgabcdefg", "AE", PR_FALSE, 0 },
+ { "abcdefgabcdefg", "EI", PR_FALSE, 0 },
+ { "abcdefgabcdefg", "IO", PR_FALSE, 0 },
+ { "abcdefgabcdefg", "BCD", PR_FALSE, 0 },
+ { "abcdefgabcdefg", "CBD", PR_FALSE, 0 },
+ { "abcdefgabcdefg", "DBC", PR_FALSE, 0 },
+ { "abcdefgabcdefg", "GHI", PR_FALSE, 0 }
+ };
+
+ int i;
+
+ printf("Test 019 (PL_strpbrk) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char *rv = PL_strpbrk(array[i].str, array[i].chrs);
+
+ if( PR_FALSE == array[i].ret )
+ {
+ if( (char *)0 != rv )
+ {
+ printf("FAIL %d: %s,%s -> %.32s, not null\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].chrs ? array[i].chrs : "(null)",
+ rv);
+ return PR_FALSE;
+ }
+ }
+ else
+ {
+ if( (char *)0 == rv )
+ {
+ printf("FAIL %d: %s,%s -> null, not +%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].chrs ? array[i].chrs : "(null)",
+ array[i].off);
+ return PR_FALSE;
+ }
+
+ if( &array[i].str[ array[i].off ] != rv )
+ {
+ printf("FAIL %d: %s,%s -> 0x%x, not 0x%x+%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].chrs ? array[i].chrs : "(null)",
+ rv, array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strprbrk */
+PRBool test_020(void)
+{
+ static struct
+ {
+ const char *str;
+ const char *chrs;
+ PRBool ret;
+ PRUint32 off;
+ } array[] =
+ {
+ { (const char *)0, (const char *)0, PR_FALSE, 0 },
+ { (const char *)0, "abc", PR_FALSE, 0 },
+ { "abc", (const char *)0, PR_FALSE, 0 },
+ { "abcdefg", "", PR_FALSE, 0 },
+ { "", "aeiou", PR_FALSE, 0 },
+ { "abcdefg", "ae", PR_TRUE, 4 },
+ { "abcdefg", "ei", PR_TRUE, 4 },
+ { "abcdefg", "io", PR_FALSE, 0 },
+ { "abcdefg", "bcd", PR_TRUE, 3 },
+ { "abcdefg", "cbd", PR_TRUE, 3 },
+ { "abcdefg", "dbc", PR_TRUE, 3 },
+ { "abcdefg", "ghi", PR_TRUE, 6 },
+ { "abcdefg", "AE", PR_FALSE, 0 },
+ { "abcdefg", "EI", PR_FALSE, 0 },
+ { "abcdefg", "IO", PR_FALSE, 0 },
+ { "abcdefg", "BCD", PR_FALSE, 0 },
+ { "abcdefg", "CBD", PR_FALSE, 0 },
+ { "abcdefg", "DBC", PR_FALSE, 0 },
+ { "abcdefg", "GHI", PR_FALSE, 0 },
+ { "abcdefgabcdefg", "ae", PR_TRUE, 11 },
+ { "abcdefgabcdefg", "ei", PR_TRUE, 11 },
+ { "abcdefgabcdefg", "io", PR_FALSE, 0 },
+ { "abcdefgabcdefg", "bcd", PR_TRUE, 10 },
+ { "abcdefgabcdefg", "cbd", PR_TRUE, 10 },
+ { "abcdefgabcdefg", "dbc", PR_TRUE, 10 },
+ { "abcdefgabcdefg", "ghi", PR_TRUE, 13 },
+ { "abcdefgabcdefg", "AE", PR_FALSE, 0 },
+ { "abcdefgabcdefg", "EI", PR_FALSE, 0 },
+ { "abcdefgabcdefg", "IO", PR_FALSE, 0 },
+ { "abcdefgabcdefg", "BCD", PR_FALSE, 0 },
+ { "abcdefgabcdefg", "CBD", PR_FALSE, 0 },
+ { "abcdefgabcdefg", "DBC", PR_FALSE, 0 },
+ { "abcdefgabcdefg", "GHI", PR_FALSE, 0 }
+ };
+
+ int i;
+
+ printf("Test 020 (PL_strprbrk) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char *rv = PL_strprbrk(array[i].str, array[i].chrs);
+
+ if( PR_FALSE == array[i].ret )
+ {
+ if( (char *)0 != rv )
+ {
+ printf("FAIL %d: %s,%s -> %.32s, not null\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].chrs ? array[i].chrs : "(null)",
+ rv);
+ return PR_FALSE;
+ }
+ }
+ else
+ {
+ if( (char *)0 == rv )
+ {
+ printf("FAIL %d: %s,%s -> null, not +%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].chrs ? array[i].chrs : "(null)",
+ array[i].off);
+ return PR_FALSE;
+ }
+
+ if( &array[i].str[ array[i].off ] != rv )
+ {
+ printf("FAIL %d: %s,%s -> 0x%x, not 0x%x+%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].chrs ? array[i].chrs : "(null)",
+ rv, array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strnpbrk */
+PRBool test_021(void)
+{
+ static struct
+ {
+ const char *str;
+ const char *chrs;
+ PRUint32 max;
+ PRBool ret;
+ PRUint32 off;
+ } array[] =
+ {
+ { (const char *)0, (const char *)0, 3, PR_FALSE, 0 },
+ { (const char *)0, "abc", 3, PR_FALSE, 0 },
+ { "abc", (const char *)0, 3, PR_FALSE, 0 },
+ { "abcdefg", "", 3, PR_FALSE, 0 },
+ { "", "aeiou", 3, PR_FALSE, 0 },
+ { "abcdefg", "ae", 0, PR_FALSE, 0 },
+ { "abcdefg", "ae", 1, PR_TRUE, 0 },
+ { "abcdefg", "ae", 4, PR_TRUE, 0 },
+ { "abcdefg", "ae", 5, PR_TRUE, 0 },
+ { "abcdefg", "ae", 6, PR_TRUE, 0 },
+ { "abcdefg", "ei", 4, PR_FALSE, 0 },
+ { "abcdefg", "io", 10, PR_FALSE, 0 },
+ { "abcdefg", "bcd", 2, PR_TRUE, 1 },
+ { "abcdefg", "cbd", 2, PR_TRUE, 1 },
+ { "abcdefg", "dbc", 2, PR_TRUE, 1 },
+ { "abcdefg", "ghi", 6, PR_FALSE, 0 },
+ { "abcdefg", "ghi", 7, PR_TRUE, 6 },
+ { "abcdefg", "AE", 9, PR_FALSE, 0 },
+ { "abcdefg", "EI", 9, PR_FALSE, 0 },
+ { "abcdefg", "IO", 9, PR_FALSE, 0 },
+ { "abcdefg", "BCD", 9, PR_FALSE, 0 },
+ { "abcdefg", "CBD", 9, PR_FALSE, 0 },
+ { "abcdefg", "DBC", 9, PR_FALSE, 0 },
+ { "abcdefg", "GHI", 9, PR_FALSE, 0 },
+ { "abcdefgabcdefg", "ae", 10, PR_TRUE, 0 },
+ { "abcdefgabcdefg", "ei", 10, PR_TRUE, 4 },
+ { "abcdefgabcdefg", "io", 10, PR_FALSE, 0 },
+ { "abcdefgabcdefg", "bcd", 10, PR_TRUE, 1 },
+ { "abcdefgabcdefg", "cbd", 10, PR_TRUE, 1 },
+ { "abcdefgabcdefg", "dbc", 10, PR_TRUE, 1 },
+ { "abcdefgabcdefg", "ghi", 10, PR_TRUE, 6 },
+ { "abcdefgabcdefg", "AE", 10, PR_FALSE, 0 },
+ { "abcdefgabcdefg", "EI", 10, PR_FALSE, 0 },
+ { "abcdefgabcdefg", "IO", 10, PR_FALSE, 0 },
+ { "abcdefgabcdefg", "BCD", 10, PR_FALSE, 0 },
+ { "abcdefgabcdefg", "CBD", 10, PR_FALSE, 0 },
+ { "abcdefgabcdefg", "DBC", 10, PR_FALSE, 0 },
+ { "abcdefgabcdefg", "GHI", 10, PR_FALSE, 0 }
+ };
+
+ int i;
+
+ printf("Test 021 (PL_strnpbrk) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char *rv = PL_strnpbrk(array[i].str, array[i].chrs, array[i].max);
+
+ if( PR_FALSE == array[i].ret )
+ {
+ if( (char *)0 != rv )
+ {
+ printf("FAIL %d: %s,%s/%lu -> %.32s, not null\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].chrs ? array[i].chrs : "(null)",
+ array[i].max, rv);
+ return PR_FALSE;
+ }
+ }
+ else
+ {
+ if( (char *)0 == rv )
+ {
+ printf("FAIL %d: %s,%s/%lu -> null, not +%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].chrs ? array[i].chrs : "(null)",
+ array[i].max, array[i].off);
+ return PR_FALSE;
+ }
+
+ if( &array[i].str[ array[i].off ] != rv )
+ {
+ printf("FAIL %d: %s,%s/%lu -> 0x%x, not 0x%x+%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].chrs ? array[i].chrs : "(null)",
+ array[i].max, rv, array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strnprbrk */
+PRBool test_022(void)
+{
+ static struct
+ {
+ const char *str;
+ const char *chrs;
+ PRUint32 max;
+ PRBool ret;
+ PRUint32 off;
+ } array[] =
+ {
+ { (const char *)0, (const char *)0, 3, PR_FALSE, 0 },
+ { (const char *)0, "abc", 3, PR_FALSE, 0 },
+ { "abc", (const char *)0, 3, PR_FALSE, 0 },
+ { "abcdefg", "", 3, PR_FALSE, 0 },
+ { "", "aeiou", 3, PR_FALSE, 0 },
+ { "abcdefg", "ae", 0, PR_FALSE, 0 },
+ { "abcdefg", "ae", 1, PR_TRUE, 0 },
+ { "abcdefg", "ae", 4, PR_TRUE, 0 },
+ { "abcdefg", "ae", 5, PR_TRUE, 4 },
+ { "abcdefg", "ae", 6, PR_TRUE, 4 },
+ { "abcdefg", "ei", 4, PR_FALSE, 0 },
+ { "abcdefg", "io", 10, PR_FALSE, 0 },
+ { "abcdefg", "bcd", 2, PR_TRUE, 1 },
+ { "abcdefg", "cbd", 2, PR_TRUE, 1 },
+ { "abcdefg", "dbc", 2, PR_TRUE, 1 },
+ { "abcdefg", "bcd", 3, PR_TRUE, 2 },
+ { "abcdefg", "cbd", 3, PR_TRUE, 2 },
+ { "abcdefg", "dbc", 3, PR_TRUE, 2 },
+ { "abcdefg", "bcd", 5, PR_TRUE, 3 },
+ { "abcdefg", "cbd", 5, PR_TRUE, 3 },
+ { "abcdefg", "dbc", 5, PR_TRUE, 3 },
+ { "abcdefg", "bcd", 15, PR_TRUE, 3 },
+ { "abcdefg", "cbd", 15, PR_TRUE, 3 },
+ { "abcdefg", "dbc", 15, PR_TRUE, 3 },
+ { "abcdefg", "ghi", 6, PR_FALSE, 0 },
+ { "abcdefg", "ghi", 7, PR_TRUE, 6 },
+ { "abcdefg", "AE", 9, PR_FALSE, 0 },
+ { "abcdefg", "EI", 9, PR_FALSE, 0 },
+ { "abcdefg", "IO", 9, PR_FALSE, 0 },
+ { "abcdefg", "BCD", 9, PR_FALSE, 0 },
+ { "abcdefg", "CBD", 9, PR_FALSE, 0 },
+ { "abcdefg", "DBC", 9, PR_FALSE, 0 },
+ { "abcdefg", "GHI", 9, PR_FALSE, 0 },
+ { "abcdefgabcdefg", "ae", 10, PR_TRUE, 7 },
+ { "abcdefgabcdefg", "ei", 10, PR_TRUE, 4 },
+ { "abcdefgabcdefg", "io", 10, PR_FALSE, 0 },
+ { "abcdefgabcdefg", "bcd", 10, PR_TRUE, 9 },
+ { "abcdefgabcdefg", "cbd", 10, PR_TRUE, 9 },
+ { "abcdefgabcdefg", "dbc", 10, PR_TRUE, 9 },
+ { "abcdefgabcdefg", "ghi", 10, PR_TRUE, 6 },
+ { "abcdefgabcdefg", "AE", 10, PR_FALSE, 0 },
+ { "abcdefgabcdefg", "EI", 10, PR_FALSE, 0 },
+ { "abcdefgabcdefg", "IO", 10, PR_FALSE, 0 },
+ { "abcdefgabcdefg", "BCD", 10, PR_FALSE, 0 },
+ { "abcdefgabcdefg", "CBD", 10, PR_FALSE, 0 },
+ { "abcdefgabcdefg", "DBC", 10, PR_FALSE, 0 },
+ { "abcdefgabcdefg", "GHI", 10, PR_FALSE, 0 }
+ };
+
+ int i;
+
+ printf("Test 022 (PL_strnprbrk) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char *rv = PL_strnprbrk(array[i].str, array[i].chrs, array[i].max);
+
+ if( PR_FALSE == array[i].ret )
+ {
+ if( (char *)0 != rv )
+ {
+ printf("FAIL %d: %s,%s/%lu -> %.32s, not null\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].chrs ? array[i].chrs : "(null)",
+ array[i].max, rv);
+ return PR_FALSE;
+ }
+ }
+ else
+ {
+ if( (char *)0 == rv )
+ {
+ printf("FAIL %d: %s,%s/%lu -> null, not +%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].chrs ? array[i].chrs : "(null)",
+ array[i].max, array[i].off);
+ return PR_FALSE;
+ }
+
+ if( &array[i].str[ array[i].off ] != rv )
+ {
+ printf("FAIL %d: %s,%s/%lu -> 0x%x, not 0x%x+%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].chrs ? array[i].chrs : "(null)",
+ array[i].max, rv, array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strstr */
+PRBool test_023(void)
+{
+ static struct
+ {
+ const char *str;
+ const char *sub;
+ PRBool ret;
+ PRUint32 off;
+ } array[] =
+ {
+ { (const char *)0, (const char *)0, PR_FALSE, 0 },
+ { (const char *)0, "blah", PR_FALSE, 0 },
+ { "blah-de-blah", (const char *)0, PR_FALSE, 0 },
+ { "blah-de-blah", "blah", PR_TRUE, 0 },
+ { "", "blah", PR_FALSE, 0 },
+ { "blah-de-blah", "", PR_FALSE, 0 },
+ { "abcdefg", "a", PR_TRUE, 0 },
+ { "abcdefg", "c", PR_TRUE, 2 },
+ { "abcdefg", "e", PR_TRUE, 4 },
+ { "abcdefg", "g", PR_TRUE, 6 },
+ { "abcdefg", "i", PR_FALSE, 0 },
+ { "abcdefg", "ab", PR_TRUE, 0 },
+ { "abcdefg", "cd", PR_TRUE, 2 },
+ { "abcdefg", "ef", PR_TRUE, 4 },
+ { "abcdefg", "gh", PR_FALSE, 0 },
+ { "abcdabc", "bc", PR_TRUE, 1 },
+ { "abcdefg", "abcdefg", PR_TRUE, 0 },
+ { "abcdefgabcdefg", "a", PR_TRUE, 0 },
+ { "abcdefgabcdefg", "c", PR_TRUE, 2 },
+ { "abcdefgabcdefg", "e", PR_TRUE, 4 },
+ { "abcdefgabcdefg", "g", PR_TRUE, 6 },
+ { "abcdefgabcdefg", "i", PR_FALSE, 0 },
+ { "abcdefgabcdefg", "ab", PR_TRUE, 0 },
+ { "abcdefgabcdefg", "cd", PR_TRUE, 2 },
+ { "abcdefgabcdefg", "ef", PR_TRUE, 4 },
+ { "abcdefgabcdefg", "gh", PR_FALSE, 0 },
+ { "abcdabcabcdabc", "bc", PR_TRUE, 1 },
+ { "abcdefgabcdefg", "abcdefg", PR_TRUE, 0 },
+ { "ABCDEFG", "a", PR_FALSE, 0 },
+ { "ABCDEFG", "c", PR_FALSE, 0 },
+ { "ABCDEFG", "e", PR_FALSE, 0 },
+ { "ABCDEFG", "g", PR_FALSE, 0 },
+ { "ABCDEFG", "i", PR_FALSE, 0 },
+ { "ABCDEFG", "ab", PR_FALSE, 0 },
+ { "ABCDEFG", "cd", PR_FALSE, 0 },
+ { "ABCDEFG", "ef", PR_FALSE, 0 },
+ { "ABCDEFG", "gh", PR_FALSE, 0 },
+ { "ABCDABC", "bc", PR_FALSE, 0 },
+ { "ABCDEFG", "abcdefg", PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "a", PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "c", PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "e", PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "g", PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "i", PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "ab", PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "cd", PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "ef", PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "gh", PR_FALSE, 0 },
+ { "ABCDABCABCDABC", "bc", PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "abcdefg", PR_FALSE, 0 }
+ };
+
+ int i;
+
+ printf("Test 023 (PL_strstr) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char *rv = PL_strstr(array[i].str, array[i].sub);
+
+ if( PR_FALSE == array[i].ret )
+ {
+ if( (char *)0 != rv )
+ {
+ printf("FAIL %d: %s,%s -> %.32s, not null\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ rv);
+ return PR_FALSE;
+ }
+ }
+ else
+ {
+ if( (char *)0 == rv )
+ {
+ printf("FAIL %d: %s,%s -> null, not 0x%x+%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+
+ if( &array[i].str[ array[i].off ] != rv )
+ {
+ printf("FAIL %d: %s,%s -> 0x%x, not 0x%x+%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ rv, array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strrstr */
+PRBool test_024(void)
+{
+ static struct
+ {
+ const char *str;
+ const char *sub;
+ PRBool ret;
+ PRUint32 off;
+ } array[] =
+ {
+ { (const char *)0, (const char *)0, PR_FALSE, 0 },
+ { (const char *)0, "blah", PR_FALSE, 0 },
+ { "blah-de-blah", (const char *)0, PR_FALSE, 0 },
+ { "blah-de-blah", "blah", PR_TRUE, 8 },
+ { "", "blah", PR_FALSE, 0 },
+ { "blah-de-blah", "", PR_FALSE, 0 },
+ { "abcdefg", "a", PR_TRUE, 0 },
+ { "abcdefg", "c", PR_TRUE, 2 },
+ { "abcdefg", "e", PR_TRUE, 4 },
+ { "abcdefg", "g", PR_TRUE, 6 },
+ { "abcdefg", "i", PR_FALSE, 0 },
+ { "abcdefg", "ab", PR_TRUE, 0 },
+ { "abcdefg", "cd", PR_TRUE, 2 },
+ { "abcdefg", "ef", PR_TRUE, 4 },
+ { "abcdefg", "gh", PR_FALSE, 0 },
+ { "abcdabc", "bc", PR_TRUE, 5 },
+ { "abcdefg", "abcdefg", PR_TRUE, 0 },
+ { "abcdefgabcdefg", "a", PR_TRUE, 7 },
+ { "abcdefgabcdefg", "c", PR_TRUE, 9 },
+ { "abcdefgabcdefg", "e", PR_TRUE, 11 },
+ { "abcdefgabcdefg", "g", PR_TRUE, 13 },
+ { "abcdefgabcdefg", "i", PR_FALSE, 0 },
+ { "abcdefgabcdefg", "ab", PR_TRUE, 7 },
+ { "abcdefgabcdefg", "cd", PR_TRUE, 9 },
+ { "abcdefgabcdefg", "ef", PR_TRUE, 11 },
+ { "abcdefgabcdefg", "gh", PR_FALSE, 0 },
+ { "abcdabcabcdabc", "bc", PR_TRUE, 12 },
+ { "abcdefgabcdefg", "abcdefg", PR_TRUE, 7 },
+ { "ABCDEFG", "a", PR_FALSE, 0 },
+ { "ABCDEFG", "c", PR_FALSE, 0 },
+ { "ABCDEFG", "e", PR_FALSE, 0 },
+ { "ABCDEFG", "g", PR_FALSE, 0 },
+ { "ABCDEFG", "i", PR_FALSE, 0 },
+ { "ABCDEFG", "ab", PR_FALSE, 0 },
+ { "ABCDEFG", "cd", PR_FALSE, 0 },
+ { "ABCDEFG", "ef", PR_FALSE, 0 },
+ { "ABCDEFG", "gh", PR_FALSE, 0 },
+ { "ABCDABC", "bc", PR_FALSE, 0 },
+ { "ABCDEFG", "abcdefg", PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "a", PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "c", PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "e", PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "g", PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "i", PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "ab", PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "cd", PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "ef", PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "gh", PR_FALSE, 0 },
+ { "ABCDABCABCDABC", "bc", PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "abcdefg", PR_FALSE, 0 }
+ };
+
+ int i;
+
+ printf("Test 024 (PL_strrstr) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char *rv = PL_strrstr(array[i].str, array[i].sub);
+
+ if( PR_FALSE == array[i].ret )
+ {
+ if( (char *)0 != rv )
+ {
+ printf("FAIL %d: %s,%s -> %.32s, not null\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ rv);
+ return PR_FALSE;
+ }
+ }
+ else
+ {
+ if( (char *)0 == rv )
+ {
+ printf("FAIL %d: %s,%s -> null, not 0x%x+%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+
+ if( &array[i].str[ array[i].off ] != rv )
+ {
+ printf("FAIL %d: %s,%s -> 0x%x, not 0x%x+%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ rv, array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strnstr */
+PRBool test_025(void)
+{
+ static struct
+ {
+ const char *str;
+ const char *sub;
+ PRUint32 max;
+ PRBool ret;
+ PRUint32 off;
+ } array[] =
+ {
+ { (const char *)0, (const char *)0, 12, PR_FALSE, 0 },
+ { (const char *)0, "blah", 12, PR_FALSE, 0 },
+ { "blah-de-blah", (const char *)0, 12, PR_FALSE, 0 },
+ { "blah-de-blah", "blah", 0, PR_FALSE, 0 },
+ { "blah-de-blah", "blah", 2, PR_FALSE, 0 },
+ { "blah-de-blah", "blah", 3, PR_FALSE, 0 },
+ { "blah-de-blah", "blah", 4, PR_TRUE, 0 },
+ { "blah-de-blah", "blah", 5, PR_TRUE, 0 },
+ { "blah-de-blah", "blah", 12, PR_TRUE, 0 },
+ { "", "blah", 12, PR_FALSE, 0 },
+ { "blah-de-blah", "", 12, PR_FALSE, 0 },
+ { "abcdefg", "a", 5, PR_TRUE, 0 },
+ { "abcdefg", "c", 5, PR_TRUE, 2 },
+ { "abcdefg", "e", 5, PR_TRUE, 4 },
+ { "abcdefg", "g", 5, PR_FALSE, 0 },
+ { "abcdefg", "i", 5, PR_FALSE, 0 },
+ { "abcdefg", "ab", 5, PR_TRUE, 0 },
+ { "abcdefg", "cd", 5, PR_TRUE, 2 },
+ { "abcdefg", "ef", 5, PR_FALSE, 0 },
+ { "abcdefg", "gh", 5, PR_FALSE, 0 },
+ { "abcdabc", "bc", 5, PR_TRUE, 1 },
+ { "abcdabc", "bc", 6, PR_TRUE, 1 },
+ { "abcdabc", "bc", 7, PR_TRUE, 1 },
+ { "abcdefg", "abcdefg", 6, PR_FALSE, 0 },
+ { "abcdefg", "abcdefg", 7, PR_TRUE, 0 },
+ { "abcdefg", "abcdefg", 8, PR_TRUE, 0 },
+ { "abcdefgabcdefg", "a", 12, PR_TRUE, 0 },
+ { "abcdefgabcdefg", "c", 12, PR_TRUE, 2 },
+ { "abcdefgabcdefg", "e", 12, PR_TRUE, 4 },
+ { "abcdefgabcdefg", "g", 12, PR_TRUE, 6 },
+ { "abcdefgabcdefg", "i", 12, PR_FALSE, 0 },
+ { "abcdefgabcdefg", "ab", 12, PR_TRUE, 0 },
+ { "abcdefgabcdefg", "cd", 12, PR_TRUE, 2 },
+ { "abcdefgabcdefg", "ef", 12, PR_TRUE, 4 },
+ { "abcdefgabcdefg", "gh", 12, PR_FALSE, 0 },
+ { "abcdabcabcdabc", "bc", 5, PR_TRUE, 1 },
+ { "abcdabcabcdabc", "bc", 6, PR_TRUE, 1 },
+ { "abcdabcabcdabc", "bc", 7, PR_TRUE, 1 },
+ { "abcdefgabcdefg", "abcdefg", 6, PR_FALSE, 0 },
+ { "abcdefgabcdefg", "abcdefg", 7, PR_TRUE, 0 },
+ { "abcdefgabcdefg", "abcdefg", 8, PR_TRUE, 0 },
+ { "ABCDEFG", "a", 5, PR_FALSE, 0 },
+ { "ABCDEFG", "c", 5, PR_FALSE, 0 },
+ { "ABCDEFG", "e", 5, PR_FALSE, 0 },
+ { "ABCDEFG", "g", 5, PR_FALSE, 0 },
+ { "ABCDEFG", "i", 5, PR_FALSE, 0 },
+ { "ABCDEFG", "ab", 5, PR_FALSE, 0 },
+ { "ABCDEFG", "cd", 5, PR_FALSE, 0 },
+ { "ABCDEFG", "ef", 5, PR_FALSE, 0 },
+ { "ABCDEFG", "gh", 5, PR_FALSE, 0 },
+ { "ABCDABC", "bc", 5, PR_FALSE, 0 },
+ { "ABCDABC", "bc", 6, PR_FALSE, 0 },
+ { "ABCDABC", "bc", 7, PR_FALSE, 0 },
+ { "ABCDEFG", "abcdefg", 6, PR_FALSE, 0 },
+ { "ABCDEFG", "abcdefg", 7, PR_FALSE, 0 },
+ { "ABCDEFG", "abcdefg", 8, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "a", 12, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "c", 12, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "e", 12, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "g", 12, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "i", 12, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "ab", 12, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "cd", 12, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "ef", 12, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "gh", 12, PR_FALSE, 0 },
+ { "ABCDABCABCDABC", "bc", 5, PR_FALSE, 0 },
+ { "ABCDABCABCDABC", "bc", 6, PR_FALSE, 0 },
+ { "ABCDABCABCDABC", "bc", 7, PR_FALSE, },
+ { "ABCDEFGABCDEFG", "abcdefg", 6, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "abcdefg", 7, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "abcdefg", 8, PR_FALSE, 0 }
+ };
+
+ int i;
+
+ printf("Test 025 (PL_strnstr) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char *rv = PL_strnstr(array[i].str, array[i].sub, array[i].max);
+
+ if( PR_FALSE == array[i].ret )
+ {
+ if( (char *)0 != rv )
+ {
+ printf("FAIL %d: %s,%s/%lu -> %.32s, not null\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ array[i].max, rv);
+ return PR_FALSE;
+ }
+ }
+ else
+ {
+ if( (char *)0 == rv )
+ {
+ printf("FAIL %d: %s,%s/%lu -> null, not 0x%x+%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ array[i].max, array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+
+ if( &array[i].str[ array[i].off ] != rv )
+ {
+ printf("FAIL %d: %s,%s/%lu -> 0x%x, not 0x%x+%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ array[i].max, rv, array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strnrstr */
+PRBool test_026(void)
+{
+ static struct
+ {
+ const char *str;
+ const char *sub;
+ PRUint32 max;
+ PRBool ret;
+ PRUint32 off;
+ } array[] =
+ {
+ { (const char *)0, (const char *)0, 12, PR_FALSE, 0 },
+ { (const char *)0, "blah", 12, PR_FALSE, 0 },
+ { "blah-de-blah", (const char *)0, 12, PR_FALSE, 0 },
+ { "blah-de-blah", "blah", 0, PR_FALSE, 0 },
+ { "blah-de-blah", "blah", 2, PR_FALSE, 0 },
+ { "blah-de-blah", "blah", 3, PR_FALSE, 0 },
+ { "blah-de-blah", "blah", 4, PR_TRUE, 0 },
+ { "blah-de-blah", "blah", 5, PR_TRUE, 0 },
+ { "blah-de-blah", "blah", 11, PR_TRUE, 0 },
+ { "blah-de-blah", "blah", 12, PR_TRUE, 8 },
+ { "blah-de-blah", "blah", 13, PR_TRUE, 8 },
+ { "", "blah", 12, PR_FALSE, 0 },
+ { "blah-de-blah", "", 12, PR_FALSE, 0 },
+ { "abcdefg", "a", 5, PR_TRUE, 0 },
+ { "abcdefg", "c", 5, PR_TRUE, 2 },
+ { "abcdefg", "e", 5, PR_TRUE, 4 },
+ { "abcdefg", "g", 5, PR_FALSE, 0 },
+ { "abcdefg", "i", 5, PR_FALSE, 0 },
+ { "abcdefg", "ab", 5, PR_TRUE, 0 },
+ { "abcdefg", "cd", 5, PR_TRUE, 2 },
+ { "abcdefg", "ef", 5, PR_FALSE, 0 },
+ { "abcdefg", "gh", 5, PR_FALSE, 0 },
+ { "abcdabc", "bc", 5, PR_TRUE, 1 },
+ { "abcdabc", "bc", 6, PR_TRUE, 1 },
+ { "abcdabc", "bc", 7, PR_TRUE, 5 },
+ { "abcdefg", "abcdefg", 6, PR_FALSE, 0 },
+ { "abcdefg", "abcdefg", 7, PR_TRUE, 0 },
+ { "abcdefg", "abcdefg", 8, PR_TRUE, 0 },
+ { "abcdefgabcdefg", "a", 12, PR_TRUE, 7 },
+ { "abcdefgabcdefg", "c", 12, PR_TRUE, 9 },
+ { "abcdefgabcdefg", "e", 12, PR_TRUE, 11 },
+ { "abcdefgabcdefg", "g", 12, PR_TRUE, 6 },
+ { "abcdefgabcdefg", "i", 12, PR_FALSE, 0 },
+ { "abcdefgabcdefg", "ab", 12, PR_TRUE, 7 },
+ { "abcdefgabcdefg", "cd", 12, PR_TRUE, 9 },
+ { "abcdefgabcdefg", "ef", 12, PR_TRUE, 4 },
+ { "abcdefgabcdefg", "gh", 12, PR_FALSE, 0 },
+ { "abcdabcabcdabc", "bc", 12, PR_TRUE, 8 },
+ { "abcdabcabcdabc", "bc", 13, PR_TRUE, 8 },
+ { "abcdabcabcdabc", "bc", 14, PR_TRUE, 12 },
+ { "abcdefgabcdefg", "abcdefg", 13, PR_TRUE, 0 },
+ { "abcdefgabcdefg", "abcdefg", 14, PR_TRUE, 7 },
+ { "abcdefgabcdefg", "abcdefg", 15, PR_TRUE, 7 },
+ { "ABCDEFG", "a", 5, PR_FALSE, 0 },
+ { "ABCDEFG", "c", 5, PR_FALSE, 0 },
+ { "ABCDEFG", "e", 5, PR_FALSE, 0 },
+ { "ABCDEFG", "g", 5, PR_FALSE, 0 },
+ { "ABCDEFG", "i", 5, PR_FALSE, 0 },
+ { "ABCDEFG", "ab", 5, PR_FALSE, 0 },
+ { "ABCDEFG", "cd", 5, PR_FALSE, 0 },
+ { "ABCDEFG", "ef", 5, PR_FALSE, 0 },
+ { "ABCDEFG", "gh", 5, PR_FALSE, 0 },
+ { "ABCDABC", "bc", 5, PR_FALSE, 0 },
+ { "ABCDABC", "bc", 6, PR_FALSE, 0 },
+ { "ABCDABC", "bc", 7, PR_FALSE, 0 },
+ { "ABCDEFG", "abcdefg", 6, PR_FALSE, 0 },
+ { "ABCDEFG", "abcdefg", 7, PR_FALSE, 0 },
+ { "ABCDEFG", "abcdefg", 8, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "a", 12, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "c", 12, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "e", 12, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "g", 12, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "i", 12, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "ab", 12, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "cd", 12, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "ef", 12, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "gh", 12, PR_FALSE, 0 },
+ { "ABCDABCABCDABC", "bc", 12, PR_FALSE, 0 },
+ { "ABCDABCABCDABC", "bc", 13, PR_FALSE, 0 },
+ { "ABCDABCABCDABC", "bc", 14, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "abcdefg", 13, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "abcdefg", 14, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "abcdefg", 15, PR_FALSE, 0 }
+ };
+
+ int i;
+
+ printf("Test 026 (PL_strnrstr) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char *rv = PL_strnrstr(array[i].str, array[i].sub, array[i].max);
+
+ if( PR_FALSE == array[i].ret )
+ {
+ if( (char *)0 != rv )
+ {
+ printf("FAIL %d: %s,%s/%lu -> %.32s, not null\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ array[i].max, rv);
+ return PR_FALSE;
+ }
+ }
+ else
+ {
+ if( (char *)0 == rv )
+ {
+ printf("FAIL %d: %s,%s/%lu -> null, not 0x%x+%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ array[i].max, array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+
+ if( &array[i].str[ array[i].off ] != rv )
+ {
+ printf("FAIL %d: %s,%s/%lu -> 0x%x, not 0x%x+%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ array[i].max, rv, array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strcasestr */
+PRBool test_027(void)
+{
+ static struct
+ {
+ const char *str;
+ const char *sub;
+ PRBool ret;
+ PRUint32 off;
+ } array[] =
+ {
+ { (const char *)0, (const char *)0, PR_FALSE, 0 },
+ { (const char *)0, "blah", PR_FALSE, 0 },
+ { "blah-de-blah", (const char *)0, PR_FALSE, 0 },
+ { "blah-de-blah", "blah", PR_TRUE, 0 },
+ { "", "blah", PR_FALSE, 0 },
+ { "blah-de-blah", "", PR_FALSE, 0 },
+ { "abcdefg", "a", PR_TRUE, 0 },
+ { "abcdefg", "c", PR_TRUE, 2 },
+ { "abcdefg", "e", PR_TRUE, 4 },
+ { "abcdefg", "g", PR_TRUE, 6 },
+ { "abcdefg", "i", PR_FALSE, 0 },
+ { "abcdefg", "ab", PR_TRUE, 0 },
+ { "abcdefg", "cd", PR_TRUE, 2 },
+ { "abcdefg", "ef", PR_TRUE, 4 },
+ { "abcdefg", "gh", PR_FALSE, 0 },
+ { "abcdabc", "bc", PR_TRUE, 1 },
+ { "abcdefg", "abcdefg", PR_TRUE, 0 },
+ { "abcdefgabcdefg", "a", PR_TRUE, 0 },
+ { "abcdefgabcdefg", "c", PR_TRUE, 2 },
+ { "abcdefgabcdefg", "e", PR_TRUE, 4 },
+ { "abcdefgabcdefg", "g", PR_TRUE, 6 },
+ { "abcdefgabcdefg", "i", PR_FALSE, 0 },
+ { "abcdefgabcdefg", "ab", PR_TRUE, 0 },
+ { "abcdefgabcdefg", "cd", PR_TRUE, 2 },
+ { "abcdefgabcdefg", "ef", PR_TRUE, 4 },
+ { "abcdefgabcdefg", "gh", PR_FALSE, 0 },
+ { "abcdabcabcdabc", "bc", PR_TRUE, 1 },
+ { "abcdefgabcdefg", "abcdefg", PR_TRUE, 0 },
+ { "ABCDEFG", "a", PR_TRUE, 0 },
+ { "ABCDEFG", "c", PR_TRUE, 2 },
+ { "ABCDEFG", "e", PR_TRUE, 4 },
+ { "ABCDEFG", "g", PR_TRUE, 6 },
+ { "ABCDEFG", "i", PR_FALSE, 0 },
+ { "ABCDEFG", "ab", PR_TRUE, 0 },
+ { "ABCDEFG", "cd", PR_TRUE, 2 },
+ { "ABCDEFG", "ef", PR_TRUE, 4 },
+ { "ABCDEFG", "gh", PR_FALSE, 0 },
+ { "ABCDABC", "bc", PR_TRUE, 1 },
+ { "ABCDEFG", "abcdefg", PR_TRUE, 0 },
+ { "ABCDEFGABCDEFG", "a", PR_TRUE, 0 },
+ { "ABCDEFGABCDEFG", "c", PR_TRUE, 2 },
+ { "ABCDEFGABCDEFG", "e", PR_TRUE, 4 },
+ { "ABCDEFGABCDEFG", "g", PR_TRUE, 6 },
+ { "ABCDEFGABCDEFG", "i", PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "ab", PR_TRUE, 0 },
+ { "ABCDEFGABCDEFG", "cd", PR_TRUE, 2 },
+ { "ABCDEFGABCDEFG", "ef", PR_TRUE, 4 },
+ { "ABCDEFGABCDEFG", "gh", PR_FALSE, 0 },
+ { "ABCDABCABCDABC", "bc", PR_TRUE, 1 },
+ { "ABCDEFGABCDEFG", "abcdefg", PR_TRUE, 0 }
+ };
+
+ int i;
+
+ printf("Test 027 (PL_strcasestr) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char *rv = PL_strcasestr(array[i].str, array[i].sub);
+
+ if( PR_FALSE == array[i].ret )
+ {
+ if( (char *)0 != rv )
+ {
+ printf("FAIL %d: %s,%s -> %.32s, not null\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ rv);
+ return PR_FALSE;
+ }
+ }
+ else
+ {
+ if( (char *)0 == rv )
+ {
+ printf("FAIL %d: %s,%s -> null, not 0x%x+%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+
+ if( &array[i].str[ array[i].off ] != rv )
+ {
+ printf("FAIL %d: %s,%s -> 0x%x, not 0x%x+%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ rv, array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strcaserstr */
+PRBool test_028(void)
+{
+ static struct
+ {
+ const char *str;
+ const char *sub;
+ PRBool ret;
+ PRUint32 off;
+ } array[] =
+ {
+ { (const char *)0, (const char *)0, PR_FALSE, 0 },
+ { (const char *)0, "blah", PR_FALSE, 0 },
+ { "blah-de-blah", (const char *)0, PR_FALSE, 0 },
+ { "blah-de-blah", "blah", PR_TRUE, 8 },
+ { "", "blah", PR_FALSE, 0 },
+ { "blah-de-blah", "", PR_FALSE, 0 },
+ { "abcdefg", "a", PR_TRUE, 0 },
+ { "abcdefg", "c", PR_TRUE, 2 },
+ { "abcdefg", "e", PR_TRUE, 4 },
+ { "abcdefg", "g", PR_TRUE, 6 },
+ { "abcdefg", "i", PR_FALSE, 0 },
+ { "abcdefg", "ab", PR_TRUE, 0 },
+ { "abcdefg", "cd", PR_TRUE, 2 },
+ { "abcdefg", "ef", PR_TRUE, 4 },
+ { "abcdefg", "gh", PR_FALSE, 0 },
+ { "abcdabc", "bc", PR_TRUE, 5 },
+ { "abcdefg", "abcdefg", PR_TRUE, 0 },
+ { "abcdefgabcdefg", "a", PR_TRUE, 7 },
+ { "abcdefgabcdefg", "c", PR_TRUE, 9 },
+ { "abcdefgabcdefg", "e", PR_TRUE, 11 },
+ { "abcdefgabcdefg", "g", PR_TRUE, 13 },
+ { "abcdefgabcdefg", "i", PR_FALSE, 0 },
+ { "abcdefgabcdefg", "ab", PR_TRUE, 7 },
+ { "abcdefgabcdefg", "cd", PR_TRUE, 9 },
+ { "abcdefgabcdefg", "ef", PR_TRUE, 11 },
+ { "abcdefgabcdefg", "gh", PR_FALSE, 0 },
+ { "abcdabcabcdabc", "bc", PR_TRUE, 12 },
+ { "abcdefgabcdefg", "abcdefg", PR_TRUE, 7 },
+ { "ABCDEFG", "a", PR_TRUE, 0 },
+ { "ABCDEFG", "c", PR_TRUE, 2 },
+ { "ABCDEFG", "e", PR_TRUE, 4 },
+ { "ABCDEFG", "g", PR_TRUE, 6 },
+ { "ABCDEFG", "i", PR_FALSE, 0 },
+ { "ABCDEFG", "ab", PR_TRUE, 0 },
+ { "ABCDEFG", "cd", PR_TRUE, 2 },
+ { "ABCDEFG", "ef", PR_TRUE, 4 },
+ { "ABCDEFG", "gh", PR_FALSE, 0 },
+ { "ABCDABC", "bc", PR_TRUE, 5 },
+ { "ABCDEFG", "abcdefg", PR_TRUE, 0 },
+ { "ABCDEFGABCDEFG", "a", PR_TRUE, 7 },
+ { "ABCDEFGABCDEFG", "c", PR_TRUE, 9 },
+ { "ABCDEFGABCDEFG", "e", PR_TRUE, 11 },
+ { "ABCDEFGABCDEFG", "g", PR_TRUE, 13 },
+ { "ABCDEFGABCDEFG", "i", PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "ab", PR_TRUE, 7 },
+ { "ABCDEFGABCDEFG", "cd", PR_TRUE, 9 },
+ { "ABCDEFGABCDEFG", "ef", PR_TRUE, 11 },
+ { "ABCDEFGABCDEFG", "gh", PR_FALSE, 0 },
+ { "ABCDABCABCDABC", "bc", PR_TRUE, 12 },
+ { "ABCDEFGABCDEFG", "abcdefg", PR_TRUE, 7 }
+ };
+
+ int i;
+
+ printf("Test 028 (PL_strcaserstr) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char *rv = PL_strcaserstr(array[i].str, array[i].sub);
+
+ if( PR_FALSE == array[i].ret )
+ {
+ if( (char *)0 != rv )
+ {
+ printf("FAIL %d: %s,%s -> %.32s, not null\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ rv);
+ return PR_FALSE;
+ }
+ }
+ else
+ {
+ if( (char *)0 == rv )
+ {
+ printf("FAIL %d: %s,%s -> null, not 0x%x+%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+
+ if( &array[i].str[ array[i].off ] != rv )
+ {
+ printf("FAIL %d: %s,%s -> 0x%x, not 0x%x+%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ rv, array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strncasestr */
+PRBool test_029(void)
+{
+ static struct
+ {
+ const char *str;
+ const char *sub;
+ PRUint32 max;
+ PRBool ret;
+ PRUint32 off;
+ } array[] =
+ {
+ { (const char *)0, (const char *)0, 12, PR_FALSE, 0 },
+ { (const char *)0, "blah", 12, PR_FALSE, 0 },
+ { "blah-de-blah", (const char *)0, 12, PR_FALSE, 0 },
+ { "blah-de-blah", "blah", 0, PR_FALSE, 0 },
+ { "blah-de-blah", "blah", 2, PR_FALSE, 0 },
+ { "blah-de-blah", "blah", 3, PR_FALSE, 0 },
+ { "blah-de-blah", "blah", 4, PR_TRUE, 0 },
+ { "blah-de-blah", "blah", 5, PR_TRUE, 0 },
+ { "blah-de-blah", "blah", 12, PR_TRUE, 0 },
+ { "", "blah", 12, PR_FALSE, 0 },
+ { "blah-de-blah", "", 12, PR_FALSE, 0 },
+ { "abcdefg", "a", 5, PR_TRUE, 0 },
+ { "abcdefg", "c", 5, PR_TRUE, 2 },
+ { "abcdefg", "e", 5, PR_TRUE, 4 },
+ { "abcdefg", "g", 5, PR_FALSE, 0 },
+ { "abcdefg", "i", 5, PR_FALSE, 0 },
+ { "abcdefg", "ab", 5, PR_TRUE, 0 },
+ { "abcdefg", "cd", 5, PR_TRUE, 2 },
+ { "abcdefg", "ef", 5, PR_FALSE, 0 },
+ { "abcdefg", "gh", 5, PR_FALSE, 0 },
+ { "abcdabc", "bc", 5, PR_TRUE, 1 },
+ { "abcdabc", "bc", 6, PR_TRUE, 1 },
+ { "abcdabc", "bc", 7, PR_TRUE, 1 },
+ { "abcdefg", "abcdefg", 6, PR_FALSE, 0 },
+ { "abcdefg", "abcdefg", 7, PR_TRUE, 0 },
+ { "abcdefg", "abcdefg", 8, PR_TRUE, 0 },
+ { "abcdefgabcdefg", "a", 12, PR_TRUE, 0 },
+ { "abcdefgabcdefg", "c", 12, PR_TRUE, 2 },
+ { "abcdefgabcdefg", "e", 12, PR_TRUE, 4 },
+ { "abcdefgabcdefg", "g", 12, PR_TRUE, 6 },
+ { "abcdefgabcdefg", "i", 12, PR_FALSE, 0 },
+ { "abcdefgabcdefg", "ab", 12, PR_TRUE, 0 },
+ { "abcdefgabcdefg", "cd", 12, PR_TRUE, 2 },
+ { "abcdefgabcdefg", "ef", 12, PR_TRUE, 4 },
+ { "abcdefgabcdefg", "gh", 12, PR_FALSE, 0 },
+ { "abcdabcabcdabc", "bc", 5, PR_TRUE, 1 },
+ { "abcdabcabcdabc", "bc", 6, PR_TRUE, 1 },
+ { "abcdabcabcdabc", "bc", 7, PR_TRUE, 1 },
+ { "abcdefgabcdefg", "abcdefg", 6, PR_FALSE, 0 },
+ { "abcdefgabcdefg", "abcdefg", 7, PR_TRUE, 0 },
+ { "abcdefgabcdefg", "abcdefg", 8, PR_TRUE, 0 },
+ { "ABCDEFG", "a", 5, PR_TRUE, 0 },
+ { "ABCDEFG", "c", 5, PR_TRUE, 2 },
+ { "ABCDEFG", "e", 5, PR_TRUE, 4 },
+ { "ABCDEFG", "g", 5, PR_FALSE, 0 },
+ { "ABCDEFG", "i", 5, PR_FALSE, 0 },
+ { "ABCDEFG", "ab", 5, PR_TRUE, 0 },
+ { "ABCDEFG", "cd", 5, PR_TRUE, 2 },
+ { "ABCDEFG", "ef", 5, PR_FALSE, 0 },
+ { "ABCDEFG", "gh", 5, PR_FALSE, 0 },
+ { "ABCDABC", "bc", 5, PR_TRUE, 1 },
+ { "ABCDABC", "bc", 6, PR_TRUE, 1 },
+ { "ABCDABC", "bc", 7, PR_TRUE, 1 },
+ { "ABCDEFG", "abcdefg", 6, PR_FALSE, 0 },
+ { "ABCDEFG", "abcdefg", 7, PR_TRUE, 0 },
+ { "ABCDEFG", "abcdefg", 8, PR_TRUE, 0 },
+ { "ABCDEFGABCDEFG", "a", 12, PR_TRUE, 0 },
+ { "ABCDEFGABCDEFG", "c", 12, PR_TRUE, 2 },
+ { "ABCDEFGABCDEFG", "e", 12, PR_TRUE, 4 },
+ { "ABCDEFGABCDEFG", "g", 12, PR_TRUE, 6 },
+ { "ABCDEFGABCDEFG", "i", 12, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "ab", 12, PR_TRUE, 0 },
+ { "ABCDEFGABCDEFG", "cd", 12, PR_TRUE, 2 },
+ { "ABCDEFGABCDEFG", "ef", 12, PR_TRUE, 4 },
+ { "ABCDEFGABCDEFG", "gh", 12, PR_FALSE, 0 },
+ { "ABCDABCABCDABC", "bc", 5, PR_TRUE, 1 },
+ { "ABCDABCABCDABC", "bc", 6, PR_TRUE, 1 },
+ { "ABCDABCABCDABC", "bc", 7, PR_TRUE, 1 },
+ { "ABCDEFGABCDEFG", "abcdefg", 6, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "abcdefg", 7, PR_TRUE, 0 },
+ { "ABCDEFGABCDEFG", "abcdefg", 8, PR_TRUE, 0 }
+ };
+
+ int i;
+
+ printf("Test 029 (PL_strncasestr) ..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char *rv = PL_strncasestr(array[i].str, array[i].sub, array[i].max);
+
+ if( PR_FALSE == array[i].ret )
+ {
+ if( (char *)0 != rv )
+ {
+ printf("FAIL %d: %s,%s/%lu -> %.32s, not null\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ array[i].max, rv);
+ return PR_FALSE;
+ }
+ }
+ else
+ {
+ if( (char *)0 == rv )
+ {
+ printf("FAIL %d: %s,%s/%lu -> null, not 0x%x+%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ array[i].max, array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+
+ if( &array[i].str[ array[i].off ] != rv )
+ {
+ printf("FAIL %d: %s,%s/%lu -> 0x%x, not 0x%x+%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ array[i].max, rv, array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+/* PL_strncaserstr */
+PRBool test_030(void)
+{
+ static struct
+ {
+ const char *str;
+ const char *sub;
+ PRUint32 max;
+ PRBool ret;
+ PRUint32 off;
+ } array[] =
+ {
+ { (const char *)0, (const char *)0, 12, PR_FALSE, 0 },
+ { (const char *)0, "blah", 12, PR_FALSE, 0 },
+ { "blah-de-blah", (const char *)0, 12, PR_FALSE, 0 },
+ { "blah-de-blah", "blah", 0, PR_FALSE, 0 },
+ { "blah-de-blah", "blah", 2, PR_FALSE, 0 },
+ { "blah-de-blah", "blah", 3, PR_FALSE, 0 },
+ { "blah-de-blah", "blah", 4, PR_TRUE, 0 },
+ { "blah-de-blah", "blah", 5, PR_TRUE, 0 },
+ { "blah-de-blah", "blah", 11, PR_TRUE, 0 },
+ { "blah-de-blah", "blah", 12, PR_TRUE, 8 },
+ { "blah-de-blah", "blah", 13, PR_TRUE, 8 },
+ { "", "blah", 12, PR_FALSE, 0 },
+ { "blah-de-blah", "", 12, PR_FALSE, 0 },
+ { "abcdefg", "a", 5, PR_TRUE, 0 },
+ { "abcdefg", "c", 5, PR_TRUE, 2 },
+ { "abcdefg", "e", 5, PR_TRUE, 4 },
+ { "abcdefg", "g", 5, PR_FALSE, 0 },
+ { "abcdefg", "i", 5, PR_FALSE, 0 },
+ { "abcdefg", "ab", 5, PR_TRUE, 0 },
+ { "abcdefg", "cd", 5, PR_TRUE, 2 },
+ { "abcdefg", "ef", 5, PR_FALSE, 0 },
+ { "abcdefg", "gh", 5, PR_FALSE, 0 },
+ { "abcdabc", "bc", 5, PR_TRUE, 1 },
+ { "abcdabc", "bc", 6, PR_TRUE, 1 },
+ { "abcdabc", "bc", 7, PR_TRUE, 5 },
+ { "abcdefg", "abcdefg", 6, PR_FALSE, 0 },
+ { "abcdefg", "abcdefg", 7, PR_TRUE, 0 },
+ { "abcdefg", "abcdefg", 8, PR_TRUE, 0 },
+ { "abcdefgabcdefg", "a", 12, PR_TRUE, 7 },
+ { "abcdefgabcdefg", "c", 12, PR_TRUE, 9 },
+ { "abcdefgabcdefg", "e", 12, PR_TRUE, 11 },
+ { "abcdefgabcdefg", "g", 12, PR_TRUE, 6 },
+ { "abcdefgabcdefg", "i", 12, PR_FALSE, 0 },
+ { "abcdefgabcdefg", "ab", 12, PR_TRUE, 7 },
+ { "abcdefgabcdefg", "cd", 12, PR_TRUE, 9 },
+ { "abcdefgabcdefg", "ef", 12, PR_TRUE, 4 },
+ { "abcdefgabcdefg", "gh", 12, PR_FALSE, 0 },
+ { "abcdabcabcdabc", "bc", 12, PR_TRUE, 8 },
+ { "abcdabcabcdabc", "bc", 13, PR_TRUE, 8 },
+ { "abcdabcabcdabc", "bc", 14, PR_TRUE, 12 },
+ { "abcdefgabcdefg", "abcdefg", 13, PR_TRUE, 0 },
+ { "abcdefgabcdefg", "abcdefg", 14, PR_TRUE, 7 },
+ { "abcdefgabcdefg", "abcdefg", 15, PR_TRUE, 7 },
+ { "ABCDEFG", "a", 5, PR_TRUE, 0 },
+ { "ABCDEFG", "c", 5, PR_TRUE, 2 },
+ { "ABCDEFG", "e", 5, PR_TRUE, 4 },
+ { "ABCDEFG", "g", 5, PR_FALSE, 0 },
+ { "ABCDEFG", "i", 5, PR_FALSE, 0 },
+ { "ABCDEFG", "ab", 5, PR_TRUE, 0 },
+ { "ABCDEFG", "cd", 5, PR_TRUE, 2 },
+ { "ABCDEFG", "ef", 5, PR_FALSE, 0 },
+ { "ABCDEFG", "gh", 5, PR_FALSE, 0 },
+ { "ABCDABC", "bc", 5, PR_TRUE, 1 },
+ { "ABCDABC", "bc", 6, PR_TRUE, 1 },
+ { "ABCDABC", "bc", 7, PR_TRUE, 5 },
+ { "ABCDEFG", "abcdefg", 6, PR_FALSE, 0 },
+ { "ABCDEFG", "abcdefg", 7, PR_TRUE, 0 },
+ { "ABCDEFG", "abcdefg", 8, PR_TRUE, 0 },
+ { "ABCDEFGABCDEFG", "a", 12, PR_TRUE, 7 },
+ { "ABCDEFGABCDEFG", "c", 12, PR_TRUE, 9 },
+ { "ABCDEFGABCDEFG", "e", 12, PR_TRUE, 11 },
+ { "ABCDEFGABCDEFG", "g", 12, PR_TRUE, 6 },
+ { "ABCDEFGABCDEFG", "i", 12, PR_FALSE, 0 },
+ { "ABCDEFGABCDEFG", "ab", 12, PR_TRUE, 7 },
+ { "ABCDEFGABCDEFG", "cd", 12, PR_TRUE, 9 },
+ { "ABCDEFGABCDEFG", "ef", 12, PR_TRUE, 4 },
+ { "ABCDEFGABCDEFG", "gh", 12, PR_FALSE, 0 },
+ { "ABCDABCABCDABC", "bc", 12, PR_TRUE, 8 },
+ { "ABCDABCABCDABC", "bc", 13, PR_TRUE, 8 },
+ { "ABCDABCABCDABC", "bc", 14, PR_TRUE, 12 },
+ { "ABCDEFGABCDEFG", "abcdefg", 13, PR_TRUE, 0 },
+ { "ABCDEFGABCDEFG", "abcdefg", 14, PR_TRUE, 7 },
+ { "ABCDEFGABCDEFG", "abcdefg", 15, PR_TRUE, 7 }
+ };
+
+ int i;
+
+ printf("Test 030 (PL_strncaserstr)..."); fflush(stdout);
+
+ for( i = 0; i < sizeof(array)/sizeof(array[0]); i++ )
+ {
+ char *rv = PL_strncaserstr(array[i].str, array[i].sub, array[i].max);
+
+ if( PR_FALSE == array[i].ret )
+ {
+ if( (char *)0 != rv )
+ {
+ printf("FAIL %d: %s,%s/%lu -> %.32s, not null\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ array[i].max, rv);
+ return PR_FALSE;
+ }
+ }
+ else
+ {
+ if( (char *)0 == rv )
+ {
+ printf("FAIL %d: %s,%s/%lu -> null, not 0x%x+%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ array[i].max, array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+
+ if( &array[i].str[ array[i].off ] != rv )
+ {
+ printf("FAIL %d: %s,%s/%lu -> 0x%x, not 0x%x+%lu\n", i,
+ array[i].str ? array[i].str : "(null)",
+ array[i].sub ? array[i].sub : "(null)",
+ array[i].max, rv, array[i].str, array[i].off);
+ return PR_FALSE;
+ }
+ }
+ }
+
+ printf("PASS\n");
+ return PR_TRUE;
+}
+
+int
+main
+(
+ int argc,
+ char *argv[]
+)
+{
+ printf("Testing the Portable Library string functions:\n");
+
+ if( 1
+ && test_001()
+ && test_001()
+ && test_002()
+ && test_003()
+ && test_004()
+ && test_005()
+ && test_006()
+ && test_007()
+ && test_008()
+ && test_009()
+ && test_010()
+ && test_011()
+ && test_012()
+ && test_013()
+ && test_014()
+ && test_015()
+ && test_016()
+ && test_017()
+ && test_018()
+ && test_019()
+ && test_020()
+ && test_021()
+ && test_022()
+ && test_023()
+ && test_024()
+ && test_025()
+ && test_026()
+ && test_027()
+ && test_028()
+ && test_029()
+ && test_030()
+ )
+ {
+ printf("Suite passed.\n");
+ return 0;
+ }
+ else
+ {
+ printf("Suite failed.\n");
+ return 1;
+ }
+
+ /*NOTREACHED*/
+}
diff --git a/macbuild/NSPR.Debug.Prefix b/macbuild/NSPR.Debug.Prefix
new file mode 100644
index 00000000..ae6233ee
--- /dev/null
+++ b/macbuild/NSPR.Debug.Prefix
@@ -0,0 +1 @@
+// // NSPR.Prefix // // Global prefix file for the debug NSPR project. // // #define DEBUG 1 #include "NSPRConfig.h" \ No newline at end of file
diff --git a/macbuild/NSPR.Prefix b/macbuild/NSPR.Prefix
new file mode 100644
index 00000000..a3947318
--- /dev/null
+++ b/macbuild/NSPR.Prefix
@@ -0,0 +1 @@
+// // NSPR.Prefix // // Global prefix file for the non-debug NSPR project. // // #include "NSPRConfig.h" \ No newline at end of file
diff --git a/macbuild/NSPR20PPC.mcp b/macbuild/NSPR20PPC.mcp
new file mode 100644
index 00000000..f15a68a2
--- /dev/null
+++ b/macbuild/NSPR20PPC.mcp
Binary files differ
diff --git a/macbuild/NSPRConfig.h b/macbuild/NSPRConfig.h
new file mode 100644
index 00000000..e97579b5
--- /dev/null
+++ b/macbuild/NSPRConfig.h
@@ -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.
+ */
+
+#define OLDROUTINELOCATIONS 0
+
+#define XP_MAC 1
+#define _PR_NO_PREEMPT 1
+#define _NO_FAST_STRING_INLINES_ 1
+#define FORCE_PR_LOG 1
+#define NSPR20 1
+
diff --git a/macbuild/NSPRDebug.Prefix b/macbuild/NSPRDebug.Prefix
new file mode 100644
index 00000000..a656aa49
--- /dev/null
+++ b/macbuild/NSPRDebug.Prefix
@@ -0,0 +1,11 @@
+//
+// NSPR.Prefix
+//
+// Global prefix file for the debug NSPR project.
+//
+//
+
+
+#define DEBUG 1
+#define DEVELOPER_DEBUG 1
+#include "NSPRConfig.h" \ No newline at end of file
diff --git a/makefile.win b/makefile.win
new file mode 100644
index 00000000..11ad6cb2
--- /dev/null
+++ b/makefile.win
@@ -0,0 +1,97 @@
+#
+# 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.
+#
+
+#
+# An NMAKE file to set up and adjust NSPR20's build system for
+# Client build. Client build should invoke NMAKE on this file
+# instead of invoking gmake directly.
+#
+
+DEPTH = ..
+include <$(DEPTH)\config\config.mak>
+
+#
+# Backslashes are escape characters to gmake, so flip all backslashes
+# in $(MOZ_TOOLS) to forward slashes and pass that to gmake.
+#
+
+GMAKE = $(MOZ_TOOLS)\bin\gmake.exe
+
+GMAKE_FLAGS = MOZ_TOOLS_FLIPPED=$(MOZ_TOOLS:\=/) PR_CLIENT_BUILD=1 PR_CLIENT_BUILD_WINDOWS=1
+
+#
+# The Client's debug build uses MSVC's debug runtime library (/MDd).
+#
+
+!ifdef MOZ_DEBUG
+!else
+GMAKE_FLAGS = $(GMAKE_FLAGS) BUILD_OPT=1
+!endif
+
+!if "$(MOZ_BITS)" == "16"
+GMAKE_FLAGS = $(GMAKE_FLAGS) OS_TARGET=WIN16
+!else
+
+GMAKE_FLAGS = $(GMAKE_FLAGS) OS_TARGET=WIN95
+!ifdef MOZ_DEBUG
+!ifdef MOZ_NO_DEBUG_RTL
+PR_OBJDIR = WIN954.0_DBG.OBJ
+!else
+GMAKE_FLAGS = $(GMAKE_FLAGS) USE_DEBUG_RTL=1
+PR_OBJDIR = WIN954.0_DBG.OBJD
+!endif
+!else
+PR_OBJDIR = WIN954.0_OPT.OBJ
+!endif
+
+!endif
+
+
+#
+# The rules. Simply invoke gmake with the same target.
+# The default target is 'all'. For Win16, set up the
+# environment to use the Watcom compiler, Watcom headers,
+# and Watcom libs.
+#
+
+all:: export libs install
+
+export libs install clobber clobber_all clean::
+!if "$(MOZ_BITS)" == "16"
+ set PATH=%WATCPATH%
+ set INCLUDE=%WATC_INC%
+ set LIB=%WATC_LIB%
+!endif
+ $(GMAKE) $(GMAKE_FLAGS) $@
+!if "$(MOZ_BITS)" == "16"
+ set PATH=%MSVCPATH%
+ set INCLUDE=%MSVC_INC%
+ set LIB=%MSVC_LIB%
+!endif
+
+!if "$(MOZ_BITS)" != "16"
+export::
+ $(MAKE_INSTALL) $(XPDIST)\$(PR_OBJDIR)\include\*.h $(DIST)\include
+ $(MAKE_INSTALL) $(XPDIST)\$(PR_OBJDIR)\include\md\*.h $(DIST)\include\md
+ $(MAKE_INSTALL) $(XPDIST)\$(PR_OBJDIR)\include\obsolete\*.h $(DIST)\include\obsolete
+ $(MAKE_INSTALL) $(XPDIST)\$(PR_OBJDIR)\include\private\*.h $(DIST)\include\private
+ $(MAKE_INSTALL) $(XPDIST)\$(PR_OBJDIR)\lib\*.lib $(DIST)\lib
+ $(MAKE_INSTALL) $(XPDIST)\$(PR_OBJDIR)\lib\*.dll $(DIST)\bin
+!endif
+
+depend::
+ @echo NSPR20 has no dependencies. Skipped.
diff --git a/pr/Makefile b/pr/Makefile
new file mode 100644
index 00000000..0c667155
--- /dev/null
+++ b/pr/Makefile
@@ -0,0 +1,24 @@
+#
+# 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 = ..
+
+DIRS = include src
+
+include $(MOD_DEPTH)/config/rules.mk
diff --git a/pr/include/MANIFEST b/pr/include/MANIFEST
new file mode 100644
index 00000000..6ef1b6d0
--- /dev/null
+++ b/pr/include/MANIFEST
@@ -0,0 +1,45 @@
+#
+# This is a list of local files which get copied to the mozilla:dist directory
+#
+
+nspr.h
+pratom.h
+prbit.h
+prclist.h
+prcmon.h
+prcountr.h
+prcvar.h
+prdtoa.h
+prenv.h
+prerror.h
+prinet.h
+prinit.h
+prinrval.h
+prio.h
+prlink.h
+prlock.h
+prlog.h
+prlong.h
+prmem.h
+prmon.h
+prmwait.h
+prnetdb.h
+prolock.h
+prpdce.h
+prprf.h
+prproces.h
+prsystem.h
+prthread.h
+prtime.h
+prtrace.h
+prtypes.h
+prvrsion.h
+prwin16.h
+
+obsolete/protypes.h
+obsolete/prsem.h
+obsolete/probslet.h
+
+private/prpriv.h
+private/pprio.h
+private/pprthred.h
diff --git a/pr/include/Makefile b/pr/include/Makefile
new file mode 100644
index 00000000..9ede1f4a
--- /dev/null
+++ b/pr/include/Makefile
@@ -0,0 +1,37 @@
+#
+# 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 = ../..
+
+DIRS = md private obsolete
+
+include $(MOD_DEPTH)/config/config.mk
+
+HEADERS = $(wildcard *.h)
+
+RELEASE_HEADERS = $(HEADERS)
+RELEASE_HEADERS_DEST = $(RELEASE_INCLUDE_DIR)
+
+include $(MOD_DEPTH)/config/rules.mk
+
+export:: $(HEADERS)
+ $(INSTALL) -m 444 $(HEADERS) $(DIST)/include
+ifeq ($(MOZ_BITS),16)
+ $(INSTALL) -m 444 $(HEADERS) $(MOZ_INCL)
+endif
diff --git a/pr/include/gencfg.c b/pr/include/gencfg.c
new file mode 100644
index 00000000..695f17ba
--- /dev/null
+++ b/pr/include/gencfg.c
@@ -0,0 +1,284 @@
+/* -*- 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 <stdio.h>
+
+#if defined(sgi)
+#ifndef IRIX
+ error - IRIX is not defined
+#endif
+#endif
+
+#if defined(__sun)
+#if defined(__svr4) || defined(__svr4__) || defined(__SVR4)
+#ifndef SOLARIS
+ error - SOLARIS is not defined
+#endif
+#else
+#ifndef SUNOS4
+ error - SUNOS4 is not defined
+#endif
+#endif
+#endif
+
+#if defined(__hpux)
+#ifndef HPUX
+ error - HPUX is not defined
+#endif
+#endif
+
+#if defined(__alpha)
+#if !(defined(_WIN32)) && !(defined(OSF1)) && !(defined(__linux))
+ error - None of OSF1, _WIN32, or __linux is defined
+#endif
+#endif
+
+#if defined(_IBMR2)
+#ifndef AIX
+ error - AIX is not defined
+#endif
+#endif
+
+#if defined(linux)
+#ifndef LINUX
+ error - LINUX is not defined
+#endif
+#endif
+
+#if defined(bsdi)
+#ifndef BSDI
+ error - BSDI is not defined
+#endif
+#endif
+
+#if defined(M_UNIX)
+#ifndef SCO
+ error - SCO is not defined
+#endif
+#endif
+#if !defined(M_UNIX) && defined(_USLC_)
+#ifndef UNIXWARE
+ error - UNIXWARE is not defined
+#endif
+#endif
+
+#if defined(__APPLE__)
+#ifndef RHAPSODY
+ error - RHAPSODY is not defined
+#endif
+#endif
+
+/************************************************************************/
+
+/* Generate cpucfg.h */
+
+#ifdef XP_PC
+#ifdef WIN32
+#define INT64 _PRInt64
+#else
+#define INT64 long
+#endif
+#else
+#if defined(HPUX) || defined(NECSVR4) || defined(SCO) || defined(UNIXWARE) || defined (NCR)
+#define INT64 long
+#else
+#define INT64 long long
+#endif
+#endif
+
+struct align_short {
+ char c;
+ short a;
+};
+struct align_int {
+ char c;
+ int a;
+};
+struct align_long {
+ char c;
+ long a;
+};
+struct align_PRInt64 {
+ char c;
+ INT64 a;
+};
+struct align_fakelonglong {
+ char c;
+ struct {
+ long hi, lo;
+ } a;
+};
+struct align_float {
+ char c;
+ float a;
+};
+struct align_double {
+ char c;
+ double a;
+};
+struct align_pointer {
+ char c;
+ void *a;
+};
+
+#define ALIGN_OF(type) \
+ (((char*)&(((struct align_##type *)0)->a)) - ((char*)0))
+
+int bpb;
+
+/* Used if shell doesn't support redirection. By default, assume it does. */
+FILE *stream;
+
+static int Log2(int n)
+{
+ int 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;
+}
+
+/* We assume that int's are 32 bits */
+static void do64(void)
+{
+ union {
+ int i;
+ char c[4];
+ } u;
+
+ u.i = 0x01020304;
+ if (u.c[0] == 0x01) {
+ fprintf(stream, "#undef IS_LITTLE_ENDIAN\n");
+ fprintf(stream, "#define IS_BIG_ENDIAN 1\n\n");
+ } else {
+ fprintf(stream, "#define IS_LITTLE_ENDIAN 1\n");
+ fprintf(stream, "#undef IS_BIG_ENDIAN\n\n");
+ }
+}
+
+static void do32(void)
+{
+ union {
+ long i;
+ char c[4];
+ } u;
+
+ u.i = 0x01020304;
+ if (u.c[0] == 0x01) {
+ fprintf(stream, "#undef IS_LITTLE_ENDIAN\n");
+ fprintf(stream, "#define IS_BIG_ENDIAN 1\n\n");
+ } else {
+ fprintf(stream, "#define IS_LITTLE_ENDIAN 1\n");
+ fprintf(stream, "#undef IS_BIG_ENDIAN\n\n");
+ }
+}
+
+/*
+** Concievably this could actually be used; but there is lots of code out
+** there with and's and shift's in it that assumes a byte is 8 bits, so
+** forget about porting THIS code to those non 8 bit byte machines.
+*/
+static void BitsPerByte(void)
+{
+ bpb = 8;
+}
+
+int main(int argc, char **argv)
+{
+ BitsPerByte();
+
+ /* If we got a command line argument, try to use it as the stream. */
+ ++argv;
+ if(*argv) {
+ if(!(stream = fopen ( *argv, "wt" ))) {
+ fprintf(stderr, "Could not write to output file %s.\n", *argv);
+ return 1;
+ }
+ } else {
+ stream = stdout;
+ }
+
+ fprintf(stream, "#ifndef nspr_cpucfg___\n");
+ fprintf(stream, "#define nspr_cpucfg___\n\n");
+
+ fprintf(stream, "/* AUTOMATICALLY GENERATED - DO NOT EDIT */\n\n");
+
+ if (sizeof(long) == 8) {
+ do64();
+ } else {
+ do32();
+ }
+ fprintf(stream, "#define PR_BYTES_PER_BYTE %d\n", sizeof(char));
+ fprintf(stream, "#define PR_BYTES_PER_SHORT %d\n", sizeof(short));
+ fprintf(stream, "#define PR_BYTES_PER_INT %d\n", sizeof(int));
+ fprintf(stream, "#define PR_BYTES_PER_INT64 %d\n", 8);
+ fprintf(stream, "#define PR_BYTES_PER_LONG %d\n", sizeof(long));
+ fprintf(stream, "#define PR_BYTES_PER_FLOAT %d\n", sizeof(float));
+ fprintf(stream, "#define PR_BYTES_PER_DOUBLE %d\n\n", sizeof(double));
+
+ fprintf(stream, "#define PR_BITS_PER_BYTE %d\n", bpb);
+ fprintf(stream, "#define PR_BITS_PER_SHORT %d\n", bpb * sizeof(short));
+ fprintf(stream, "#define PR_BITS_PER_INT %d\n", bpb * sizeof(int));
+ fprintf(stream, "#define PR_BITS_PER_INT64 %d\n", bpb * 8);
+ fprintf(stream, "#define PR_BITS_PER_LONG %d\n", bpb * sizeof(long));
+ fprintf(stream, "#define PR_BITS_PER_FLOAT %d\n", bpb * sizeof(float));
+ fprintf(stream, "#define PR_BITS_PER_DOUBLE %d\n\n",
+ bpb * sizeof(double));
+
+ fprintf(stream, "#define PR_BITS_PER_BYTE_LOG2 %d\n", Log2(bpb));
+ fprintf(stream, "#define PR_BITS_PER_SHORT_LOG2 %d\n",
+ Log2(bpb * sizeof(short)));
+ fprintf(stream, "#define PR_BITS_PER_INT_LOG2 %d\n",
+ Log2(bpb * sizeof(int)));
+ fprintf(stream, "#define PR_BITS_PER_INT64_LOG2 %d\n", 6);
+ fprintf(stream, "#define PR_BITS_PER_LONG_LOG2 %d\n",
+ Log2(bpb * sizeof(long)));
+ fprintf(stream, "#define PR_BITS_PER_FLOAT_LOG2 %d\n",
+ Log2(bpb * sizeof(float)));
+ fprintf(stream, "#define PR_BITS_PER_DOUBLE_LOG2 %d\n\n",
+ Log2(bpb * sizeof(double)));
+
+ fprintf(stream, "#define PR_ALIGN_OF_SHORT %d\n", ALIGN_OF(short));
+ fprintf(stream, "#define PR_ALIGN_OF_INT %d\n", ALIGN_OF(int));
+ fprintf(stream, "#define PR_ALIGN_OF_LONG %d\n", ALIGN_OF(long));
+ if (sizeof(INT64) < 8) {
+ /* this machine doesn't actually support PRInt64's */
+ fprintf(stream, "#define PR_ALIGN_OF_INT64 %d\n",
+ ALIGN_OF(fakelonglong));
+ } else {
+ fprintf(stream, "#define PR_ALIGN_OF_INT64 %d\n", ALIGN_OF(PRInt64));
+ }
+ fprintf(stream, "#define PR_ALIGN_OF_FLOAT %d\n", ALIGN_OF(float));
+ fprintf(stream, "#define PR_ALIGN_OF_DOUBLE %d\n", ALIGN_OF(double));
+ fprintf(stream, "#define PR_ALIGN_OF_POINTER %d\n\n", ALIGN_OF(pointer));
+
+ fprintf(stream, "#endif /* nspr_cpucfg___ */\n");
+ fclose(stream);
+
+ return 0;
+}
diff --git a/pr/include/md/Makefile b/pr/include/md/Makefile
new file mode 100644
index 00000000..a365ef91
--- /dev/null
+++ b/pr/include/md/Makefile
@@ -0,0 +1,148 @@
+#
+# 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 = ../../..
+
+HEADERS = $(wildcard *.h)
+
+include $(MOD_DEPTH)/config/rules.mk
+
+ifeq ($(OS_ARCH),IRIX)
+ifeq ($(USE_64), 1)
+MDCPUCFG_H = _irix64.cfg
+else
+MDCPUCFG_H = _irix32.cfg
+endif
+endif
+
+ifeq ($(OS_ARCH),WINNT)
+ifeq ($(OS_TARGET), WIN95)
+MDCPUCFG_H = _win95.cfg
+else
+ifeq ($(OS_TARGET), WIN16)
+MDCPUCFG_H = _win16.cfg
+else
+ifeq ($(OS_TARGET), OS2)
+MDCPUCFG_H = _os2.cfg
+else
+MDCPUCFG_H = _winnt.cfg
+endif
+endif
+endif
+endif
+
+ifeq ($(OS_ARCH),AIX)
+MDCPUCFG_H = _aix.cfg
+endif
+
+ifeq ($(OS_ARCH),BSD_OS)
+MDCPUCFG_H = _bsdi.cfg
+endif
+
+ifeq ($(OS_ARCH),FreeBSD)
+MDCPUCFG_H = _freebsd.cfg
+endif
+
+ifeq ($(OS_ARCH),OpenBSD)
+MDCPUCFG_H = _openbsd.cfg
+endif
+
+ifeq ($(OS_ARCH),NetBSD)
+MDCPUCFG_H = _netbsd.cfg
+endif
+
+ifeq ($(OS_ARCH),HP-UX)
+ifeq ($(USE_64), 1)
+MDCPUCFG_H = _hpux64.cfg
+else
+MDCPUCFG_H = _hpux32.cfg
+endif
+endif
+
+ifeq ($(OS_ARCH),Linux)
+MDCPUCFG_H = _linux.cfg
+endif
+
+ifeq ($(OS_ARCH),OSF1)
+MDCPUCFG_H = _osf1.cfg
+endif
+
+ifeq ($(OS_ARCH),SunOS)
+ifeq ($(OS_RELEASE),4.1.3_U1)
+MDCPUCFG_H = _sunos4.cfg
+else
+MDCPUCFG_H = _solaris.cfg
+endif
+endif
+
+ifeq ($(OS_ARCH),SINIX)
+MDCPUCFG_H = _reliantunix.cfg
+endif
+
+ifeq ($(OS_ARCH),Rhapsody)
+MDCPUCFG_H = _rhapsody.cfg
+endif
+
+ifeq ($(OS_ARCH),NEWS-OS)
+MDCPUCFG_H = _sony.cfg
+endif
+
+ifeq ($(OS_ARCH),NEC)
+MDCPUCFG_H = _nec.cfg
+endif
+
+ifeq ($(OS_ARCH),SCOOS)
+MDCPUCFG_H = _scoos.cfg
+endif
+
+ifeq ($(OS_ARCH),UNIXWARE)
+MDCPUCFG_H = _unixware.cfg
+endif
+
+ifeq ($(OS_ARCH),NCR)
+MDCPUCFG_H = _ncr.cfg
+endif
+
+ifeq ($(OS_ARCH),DGUX)
+MDCPUCFG_H = _dgux.cfg
+endif
+
+export:: $(HEADERS) $(MDCPUCFG_H)
+ $(INSTALL) -m 444 $(HEADERS) $(DIST)/include/md
+ $(INSTALL) -m 444 $(MDCPUCFG_H) $(DIST)/include
+ifeq ($(MOZ_BITS),16)
+ cp $(DIST)/include/$(MDCPUCFG_H) $(DIST)/include/prcpucfg.h
+ $(INSTALL) -m 444 $(HEADERS) $(MOZ_INCL)/md
+ $(INSTALL) -m 444 $(MDCPUCFG_H) $(MOZ_INCL)
+ mv -f $(DIST)/include/$(MDCPUCFG_H) $(MOZ_INCL)/prcpucfg.h
+else
+ mv -f $(DIST)/include/$(MDCPUCFG_H) $(DIST)/include/prcpucfg.h
+endif
+
+release:: export
+ @echo "Copying machine-dependent prcpucfg.h"
+ @if test -z "$(BUILD_NUMBER)"; then \
+ echo "BUILD_NUMBER must be defined"; \
+ false; \
+ fi
+ @if test ! -d $(RELEASE_INCLUDE_DIR); then \
+ rm -rf $(RELEASE_INCLUDE_DIR); \
+ $(NSINSTALL) -D $(RELEASE_INCLUDE_DIR);\
+ fi
+ cp $(MDCPUCFG_H) $(RELEASE_INCLUDE_DIR)/prcpucfg.h
diff --git a/pr/include/md/_aix.cfg b/pr/include/md/_aix.cfg
new file mode 100644
index 00000000..a8d26d89
--- /dev/null
+++ b/pr/include/md/_aix.cfg
@@ -0,0 +1,121 @@
+/* -*- 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_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef AIX
+#define AIX
+#endif
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#define HAVE_LONG_LONG
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#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/include/md/_aix.h b/pr/include/md/_aix.h
new file mode 100644
index 00000000..4ba958ea
--- /dev/null
+++ b/pr/include/md/_aix.h
@@ -0,0 +1,221 @@
+/* -*- 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_aix_defs_h___
+#define nspr_aix_defs_h___
+
+#include <sys/types.h>
+#if defined(_PR_PTHREADS) || defined(PTHREADS_USER)
+#include <pthread.h>
+#endif
+
+/*
+ * To pick up fd_set and the poll events.
+ */
+#include <sys/select.h>
+#include <sys/poll.h>
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "aix"
+#define _PR_SI_SYSNAME "AIX"
+#define _PR_SI_ARCHITECTURE "rs6000"
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#define NEED_TIME_R
+#undef HAVE_STACK_GROWING_UP
+#undef HAVE_WEAK_IO_SYMBOLS
+#undef HAVE_WEAK_MALLOC_SYMBOLS
+#define HAVE_DLL
+#define USE_DLFCN
+#define _PR_HAVE_SOCKADDR_LEN
+#define _PR_POLL_AVAILABLE
+#define _PR_USE_POLL
+#define _PR_STAT_HAS_ONLY_ST_ATIME
+
+/* Timer operations */
+#define AIX_TIMERS
+#if defined(AIX_TIMERS)
+extern PRIntervalTime _MD_AixGetInterval(void);
+#define _MD_GET_INTERVAL _MD_AixGetInterval
+
+extern PRIntervalTime _MD_AixIntervalPerSec(void);
+#define _MD_INTERVAL_PER_SEC _MD_AixIntervalPerSec
+
+#else /* defined(AIX_TIMERS) */
+#define _MD_GET_INTERVAL _PR_UNIX_GetInterval
+#define _MD_INTERVAL_PER_SEC _PR_UNIX_TicksPerSecond
+#endif /* defined(AIX_TIMERS) */
+
+/* The atomic operations */
+#include <sys/atomic_op.h>
+#define _PR_HAVE_ATOMIC_OPS
+#define _PR_HAVE_ATOMIC_CAS
+#define _MD_INIT_ATOMIC()
+#define _MD_ATOMIC_INCREMENT(val) ((PRInt32)fetch_and_add((atomic_p)val, 1) + 1)
+#define _MD_ATOMIC_ADD(ptr, val) ((PRInt32)fetch_and_add((atomic_p)ptr, val) + val)
+#define _MD_ATOMIC_DECREMENT(val) ((PRInt32)fetch_and_add((atomic_p)val, -1) - 1)
+#define _MD_ATOMIC_SET(val, newval) _AIX_AtomicSet(val, newval)
+
+#define USE_SETJMP
+
+#include <setjmp.h>
+
+#define _MD_GET_SP(_t) (_t)->md.jb[3]
+#define _MD_SET_THR_SP(_t, _sp) ((_t)->md.jb[3] = (int) (_sp - 2 * 64))
+#define PR_NUM_GCREGS _JBLEN
+
+#define CONTEXT(_th) ((_th)->md.jb)
+#define SAVE_CONTEXT(_th) _setjmp(CONTEXT(_th))
+#define GOTO_CONTEXT(_th) _longjmp(CONTEXT(_th), 1)
+
+#ifdef PTHREADS_USER
+#include "_nspr_pthread.h"
+#else
+
+/*
+** Initialize the thread context preparing it to execute _main.
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+ PR_BEGIN_MACRO \
+ *status = PR_TRUE; \
+ if (setjmp(CONTEXT(_thread))) { \
+ (*_main)(); \
+ } \
+ _MD_GET_SP(_thread) = (int) (_sp - 2 * 64); \
+ PR_END_MACRO
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!setjmp(CONTEXT(_thread))) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ longjmp(CONTEXT(_thread), 1); \
+}
+
+/* Machine-dependent (MD) data structures */
+
+struct _MDThread {
+ jmp_buf jb;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#if !defined(_PR_PTHREADS)
+#define _MD_INIT_LOCKS()
+#endif
+
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread)
+#define _MD_RESUME_THREAD(thread)
+#define _MD_CLEAN_THREAD(_thread)
+#endif /* PTHREADS_USER */
+
+#ifdef AIX4_1
+#define _MD_SELECT select
+#define _MD_POLL poll
+#endif
+
+#endif /* nspr_aix_defs_h___ */
diff --git a/pr/include/md/_bsdi.cfg b/pr/include/md/_bsdi.cfg
new file mode 100644
index 00000000..335baf4e
--- /dev/null
+++ b/pr/include/md/_bsdi.cfg
@@ -0,0 +1,121 @@
+/* -*- 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_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef BSDI
+#define BSDI
+#endif
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#undef HAVE_LONG_LONG
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#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/include/md/_bsdi.h b/pr/include/md/_bsdi.h
new file mode 100644
index 00000000..2639b2e3
--- /dev/null
+++ b/pr/include/md/_bsdi.h
@@ -0,0 +1,177 @@
+/* -*- 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_bsdi_defs_h___
+#define nspr_bsdi_defs_h___
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "bsdi"
+#define _PR_SI_SYSNAME "BSDI"
+#define _PR_SI_ARCHITECTURE "x86"
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#define HAVE_DLL
+#define USE_DLFCN
+#define HAVE_BSD_FLOCK
+#define NEED_TIME_R
+#define _PR_HAVE_SOCKADDR_LEN
+#define _PR_STAT_HAS_ST_ATIMESPEC
+#define _PR_NO_LARGE_FILES
+
+#if defined(BSDI_2)
+#define PROT_NONE 0x0
+#endif
+
+#define USE_SETJMP
+
+#include <setjmp.h>
+
+#if defined(BSDI_2)
+#define _MD_GET_SP(_t) (_t)->md.context[2]
+#else
+#define _MD_GET_SP(_t) (_t)->md.context[0].jb_esp
+#endif
+
+#define PR_NUM_GCREGS _JBLEN
+#define PR_CONTEXT_TYPE jmp_buf
+
+#define CONTEXT(_th) ((_th)->md.context)
+
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if (setjmp(CONTEXT(_thread))) { \
+ _main(); \
+ } \
+ _MD_GET_SP(_thread) = (int) (_sp - 64); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!setjmp(CONTEXT(_thread))) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ longjmp(CONTEXT(_thread), 1); \
+}
+
+/* Machine-dependent (MD) data structures */
+
+struct _MDThread {
+ PR_CONTEXT_TYPE context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_CLEAN_THREAD(_thread)
+
+#include <sys/syscall.h>
+#define _MD_SELECT(nfds,r,w,e,tv) syscall(SYS_select,nfds,r,w,e,tv)
+
+#define _MD_GET_INTERVAL _PR_UNIX_GetInterval
+#define _MD_INTERVAL_PER_SEC _PR_UNIX_TicksPerSecond
+
+#endif /* nspr_bsdi_defs_h___ */
diff --git a/pr/include/md/_darwin.cfg b/pr/include/md/_darwin.cfg
new file mode 100644
index 00000000..a0787fc9
--- /dev/null
+++ b/pr/include/md/_darwin.cfg
@@ -0,0 +1,127 @@
+/* -*- 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_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef RHAPOSDY
+#define RHAPOSDY
+#endif
+
+#if defined(i386)
+#undef IS_BIG_ENDIAN
+#define IS_LITTLE_ENDIAN 1
+#else
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#endif
+
+#define HAVE_LONG_LONG
+#undef HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+#define PR_BITS_PER_DWORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#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/include/md/_darwin.h b/pr/include/md/_darwin.h
new file mode 100644
index 00000000..476b8e38
--- /dev/null
+++ b/pr/include/md/_darwin.h
@@ -0,0 +1,205 @@
+/* -*- 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_rhapsody_defs_h___
+#define nspr_rhapsody_defs_h___
+
+#include "prthread.h"
+
+#include <sys/syscall.h>
+
+#define PR_LINKER_ARCH "rhapsody"
+#define _PR_SI_SYSNAME "RHAPSODY"
+#ifdef i386
+#define _PR_SI_ARCHITECTURE "x86"
+#else
+#define _PR_SI_ARCHITECTURE "ppc"
+#endif
+#define PR_DLL_SUFFIX ".dylib"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#undef HAVE_STACK_GROWING_UP
+#define HAVE_DLL
+#define _PR_HAVE_SOCKADDR_LEN
+#define _PR_STAT_HAS_ST_ATIMESPEC
+#define _PR_TIMESPEC_HAS_TS_SEC
+#define _PR_NO_LARGE_FILES
+
+#define USE_SETJMP
+
+#if !defined(_PR_PTHREADS)
+
+#include <setjmp.h>
+
+#define PR_CONTEXT_TYPE jmp_buf
+
+#define CONTEXT(_th) ((_th)->md.context)
+#define _MD_GET_SP(_th) (((struct sigcontext *) (_th)->md.context)->sc_onstack)
+#define PR_NUM_GCREGS _JBLEN
+
+/*
+** Initialize a thread context to run "_main()" when started
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if (setjmp(CONTEXT(_thread))) { \
+ _main(); \
+ } \
+ _MD_GET_SP(_thread) = (unsigned char*) ((_sp) - 64); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!setjmp(CONTEXT(_thread))) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ longjmp(CONTEXT(_thread), 1); \
+}
+
+/* Machine-dependent (MD) data structures */
+
+struct _MDThread {
+ PR_CONTEXT_TYPE context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+extern PRStatus _MD_InitializeThread(PRThread *thread);
+
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread) _MD_suspend_thread
+#define _MD_RESUME_THREAD(thread) _MD_resume_thread
+#define _MD_CLEAN_THREAD(_thread)
+
+extern PRStatus _MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize);
+extern void _MD_SET_PRIORITY(struct _MDThread *thread, PRUintn newPri);
+extern PRStatus _MD_WAIT(PRThread *, PRIntervalTime timeout);
+extern PRStatus _MD_WAKEUP_WAITER(PRThread *);
+extern void _MD_YIELD(void);
+
+#endif /* ! _PR_PTHREADS */
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_GET_INTERVAL _PR_UNIX_GetInterval
+#define _MD_INTERVAL_PER_SEC _PR_UNIX_TicksPerSecond
+
+extern void _MD_EarlyInit(void);
+extern PRIntervalTime _PR_UNIX_GetInterval(void);
+extern PRIntervalTime _PR_UNIX_TicksPerSecond(void);
+
+/*
+ * We wrapped the select() call. _MD_SELECT refers to the built-in,
+ * unwrapped version.
+ */
+#define _MD_SELECT(nfds,r,w,e,tv) syscall(SYS_select,nfds,r,w,e,tv)
+
+/* For writev() */
+#include <sys/uio.h>
+
+#endif /* nspr_rhapsody_defs_h___ */
diff --git a/pr/include/md/_dgux.cfg b/pr/include/md/_dgux.cfg
new file mode 100644
index 00000000..4cde625a
--- /dev/null
+++ b/pr/include/md/_dgux.cfg
@@ -0,0 +1,119 @@
+/* -*- 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_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef DGUX
+#define DGUX
+#endif
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define HAVE_LONG_LONG
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#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/include/md/_dgux.h b/pr/include/md/_dgux.h
new file mode 100644
index 00000000..ba8497bd
--- /dev/null
+++ b/pr/include/md/_dgux.h
@@ -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.
+ */
+
+#ifndef nspr_dgux_defs_h___
+#define nspr_dgux_defs_h___
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "dgux"
+#define _PR_SI_SYSNAME "DGUX"
+#define _PR_SI_ARCHITECTURE "x86"
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#ifndef HAVE_WEAK_IO_SYMBOLS
+#define HAVE_WEAK_IO_SYMBOLS
+#endif
+
+#undef HAVE_STACK_GROWING_UP
+#define HAVE_NETCONFIG
+#define HAVE_DLL
+#define USE_DLFCN
+#define NEED_STRFTIME_LOCK
+#define NEED_TIME_R
+#define _PR_NEED_STRCASECMP
+#define _PR_POLL_AVAILABLE
+#define _PR_USE_POLL
+#define _PR_NO_LARGE_FILES
+#define _PR_STAT_HAS_ONLY_ST_ATIME
+
+#define USE_SETJMP
+
+#include <setjmp.h>
+
+#define _SETJMP setjmp
+#define _LONGJMP longjmp
+#define _PR_CONTEXT_TYPE jmp_buf
+#define _MD_GET_SP(_t) (_t)->md.context[4]
+#define _PR_NUM_GCREGS _JBLEN
+
+#define CONTEXT(_th) ((_th)->md.context)
+
+/*
+** Initialize the thread context preparing it to execute _main.
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if(_SETJMP(CONTEXT(_thread))) (*_main)(); \
+ _MD_GET_SP(_thread) = (int) ((_sp) - 128); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!_SETJMP(CONTEXT(_thread))) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ _LONGJMP(CONTEXT(_thread), 1); \
+}
+
+/* Machine-dependent (MD) data structures.
+ * Don't use SVR4 native threads (yet).
+ */
+
+struct _MDThread {
+ _PR_CONTEXT_TYPE context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+/*
+ * The following are copied from _sunos.h, _aix.h. This means
+ * some of them should probably be moved into _unixos.h. But
+ * _irix.h seems to be quite different in regard to these macros.
+ */
+#define _MD_GET_INTERVAL _PR_UNIX_GetInterval
+#define _MD_INTERVAL_PER_SEC _PR_UNIX_TicksPerSecond
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread)
+#define _MD_RESUME_THREAD(thread)
+#define _MD_CLEAN_THREAD(_thread)
+
+/*
+ * We wrapped the select() call. _MD_SELECT refers to the built-in,
+ * unwrapped version.
+ */
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+extern int _select(int nfds, fd_set *readfds, fd_set *writefds,
+ fd_set *execptfds, struct timeval *timeout);
+#define _MD_SELECT _select
+
+#define _MD_POLL _poll
+#include <poll.h>
+#include <stropts.h>
+extern int _poll(struct pollfd *fds, unsigned long nfds, int timeout);
+
+#endif /* nspr_dgux_defs_h___ */
diff --git a/pr/include/md/_freebsd.cfg b/pr/include/md/_freebsd.cfg
new file mode 100644
index 00000000..d5cdeef3
--- /dev/null
+++ b/pr/include/md/_freebsd.cfg
@@ -0,0 +1,119 @@
+/* -*- 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_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef FREEBSD
+#define FREEBSD
+#endif
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define HAVE_LONG_LONG
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#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/include/md/_freebsd.h b/pr/include/md/_freebsd.h
new file mode 100644
index 00000000..6e459fb5
--- /dev/null
+++ b/pr/include/md/_freebsd.h
@@ -0,0 +1,207 @@
+/* -*- 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_freebsd_defs_h___
+#define nspr_freebsd_defs_h___
+
+#include "prthread.h"
+
+#include <sys/syscall.h>
+
+#define PR_LINKER_ARCH "freebsd"
+#define _PR_SI_SYSNAME "FREEBSD"
+#define _PR_SI_ARCHITECTURE "x86"
+#define PR_DLL_SUFFIX ".so.1.0"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#undef HAVE_STACK_GROWING_UP
+#define HAVE_DLL
+#define USE_DLFCN
+#define _PR_HAVE_SOCKADDR_LEN
+#define _PR_STAT_HAS_ST_ATIMESPEC
+#define _PR_NO_LARGE_FILES
+
+#define USE_SETJMP
+
+#ifndef _PR_PTHREADS
+#include <setjmp.h>
+
+#define PR_CONTEXT_TYPE sigjmp_buf
+
+#define CONTEXT(_th) ((_th)->md.context)
+
+#define _MD_GET_SP(_th) (_th)->md.context[0]._sjb[2]
+#define PR_NUM_GCREGS _JBLEN
+
+/*
+** Initialize a thread context to run "_main()" when started
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if (sigsetjmp(CONTEXT(_thread), 1)) { \
+ _main(); \
+ } \
+ _MD_GET_SP(_thread) = (unsigned char*) ((_sp) - 64); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!sigsetjmp(CONTEXT(_thread), 1)) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ siglongjmp(CONTEXT(_thread), 1); \
+}
+
+/* Machine-dependent (MD) data structures */
+
+struct _MDThread {
+ PR_CONTEXT_TYPE context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+extern PRStatus _MD_InitializeThread(PRThread *thread);
+
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread) _MD_suspend_thread
+#define _MD_RESUME_THREAD(thread) _MD_resume_thread
+#define _MD_CLEAN_THREAD(_thread)
+
+extern PRStatus _MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize);
+extern void _MD_SET_PRIORITY(struct _MDThread *thread, PRUintn newPri);
+extern PRStatus _MD_WAIT(PRThread *, PRIntervalTime timeout);
+extern PRStatus _MD_WAKEUP_WAITER(PRThread *);
+extern void _MD_YIELD(void);
+
+#endif /* ! _PR_PTHREADS */
+
+extern void _MD_EarlyInit(void);
+extern PRIntervalTime _PR_UNIX_GetInterval(void);
+extern PRIntervalTime _PR_UNIX_TicksPerSecond(void);
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_GET_INTERVAL _PR_UNIX_GetInterval
+#define _MD_INTERVAL_PER_SEC _PR_UNIX_TicksPerSecond
+
+/*
+ * We wrapped the select() call. _MD_SELECT refers to the built-in,
+ * unwrapped version.
+ */
+#define _MD_SELECT(nfds,r,w,e,tv) syscall(SYS_select,nfds,r,w,e,tv)
+
+/* freebsd has INADDR_LOOPBACK defined, but in /usr/include/rpc/types.h, and I didn't
+ want to be including that.. */
+#ifndef INADDR_LOOPBACK
+#define INADDR_LOOPBACK (u_long)0x7F000001
+#endif
+
+/* For writev() */
+#include <sys/uio.h>
+
+#endif /* nspr_freebsd_defs_h___ */
diff --git a/pr/include/md/_hpux.h b/pr/include/md/_hpux.h
new file mode 100644
index 00000000..ac296da4
--- /dev/null
+++ b/pr/include/md/_hpux.h
@@ -0,0 +1,221 @@
+/* -*- 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_xhppa_defs_h___
+#define nspr_xhppa_defs_h___
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "hpux"
+#define _PR_SI_SYSNAME "HPUX"
+#define _PR_SI_ARCHITECTURE "hppa1.1"
+#define PR_DLL_SUFFIX ".sl"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#define NEED_TIME_R
+
+#define HAVE_STACK_GROWING_UP
+#undef HAVE_WEAK_IO_SYMBOLS
+#undef HAVE_WEAK_MALLOC_SYMBOLS
+#define HAVE_DLL
+#define USE_HPSHL
+#ifndef HAVE_STRERROR
+#define HAVE_STRERROR
+#endif
+#define _PR_POLL_AVAILABLE
+#define _PR_USE_POLL
+#define _PR_STAT_HAS_ONLY_ST_ATIME
+
+#undef _PR_HAVE_ATOMIC_OPS
+
+#if !defined(_PR_PTHREADS)
+
+#include <syscall.h>
+#include <setjmp.h>
+
+#define USE_SETJMP
+
+#define _MD_GET_SP(_t) (*((int *)((_t)->md.jb) + 1))
+#define PR_NUM_GCREGS _JBLEN
+/* Caveat: This makes jmp_buf full of doubles. */
+#define CONTEXT(_th) ((_th)->md.jb)
+
+ /* Stack needs two frames (64 bytes) at the bottom */ \
+#define _MD_SET_THR_SP(_t, _sp) ((_MD_GET_SP(_t)) = (int) (_sp + 64 *2))
+#define SAVE_CONTEXT(_th) _setjmp(CONTEXT(_th))
+#define GOTO_CONTEXT(_th) _longjmp(CONTEXT(_th), 1)
+
+#if !defined(PTHREADS_USER)
+
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *(status) = PR_TRUE; \
+ if (_setjmp(CONTEXT(_thread))) (*_main)(); \
+ /* Stack needs two frames (64 bytes) at the bottom */ \
+ (_MD_GET_SP(_thread)) = (int) ((_sp) + 64*2); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!_setjmp(CONTEXT(_thread))) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ _longjmp(CONTEXT(_thread), 1); \
+}
+
+/* Machine-dependent (MD) data structures. HP-UX has no native threads. */
+
+struct _MDThread {
+ jmp_buf jb;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread) _MD_suspend_thread
+#define _MD_RESUME_THREAD(thread) _MD_resume_thread
+#define _MD_CLEAN_THREAD(_thread)
+
+#else /* PTHREADS_USER */
+
+#include "_nspr_pthread.h"
+
+#endif /* PTHREADS_USER */
+
+#endif /* !defined(_PR_PTHREADS) */
+
+#if !defined(PTHREADS_USER)
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#endif
+
+#if defined(HPUX_LW_TIMER)
+extern void _PR_HPUX_LW_IntervalInit(void);
+extern PRIntervalTime _PR_HPUX_LW_GetInterval(void);
+#define _MD_INTERVAL_INIT _PR_HPUX_LW_IntervalInit
+#define _MD_GET_INTERVAL _PR_HPUX_LW_GetInterval
+#define _MD_INTERVAL_PER_SEC() 1000
+#else
+#define _MD_GET_INTERVAL _PR_UNIX_GetInterval
+#define _MD_INTERVAL_PER_SEC _PR_UNIX_TicksPerSecond
+#endif
+
+/*
+ * We wrapped the select() call. _MD_SELECT refers to the built-in,
+ * unwrapped version.
+ */
+#define _MD_SELECT(nfds,r,w,e,tv) syscall(SYS_select,nfds,r,w,e,tv)
+
+#include <poll.h>
+#define _MD_POLL(fds,nfds,timeout) syscall(SYS_poll,fds,nfds,timeout)
+
+#ifdef HPUX11
+extern void _MD_hpux_map_sendfile_error(int err);
+#if !defined(_PR_PTHREADS)
+extern PRInt32 _PR_HPUXTransmitFile(PRFileDesc *sd, PRFileDesc *fd,
+ const void *headers, PRInt32 hlen,
+ PRTransmitFileFlags flags, PRIntervalTime timeout);
+#endif /* !_PR_PTHREADS */
+#endif /* HPUX11 */
+
+#endif /* nspr_xhppa_defs_h___ */
diff --git a/pr/include/md/_hpux32.cfg b/pr/include/md/_hpux32.cfg
new file mode 100644
index 00000000..2889f693
--- /dev/null
+++ b/pr/include/md/_hpux32.cfg
@@ -0,0 +1,121 @@
+/* -*- 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_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef HPUX
+#define HPUX
+#endif
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+
+#define HAVE_LONG_LONG
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+
+#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/include/md/_hpux64.cfg b/pr/include/md/_hpux64.cfg
new file mode 100644
index 00000000..f790215c
--- /dev/null
+++ b/pr/include/md/_hpux64.cfg
@@ -0,0 +1,122 @@
+/* -*- 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_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef HPUX
+#define HPUX
+#endif
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#define HAVE_LONG_LONG
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+
+#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/include/md/_irix.h b/pr/include/md/_irix.h
new file mode 100644
index 00000000..b6b2b034
--- /dev/null
+++ b/pr/include/md/_irix.h
@@ -0,0 +1,442 @@
+/* -*- 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_irix_defs_h___
+#define nspr_irix_defs_h___
+
+#define _PR_HAVE_ATOMIC_CAS
+
+/*
+ * MipsPro assembler defines _LANGUAGE_ASSEMBLY
+ */
+#ifndef _LANGUAGE_ASSEMBLY
+
+#include "prclist.h"
+#include "prthread.h"
+#include <sys/ucontext.h>
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "irix"
+#define _PR_SI_SYSNAME "IRIX"
+#define _PR_SI_ARCHITECTURE "mips"
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _PR_NUM_GCREGS 9
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MIN_STACK_SIZE 16384L
+
+#undef HAVE_STACK_GROWING_UP
+#define HAVE_WEAK_IO_SYMBOLS
+#define HAVE_WEAK_MALLOC_SYMBOLS
+#define HAVE_DLL
+#define USE_DLFCN
+#define _PR_HAVE_ATOMIC_OPS
+#define _PR_POLL_AVAILABLE
+#define _PR_USE_POLL
+#define _PR_STAT_HAS_ST_ATIM
+#define _PR_HAVE_OFF64_T
+#define HAVE_POINTER_LOCALTIME_R
+
+/* Initialization entry points */
+PR_EXTERN(void) _MD_EarlyInit(void);
+#define _MD_EARLY_INIT _MD_EarlyInit
+
+PR_EXTERN(void) _MD_IrixInit(void);
+#define _MD_FINAL_INIT _MD_IrixInit
+
+#define _MD_INIT_IO()
+
+/* Timer operations */
+PR_EXTERN(PRIntervalTime) _MD_IrixGetInterval(void);
+#define _MD_GET_INTERVAL _MD_IrixGetInterval
+
+PR_EXTERN(PRIntervalTime) _MD_IrixIntervalPerSec(void);
+#define _MD_INTERVAL_PER_SEC _MD_IrixIntervalPerSec
+
+/* GC operations */
+PR_EXTERN(void *) _MD_GetSP(PRThread *thread);
+#define _MD_GET_SP _MD_GetSP
+
+/* The atomic operations */
+#include <mutex.h>
+#define _MD_INIT_ATOMIC()
+#define _MD_ATOMIC_INCREMENT(val) add_then_test((unsigned long*)val, 1)
+#define _MD_ATOMIC_ADD(ptr, val) add_then_test((unsigned long*)ptr, (unsigned long)val)
+#define _MD_ATOMIC_DECREMENT(val) add_then_test((unsigned long*)val, 0xffffffff)
+#define _MD_ATOMIC_SET(val, newval) test_and_set((unsigned long*)val, newval)
+
+#if defined(_PR_PTHREADS)
+#else /* defined(_PR_PTHREADS) */
+
+/************************************************************************/
+
+#include <setjmp.h>
+#include <errno.h>
+#include <unistd.h>
+#include <bstring.h>
+#include <sys/time.h>
+#include <ulocks.h>
+#include <sys/prctl.h>
+
+
+/*
+ * Data region private to each sproc. This region is setup by calling
+ * mmap(...,MAP_LOCAL,...). The private data is mapped at the same
+ * address in every sproc, but every sproc gets a private mapping.
+ *
+ * Just make sure that this structure fits in a page, as only one page
+ * is allocated for the private region.
+ */
+struct sproc_private_data {
+ struct PRThread *me;
+ struct _PRCPU *cpu;
+ struct PRThread *last;
+ PRUintn intsOff;
+ int sproc_pid;
+};
+
+extern char *_nspr_sproc_private;
+
+#define _PR_PRDA() ((struct sproc_private_data *) _nspr_sproc_private)
+#define _MD_SET_CURRENT_THREAD(_thread) _PR_PRDA()->me = (_thread)
+#define _MD_THIS_THREAD() (_PR_PRDA()->me)
+#define _MD_LAST_THREAD() (_PR_PRDA()->last)
+#define _MD_SET_LAST_THREAD(_thread) _PR_PRDA()->last = (_thread)
+#define _MD_CURRENT_CPU() (_PR_PRDA()->cpu)
+#define _MD_SET_CURRENT_CPU(_cpu) _PR_PRDA()->cpu = (_cpu)
+#define _MD_SET_INTSOFF(_val) (_PR_PRDA()->intsOff = _val)
+#define _MD_GET_INTSOFF() (_PR_PRDA()->intsOff)
+
+#define _MD_SET_SPROC_PID(_val) (_PR_PRDA()->sproc_pid = _val)
+#define _MD_GET_SPROC_PID() (_PR_PRDA()->sproc_pid)
+
+PR_EXTERN(struct PRThread*) _MD_get_attached_thread(void);
+PR_EXTERN(struct PRThread*) _MD_get_current_thread(void);
+#define _MD_GET_ATTACHED_THREAD() _MD_get_attached_thread()
+#define _MD_CURRENT_THREAD() _MD_get_current_thread()
+
+#define _MD_CHECK_FOR_EXIT() { \
+ if (_pr_irix_exit_now) { \
+ _PR_POST_SEM(_pr_irix_exit_sem); \
+ _MD_Wakeup_CPUs(); \
+ _exit(0); \
+ } \
+ }
+
+#define _MD_ATTACH_THREAD(threadp)
+
+#define _MD_SAVE_ERRNO(_thread) (_thread)->md.errcode = errno;
+#define _MD_RESTORE_ERRNO(_thread) errno = (_thread)->md.errcode;
+
+extern struct _PRCPU *_pr_primordialCPU;
+extern usema_t *_pr_irix_exit_sem;
+extern PRInt32 _pr_irix_exit_now;
+extern int _pr_irix_primoridal_cpu_fd[];
+extern PRInt32 _pr_irix_process_exit;
+extern PRInt32 _pr_irix_process_exit_code;
+
+/* Thread operations */
+#define _PR_LOCK_HEAP() { \
+ PRIntn _is; \
+ if (_pr_primordialCPU) { \
+ if (_MD_GET_ATTACHED_THREAD() && \
+ !_PR_IS_NATIVE_THREAD( \
+ _MD_GET_ATTACHED_THREAD())) \
+ _PR_INTSOFF(_is); \
+ _PR_LOCK(_pr_heapLock); \
+ }
+
+#define _PR_UNLOCK_HEAP() if (_pr_primordialCPU) { \
+ _PR_UNLOCK(_pr_heapLock); \
+ if (_MD_GET_ATTACHED_THREAD() && \
+ !_PR_IS_NATIVE_THREAD( \
+ _MD_GET_ATTACHED_THREAD())) \
+ _PR_INTSON(_is); \
+ } \
+ }
+
+#define _PR_OPEN_POLL_SEM(_sem) usopenpollsema(_sem, 0666)
+#define _PR_WAIT_SEM(_sem) uspsema(_sem)
+#define _PR_POST_SEM(_sem) usvsema(_sem)
+
+#define _MD_CVAR_POST_SEM(threadp) usvsema((threadp)->md.cvar_pollsem)
+
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+struct _MDLock {
+ ulock_t lock;
+ usptr_t *arena;
+};
+
+/*
+ * disable pre-emption for the LOCAL threads when calling the arena lock
+ * routines
+ */
+
+#define _PR_LOCK(lock) { \
+ PRIntn _is; \
+ PRThread *me = _MD_GET_ATTACHED_THREAD(); \
+ if (me && !_PR_IS_NATIVE_THREAD(me)) \
+ _PR_INTSOFF(_is); \
+ ussetlock(lock); \
+ if (me && !_PR_IS_NATIVE_THREAD(me)) \
+ _PR_FAST_INTSON(_is); \
+ }
+
+#define _PR_UNLOCK(lock) { \
+ PRIntn _is; \
+ PRThread *me = _MD_GET_ATTACHED_THREAD(); \
+ if (me && !_PR_IS_NATIVE_THREAD(me)) \
+ _PR_INTSOFF(_is); \
+ usunsetlock(lock); \
+ if (me && !_PR_IS_NATIVE_THREAD(me)) \
+ _PR_FAST_INTSON(_is); \
+ }
+
+PR_EXTERN(PRStatus) _MD_NEW_LOCK(struct _MDLock *md);
+PR_EXTERN(void) _MD_FREE_LOCK(struct _MDLock *lockp);
+
+#define _MD_LOCK(_lockp) _PR_LOCK((_lockp)->lock)
+#define _MD_UNLOCK(_lockp) _PR_UNLOCK((_lockp)->lock)
+#define _MD_TEST_AND_LOCK(_lockp) uscsetlock((_lockp)->lock, 1)
+
+extern ulock_t _pr_heapLock;
+
+struct _MDThread {
+ jmp_buf jb;
+ usptr_t *pollsem_arena;
+ usema_t *cvar_pollsem;
+ PRInt32 cvar_pollsemfd;
+ PRInt32 cvar_pollsem_select; /* acquire sem by calling select */
+ PRInt32 cvar_wait; /* if 1, thread is waiting on cvar Q */
+ PRInt32 id;
+ PRInt32 suspending_id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ usema_t *sem;
+};
+
+struct _MDCVar {
+ ulock_t mdcvar_lock;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+
+struct _MDCPU {
+ PRInt32 id;
+ PRInt32 suspending_id;
+ struct _MDCPU_Unix md_unix;
+};
+
+/*
+** Initialize the thread context preparing it to execute _main.
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+ PR_BEGIN_MACRO \
+ int *jb = (_thread)->md.jb; \
+ *status = PR_TRUE; \
+ (void) setjmp(jb); \
+ (_thread)->md.jb[JB_SP] = (int) ((_sp) - 64); \
+ (_thread)->md.jb[JB_PC] = (int) _main; \
+ _thread->no_sched = 0; \
+ PR_END_MACRO
+
+/*
+** Switch away from the current thread context by saving its state and
+** calling the thread scheduler. Reload cpu when we come back from the
+** context switch because it might have changed.
+*
+* XXX RUNQ lock needed before clearing _PR_NO_SCHED flag, because the
+* thread may be unr RUNQ?
+*/
+#define _MD_SWITCH_CONTEXT(_thread) \
+ PR_BEGIN_MACRO \
+ PR_ASSERT(_thread->no_sched); \
+ if (!setjmp(_thread->md.jb)) { \
+ _MD_SAVE_ERRNO(_thread) \
+ _MD_SET_LAST_THREAD(_thread); \
+ _PR_Schedule(); \
+ } else { \
+ PR_ASSERT(_MD_LAST_THREAD() !=_MD_CURRENT_THREAD()); \
+ _MD_LAST_THREAD()->no_sched = 0; \
+ } \
+ PR_END_MACRO
+
+/*
+** Restore a thread context that was saved by _MD_SWITCH_CONTEXT or
+** initialized by _MD_INIT_CONTEXT.
+*/
+#define _MD_RESTORE_CONTEXT(_newThread) \
+ PR_BEGIN_MACRO \
+ int *jb = (_newThread)->md.jb; \
+ _MD_RESTORE_ERRNO(_newThread) \
+ _MD_SET_CURRENT_THREAD(_newThread); \
+ _newThread->no_sched = 1; \
+ longjmp(jb, 1); \
+ PR_END_MACRO
+
+PR_EXTERN(PRStatus) _MD_InitThread(struct PRThread *thread,
+ PRBool wakeup_parent);
+PR_EXTERN(PRStatus) _MD_InitAttachedThread(struct PRThread *thread,
+ PRBool wakeup_parent);
+#define _MD_INIT_THREAD(thread) _MD_InitThread(thread, PR_TRUE)
+#define _MD_INIT_ATTACHED_THREAD(thread) \
+ _MD_InitAttachedThread(thread, PR_FALSE)
+
+PR_EXTERN(void) _MD_ExitThread(struct PRThread *thread);
+#define _MD_EXIT_THREAD _MD_ExitThread
+
+PR_EXTERN(void) _MD_SuspendThread(struct PRThread *thread);
+#define _MD_SUSPEND_THREAD _MD_SuspendThread
+
+PR_EXTERN(void) _MD_ResumeThread(struct PRThread *thread);
+#define _MD_RESUME_THREAD _MD_ResumeThread
+
+PR_EXTERN(void) _MD_SuspendCPU(struct _PRCPU *thread);
+#define _MD_SUSPEND_CPU _MD_SuspendCPU
+
+PR_EXTERN(void) _MD_ResumeCPU(struct _PRCPU *thread);
+#define _MD_RESUME_CPU _MD_ResumeCPU
+
+#define _MD_BEGIN_SUSPEND_ALL()
+#define _MD_END_SUSPEND_ALL()
+#define _MD_BEGIN_RESUME_ALL()
+#define _MD_END_RESUME_ALL()
+
+PR_EXTERN(void) _MD_InitLocks(void);
+#define _MD_INIT_LOCKS _MD_InitLocks
+
+PR_EXTERN(void) _MD_CleanThread(struct PRThread *thread);
+#define _MD_CLEAN_THREAD _MD_CleanThread
+
+#define _MD_INIT_PRIMORDIAL_THREAD(threadp)
+#define _MD_YIELD() sginap(0)
+
+/* The _PR_MD_WAIT_LOCK and _PR_MD_WAKEUP_WAITER functions put to sleep and
+ * awaken a thread which is waiting on a lock or cvar.
+ */
+PR_EXTERN(PRStatus) _MD_wait(struct PRThread *, PRIntervalTime timeout);
+#define _MD_WAIT _MD_wait
+
+PR_EXTERN(void) _PR_MD_primordial_cpu();
+PR_EXTERN(void) _PR_MD_WAKEUP_PRIMORDIAL_CPU();
+
+PR_EXTERN(PRStatus) _MD_WakeupWaiter(struct PRThread *);
+#define _MD_WAKEUP_WAITER _MD_WakeupWaiter
+
+PR_EXTERN(void ) _MD_exit(PRIntn status);
+#define _MD_EXIT _MD_exit
+
+#include "prthread.h"
+
+PR_EXTERN(void) _MD_SetPriority(struct _MDThread *thread,
+ PRThreadPriority newPri);
+#define _MD_SET_PRIORITY _MD_SetPriority
+
+PR_EXTERN(PRStatus) _MD_CreateThread(
+ struct PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize);
+#define _MD_CREATE_THREAD _MD_CreateThread
+
+extern void _MD_CleanupBeforeExit(void);
+#define _MD_CLEANUP_BEFORE_EXIT _MD_CleanupBeforeExit
+
+PR_EXTERN(void) _PR_MD_PRE_CLEANUP(PRThread *me);
+
+
+/* The following defines the unwrapped versions of select() and poll(). */
+extern int _select(int nfds, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds, struct timeval *timeout);
+#define _MD_SELECT _select
+
+#include <stropts.h>
+#include <poll.h>
+#define _MD_POLL _poll
+extern int _poll(struct pollfd *fds, unsigned long nfds, int timeout);
+
+
+#define HAVE_THREAD_AFFINITY 1
+
+PR_EXTERN(PRInt32) _MD_GetThreadAffinityMask(PRThread *unused, PRUint32 *mask);
+#define _MD_GETTHREADAFFINITYMASK _MD_GetThreadAffinityMask
+
+PR_EXTERN(void) _MD_InitRunningCPU(struct _PRCPU *cpu);
+#define _MD_INIT_RUNNING_CPU _MD_InitRunningCPU
+
+#endif /* defined(_PR_PTHREADS) */
+
+#endif /* _LANGUAGE_ASSEMBLY */
+
+#endif /* nspr_irix_defs_h___ */
diff --git a/pr/include/md/_irix32.cfg b/pr/include/md/_irix32.cfg
new file mode 100644
index 00000000..c5016880
--- /dev/null
+++ b/pr/include/md/_irix32.cfg
@@ -0,0 +1,128 @@
+/* -*- 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 _SGI_MP_SOURCE
+#define _SGI_MP_SOURCE
+#endif
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef IRIX
+#define IRIX
+#endif
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define HAVE_LONG_LONG
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+
+#define _PR_POLL_BACKCOMPAT
+
+#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/include/md/_irix64.cfg b/pr/include/md/_irix64.cfg
new file mode 100644
index 00000000..7df5f997
--- /dev/null
+++ b/pr/include/md/_irix64.cfg
@@ -0,0 +1,127 @@
+/* -*- 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 _SGI_MP_SOURCE
+#define _SGI_MP_SOURCE
+#endif
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef IRIX
+#define IRIX
+#endif
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define HAVE_LONG_LONG
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+
+#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/include/md/_linux.cfg b/pr/include/md/_linux.cfg
new file mode 100644
index 00000000..284ed7d5
--- /dev/null
+++ b/pr/include/md/_linux.cfg
@@ -0,0 +1,404 @@
+/* -*- 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_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef LINUX
+#define LINUX
+#endif
+
+#ifdef __powerpc__
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__alpha)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__mc68000__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 2
+#define PR_ALIGN_OF_LONG 2
+#define PR_ALIGN_OF_INT64 2
+#define PR_ALIGN_OF_FLOAT 2
+#define PR_ALIGN_OF_DOUBLE 2
+#define PR_ALIGN_OF_POINTER 2
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__sparc__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__i386__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__mips__)
+
+#ifdef __MIPSEB__
+#define IS_BIG_ENDIAN 1
+#undef IS_LITTLE_ENDIAN
+#elif defined(__MIPSEL__)
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#else
+#error "Unknown MIPS endianness."
+#endif
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__arm__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#else
+
+#error "Unknown CPU architecture"
+
+#endif
+
+#define HAVE_LONG_LONG
+/*
+ * XXX These two macros need to be investigated for different architectures.
+ */
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#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/include/md/_linux.h b/pr/include/md/_linux.h
new file mode 100644
index 00000000..211ecaad
--- /dev/null
+++ b/pr/include/md/_linux.h
@@ -0,0 +1,412 @@
+/* -*- 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_linux_defs_h___
+#define nspr_linux_defs_h___
+
+#include "prthread.h"
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "linux"
+#define _PR_SI_SYSNAME "LINUX"
+#ifdef __powerpc__
+#define _PR_SI_ARCHITECTURE "ppc"
+#elif defined(__alpha)
+#define _PR_SI_ARCHITECTURE "alpha"
+#elif defined(__mc68000__)
+#define _PR_SI_ARCHITECTURE "m68k"
+#elif defined(__sparc__)
+#define _PR_SI_ARCHITECTURE "sparc"
+#elif defined(__i386__)
+#define _PR_SI_ARCHITECTURE "x86"
+#elif defined(__mips__)
+#define _PR_SI_ARCHITECTURE "mips"
+#elif defined(__arm__)
+#define _PR_SI_ARCHITECTURE "arm"
+#else
+#error "Unknown CPU architecture"
+#endif
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#undef HAVE_STACK_GROWING_UP
+
+/*
+ * Elf linux supports dl* functions
+ */
+#define HAVE_DLL
+#define USE_DLFCN
+
+#define USE_SETJMP
+#if defined(__GLIBC__) && __GLIBC__ >= 2
+#define _PR_POLL_AVAILABLE
+#endif
+#undef _PR_USE_POLL
+#define _PR_STAT_HAS_ONLY_ST_ATIME
+#if defined(__alpha)
+#define _PR_HAVE_LARGE_OFF_T
+#else
+#define _PR_NO_LARGE_FILES
+#endif
+
+#ifdef _PR_PTHREADS
+
+extern void _MD_CleanupBeforeExit(void);
+#define _MD_CLEANUP_BEFORE_EXIT _MD_CleanupBeforeExit
+
+#else /* ! _PR_PTHREADS */
+
+#include <setjmp.h>
+
+#define PR_CONTEXT_TYPE sigjmp_buf
+
+#define CONTEXT(_th) ((_th)->md.context)
+
+#ifdef __powerpc__
+/*
+ * PowerPC based MkLinux
+ *
+ * On the PowerPC, the new style jmp_buf isn't used until glibc
+ * 2.1.
+ */
+#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[JB_GPR1]
+#else
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__misc[0]
+#endif /* __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1 */
+#define _MD_SET_FP(_t, val)
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) ((void *) 0)
+/* aix = 64, macos = 70 */
+#define PR_NUM_GCREGS 64
+
+#elif defined(__alpha)
+/* Alpha based Linux */
+
+#if defined(__GLIBC__) && __GLIBC__ >= 2
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[JB_SP]
+#define _MD_SET_FP(_t, val)
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) ((void *) 0)
+#define _MD_SP_TYPE long int
+#else
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__sp
+#define _MD_SET_FP(_t, val)
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) ((void *) 0)
+#define _MD_SP_TYPE __ptr_t
+#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */
+
+/* XXX not sure if this is correct, or maybe it should be 17? */
+#define PR_NUM_GCREGS 9
+
+#elif defined(__mc68000__)
+/* m68k based Linux */
+
+/*
+ * On the m68k, glibc still uses the old style sigjmp_buf, even
+ * in glibc 2.0.7.
+ */
+#if defined(__GLIBC__) && __GLIBC__ >= 2
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__sp
+#define _MD_SET_FP(_t, val)
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) ((void *) 0)
+#define _MD_SP_TYPE int
+#else
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__sp
+#define _MD_SET_FP(_t, val)
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) ((void *) 0)
+#define _MD_SP_TYPE __ptr_t
+#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */
+
+/* XXX not sure if this is correct, or maybe it should be 17? */
+#define PR_NUM_GCREGS 9
+
+#elif defined(__sparc__)
+/* Sparc */
+#if defined(__GLIBC__) && __GLIBC__ >= 2
+/*
+ * FIXME: The setjmp/longjmp approach works in Red Hat 4.2 but
+ * not in Red Hat 5.1 for Linux/Sparc. We should talk to the
+ * maintainer of Linux/Sparc glibc2 about this. Or we can write
+ * assembly code to save and restore thread contexts. Before
+ * this problem is solved, you can build the pthreads version.
+ * Set the environment variable USE_PTHREADS to 1 before you
+ * build.
+ */
+#error "Please see comments in pr/include/md/_linux.h"
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[JB_SP]
+#define _MD_SET_FP(_t, val) ((_t)->md.context[0].__jmpbuf[JB_FP] = val)
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) (&(_t)->md.context[0].__jmpbuf[JB_FP])
+#define _MD_SP_TYPE int
+#else
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__fp
+#define _MD_SET_FP(_t, val)
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) ((void *) 0)
+#define _MD_SP_TYPE __ptr_t
+#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */
+
+#elif defined(__i386__)
+/* Intel based Linux */
+#if defined(__GLIBC__) && __GLIBC__ >= 2
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[JB_SP]
+#define _MD_SET_FP(_t, val) ((_t)->md.context[0].__jmpbuf[JB_BP] = val)
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) (&(_t)->md.context[0].__jmpbuf[JB_BP])
+#define _MD_SP_TYPE int
+#else
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__sp
+#define _MD_SET_FP(_t, val) ((_t)->md.context[0].__jmpbuf[0].__bp = val)
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) &((_t)->md.context[0].__jmpbuf[0].__bp)
+#define _MD_SP_TYPE __ptr_t
+#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */
+#define PR_NUM_GCREGS 6
+
+#elif defined(__mips__)
+/* Linux/MIPS */
+#if defined(__GLIBC__) && __GLIBC__ >= 2
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__sp
+#define _MD_SET_FP(_t, val) ((_t)->md.context[0].__jmpbuf[0].__fp = (val))
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) (&(_t)->md.context[0].__jmpbuf[0].__fp)
+#define _MD_SP_TYPE __ptr_t
+#else
+#error "Linux/MIPS pre-glibc2 not supported yet"
+#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */
+
+#elif defined(__arm__)
+/* ARM/Linux */
+#if defined(__GLIBC__) && __GLIBC__ >= 2
+#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[20]
+#define _MD_SET_FP(_t, val) ((_t)->md.context[0].__jmpbuf[19] = (val))
+#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t))
+#define _MD_GET_FP_PTR(_t) (&(_t)->md.context[0].__jmpbuf[19])
+#define _MD_SP_TYPE __ptr_t
+#else
+#error "ARM/Linux pre-glibc2 not supported yet"
+#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */
+
+#else
+
+#error "Unknown CPU architecture"
+
+#endif /*__powerpc__*/
+
+/*
+** Initialize a thread context to run "_main()" when started
+*/
+#ifdef __powerpc__
+
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if (sigsetjmp(CONTEXT(_thread), 1)) { \
+ _main(); \
+ } \
+ _MD_GET_SP(_thread) = (unsigned char*) ((_sp) - 128); \
+ _thread->md.sp = _MD_GET_SP_PTR(_thread); \
+ _thread->md.fp = _MD_GET_FP_PTR(_thread); \
+ _MD_SET_FP(_thread, 0); \
+}
+
+#elif defined(__mips__)
+
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ (void) sigsetjmp(CONTEXT(_thread), 1); \
+ _thread->md.context[0].__jmpbuf[0].__pc = (__ptr_t) _main; \
+ _MD_GET_SP(_thread) = (_MD_SP_TYPE) ((_sp) - 64); \
+ _thread->md.sp = _MD_GET_SP_PTR(_thread); \
+ _thread->md.fp = _MD_GET_FP_PTR(_thread); \
+ _MD_SET_FP(_thread, 0); \
+}
+
+#else
+
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if (sigsetjmp(CONTEXT(_thread), 1)) { \
+ _main(); \
+ } \
+ _MD_GET_SP(_thread) = (_MD_SP_TYPE) ((_sp) - 64); \
+ _thread->md.sp = _MD_GET_SP_PTR(_thread); \
+ _thread->md.fp = _MD_GET_FP_PTR(_thread); \
+ _MD_SET_FP(_thread, 0); \
+}
+
+#endif /*__powerpc__*/
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!sigsetjmp(CONTEXT(_thread), 1)) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ siglongjmp(CONTEXT(_thread), 1); \
+}
+
+/* Machine-dependent (MD) data structures */
+
+struct _MDThread {
+ PR_CONTEXT_TYPE context;
+ void *sp;
+ void *fp;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#include <sys/time.h> /* for FD_SETSIZE */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+extern PRStatus _MD_InitializeThread(PRThread *thread);
+
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread) _MD_suspend_thread
+#define _MD_RESUME_THREAD(thread) _MD_resume_thread
+#define _MD_CLEAN_THREAD(_thread)
+
+extern PRStatus _MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize);
+extern void _MD_SET_PRIORITY(struct _MDThread *thread, PRUintn newPri);
+extern PRStatus _MD_WAIT(PRThread *, PRIntervalTime timeout);
+extern PRStatus _MD_WAKEUP_WAITER(PRThread *);
+extern void _MD_YIELD(void);
+
+#endif /* ! _PR_PTHREADS */
+
+extern void _MD_EarlyInit(void);
+extern PRIntervalTime _PR_UNIX_GetInterval(void);
+extern PRIntervalTime _PR_UNIX_TicksPerSecond(void);
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_GET_INTERVAL _PR_UNIX_GetInterval
+#define _MD_INTERVAL_PER_SEC _PR_UNIX_TicksPerSecond
+
+/*
+ * We wrapped the select() call. _MD_SELECT refers to the built-in,
+ * unwrapped version.
+ */
+#define _MD_SELECT __select
+
+#ifdef _PR_POLL_AVAILABLE
+#include <sys/poll.h>
+extern int __syscall_poll(struct pollfd *ufds, unsigned long int nfds,
+ int timeout);
+#define _MD_POLL __syscall_poll
+#endif
+
+/* For writev() */
+#include <sys/uio.h>
+
+#endif /* nspr_linux_defs_h___ */
diff --git a/pr/include/md/_macos.h b/pr/include/md/_macos.h
new file mode 100644
index 00000000..a4e9b137
--- /dev/null
+++ b/pr/include/md/_macos.h
@@ -0,0 +1,566 @@
+/* -*- 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 prmacos_h___
+#define prmacos_h___
+
+//
+// This file contains all changes and additions which need to be made to the NSPR runtime
+// for the Macintosh platform (specifically the Metrowerks environment). This file should
+// only be incluced in Macintosh builds.
+//
+
+#define PR_DLL_SUFFIX ""
+#define _PR_NO_PREEMPT 1
+
+#include "prinit.h"
+#include "prio.h"
+#include "prlong.h"
+#include "prlock.h"
+#include "prcvar.h"
+#include "prsem.h"
+#include "prthread.h"
+#include "prtime.h"
+#include "prproces.h"
+
+#if !defined(MAC_NSPR_STANDALONE)
+#include "macstdlibextras.h"
+#endif
+
+#include <stddef.h>
+#include <setjmp.h>
+#include <Errors.h>
+
+struct _MDProcess {
+ PRInt8 notused;
+};
+
+struct _MDThread {
+ jmp_buf jb;
+ int osErrCode;
+ PRLock *asyncIOLock;
+ PRCondVar *asyncIOCVar;
+ void *cookie;
+ PRBool notifyPending;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+struct _MDCPU {
+ PRInt8 notused;
+};
+
+struct _MDFileDesc {
+ PRInt32 osfd;
+};
+
+/*
+** Iinitialization Related definitions
+*/
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _MD_FinalInit
+
+/*
+** Interrupts Related definitions
+*/
+
+#define _MD_START_INTERRUPTS _MD_StartInterrupts
+#define _MD_STOP_INTERRUPTS _MD_StopInterrupts
+#define _MD_BLOCK_CLOCK_INTERRUPTS()
+#define _MD_UNBLOCK_CLOCK_INTERRUPTS()
+#define _MD_DISABLE_CLOCK_INTERRUPTS()
+#define _MD_ENABLE_CLOCK_INTERRUPTS()
+
+/*
+** CPU Related definitions
+*/
+
+#define _MD_PAUSE_CPU _MD_PauseCPU
+#define _MD_CLEANUP_BEFORE_EXIT()
+#define _MD_EXIT(status) exit(status)
+#define _MD_INIT_CPUS()
+#define _MD_INIT_RUNNING_CPU(cpu)
+
+/*
+** Process Related definitions
+*/
+
+extern struct PRProcess * _MD_CreateProcess(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr);
+#define _MD_CREATE_PROCESS _MD_CreateProcess
+
+extern PRStatus _MD_DetachProcess(PRProcess *process);
+#define _MD_DETACH_PROCESS _MD_DetachProcess
+
+extern PRStatus _MD_WaitProcess(PRProcess *process, PRInt32 *exitCode);
+#define _MD_WAIT_PROCESS _MD_WaitProcess
+
+extern PRStatus _MD_KillProcess(PRProcess *process);
+#define _MD_KILL_PROCESS _MD_KillProcess
+
+/*
+** Memory Segments Related definitions
+*/
+
+#define _MD_INIT_SEGS()
+
+/*
+** Thread Stacks Debugging Related definitions
+*/
+
+#define _MD_INIT_STACK _MD_InitStack
+#define _MD_CLEAR_STACK _MD_ClearStack
+
+/*
+** Locks Related definitions
+*/
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) (PR_SUCCESS)
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+
+/*
+** Thread Related definitions
+*/
+
+PR_EXTERN(PRThread *) PR_GetPrimaryThread();
+
+#if defined(powerc) || defined(__powerc)
+#define _MD_GET_PC(_t) (*((PRUint32 *)((_t)->md.jb)))
+#define _MD_GET_SP(_t) (*((PRUint32 *)((_t)->md.jb) + 2))
+#define _MD_GET_TOC(_t) (*((PRUint32 *)((_t)->md.jb) + 3))
+#define INIT_STACKPTR(stackTop) ((unsigned char*)stackTop - 128)
+#define kDefaultMacintoshStackSize 58 * 1024
+#define PR_NUM_GCREGS 70
+#else
+#define _MD_GET_PC(_t) (*((PRUint32 *)((_t)->md.jb) + 6))
+#define _MD_GET_SP(_t) (*((PRUint32 *)((_t)->md.jb) + 12))
+#define INIT_STACKPTR(stackTop) ((unsigned char*)stackTop - 4)
+#define kDefaultMacintoshStackSize 58 * 1024
+#define PR_NUM_GCREGS 13
+#endif
+
+#define _MD_DEFAULT_STACK_SIZE kDefaultMacintoshStackSize
+
+/*
+** Initialize the thread machine dependent data structure
+*/
+extern PRStatus _MD_InitThread(PRThread *thread);
+#define _MD_INIT_THREAD _MD_InitThread
+
+/*
+** Clean-up the thread machine dependent data structure
+*/
+#define _MD_CLEAN_THREAD(_thread) \
+ PR_BEGIN_MACRO \
+ PR_DestroyCondVar(_thread->md.asyncIOCVar); \
+ PR_DestroyLock(_thread->md.asyncIOLock); \
+ PR_END_MACRO
+
+
+/*
+** Initialize the thread context preparing it to execute _main.
+*/
+#if defined(powerc) || defined(__powerc)
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, _status) \
+ PR_BEGIN_MACRO \
+ unsigned char *sp; \
+ unsigned long *tvect; \
+ long **jb = (_thread)->md.jb; \
+ *((PRBool *)_status) = PR_TRUE; \
+ (void) setjmp(jb); \
+ sp = INIT_STACKPTR(_sp); \
+ (_MD_GET_SP(_thread)) = (long) sp; \
+ tvect = (unsigned long *)_main; \
+ (_MD_GET_PC(_thread)) = (int) *tvect; \
+ (_MD_GET_TOC(_thread)) = (int) *(tvect+1); \
+ _thread->no_sched = 0; \
+ PR_END_MACRO
+#else
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, _status) \
+ PR_BEGIN_MACRO \
+ unsigned char *sp; \
+ long **jb = (_thread)->md.jb; \
+ *((PRBool *)_status) = PR_TRUE; \
+ (void) setjmp(jb); \
+ sp = INIT_STACKPTR(_sp); \
+ (_MD_GET_SP(_thread)) = (long) sp; \
+ (_MD_GET_PC(_thread)) = (int) _main; \
+ _thread->no_sched = 0; \
+ PR_END_MACRO
+#endif
+
+/*
+** Switch away from the current thread context by saving its state and
+** calling the thread scheduler. Reload cpu when we come back from the
+** context switch because it might have changed.
+*/
+/* ResetTimer(); before _PR_Schedule() */
+#define _MD_SWITCH_CONTEXT(_thread) \
+ PR_BEGIN_MACRO \
+ PR_ASSERT(_thread->no_sched); \
+ if (!setjmp(_thread->md.jb)) { \
+ _MD_SET_LAST_THREAD(_thread); \
+ _PR_Schedule(); \
+ } else { \
+ PR_ASSERT(_MD_LAST_THREAD() !=_MD_CURRENT_THREAD()); \
+ _MD_LAST_THREAD()->no_sched = 0; \
+ } \
+ PR_END_MACRO
+
+/*
+** Restore a thread context that was saved by _MD_SWITCH_CONTEXT or
+** initialized by _MD_INIT_CONTEXT.
+*/
+#define _MD_RESTORE_CONTEXT(_newThread) \
+ PR_BEGIN_MACRO \
+ long **jb = (_newThread)->md.jb; \
+ _MD_SET_CURRENT_THREAD(_newThread); \
+ _newThread->no_sched = 1; \
+ longjmp(jb, 1); \
+ PR_END_MACRO
+
+
+#define _MD_ERRNO() _MD_CURRENT_THREAD()->md.osErrCode
+
+extern PRStatus _MD_wait(PRThread *thread, PRIntervalTime timeout);
+#define _MD_WAIT _MD_wait
+
+/*
+** Combined thread model related definitions
+*/
+
+#define _MD_CREATE_THREAD(a,b,c,d,e,f) (PR_SUCCESS)
+#define _MD_WAKEUP_WAITER(a)
+#define _MD_SET_PRIORITY(a,b)
+
+/*
+** File I/O Related definitions
+*/
+
+extern PRInt32 _PR_MD_WRITE_SYNC(PRFileDesc *fd, void *buf, PRInt32 amount);
+#define _PR_MD_WRITE_SYNC _MD_WRITE_SYNC
+
+struct _MDDir {
+ short ioVRefNum;
+ long ioDirID;
+ short ioFDirIndex;
+ char *currentEntryName;
+};
+
+#define PR_DIRECTORY_SEPARATOR '/'
+#define PR_DIRECTORY_SEPARATOR_STR "/"
+#define PR_PATH_SEPARATOR ':'
+#define PR_PATH_SEPARATOR_STR ":"
+
+typedef enum IOOperation {
+ READ_ASYNC,
+ WRITE_ASYNC
+} IOOperation;
+
+
+#define _MD_INIT_IO()
+
+#define _MD_OPEN _MD_Open
+#define _MD_CLOSE_FILE FSClose
+#define _MD_READ(fd,buf,amount) ReadWriteProc(fd,buf,amount,READ_ASYNC)
+#define _MD_WRITE(fd,buf,amount) ReadWriteProc(fd,buf,amount,WRITE_ASYNC)
+#define _MD_WRITE_SYNC(fd,buf,amount) WriteSyncProc(fd,buf,amount)
+#define _MD_GET_FILE_ERROR() _PR_MD_CURRENT_THREAD()->md.osErrCode
+#define _MD_LSEEK _MD_LSeek
+#define _MD_FSYNC _MD_FSync
+
+/* to be implemented */
+#define _MD_LSEEK64(a,b,c) LL_ZERO
+#define _MD_GETOPENFILEINFO64(fd,info) -1
+#define _MD_GETFILEINFO64(fd,info) -1
+
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+/*
+** File Manipulation definitions
+*/
+
+#define _MD_RENAME _MD_Rename
+#define _MD_ACCESS _MD_Access
+
+#define _MD_GETFILEINFO _MD_GetFileInfo
+#define _MD_GETOPENFILEINFO _MD_GetOpenFileInfo
+
+#define _MD_STAT _MD_Stat
+
+#define _MD_DELETE _MD_Delete
+
+extern PRStatus _MD_LockFile(PRInt32 osfd);
+#define _MD_LOCKFILE _MD_LockFile
+extern PRStatus _MD_TLockFile(PRInt32 osfd);
+#define _MD_TLOCKFILE _MD_TLockFile
+extern PRStatus _MD_UnlockFile(PRInt32 osfd);
+#define _MD_UNLOCKFILE _MD_UnlockFile
+
+/*
+** Directory enumeration related definitions
+*/
+
+extern PRStatus _MD_OpenDir(struct _MDDir *md,const char *name);
+#define _MD_OPEN_DIR _MD_OpenDir
+
+extern char* _MD_ReadDir(struct _MDDir *md,PRIntn flags);
+#define _MD_READ_DIR _MD_ReadDir
+
+#define _MD_CLOSE_DIR _MD_CloseDir
+
+#define _MD_MKDIR _MD_MkDir
+#define _MD_RMDIR _MD_Delete
+
+/*
+** Socket I/O Related definitions
+*/
+
+#define _MD_SOCKET _MD_socket
+#define _MD_BIND _MD_bind
+#define _MD_LISTEN _MD_listen
+#define _MD_GETSOCKNAME _MD_getsockname
+
+extern PRStatus _MD_getsockopt(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen);
+#define _MD_GETSOCKOPT _MD_getsockopt
+
+extern PRStatus _MD_setsockopt(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen);
+#define _MD_SETSOCKOPT _MD_setsockopt
+
+#define _MD_SOCKETAVAILABLE _MD_socketavailable
+#define _MD_ACCEPT _MD_accept
+#define _MD_CONNECT _MD_connect
+#define _MD_SEND _MD_send
+#define _MD_RECV _MD_recv
+#define _MD_CLOSE_SOCKET _MD_closesocket
+#define _MD_SENDTO _MD_sendto
+#define _MD_RECVFROM _MD_recvfrom
+#define _MD_PR_POLL _MD_poll
+#define _MD_MAKE_NONBLOCK _MD_makenonblock
+
+#define _MD_GET_SOCKET_ERROR() _PR_MD_CURRENT_THREAD()->md.osErrCode
+
+#define _PR_MD_MAP_SELECT_ERROR(x) (x)
+/*
+** Netdb Related definitions
+*/
+extern PRStatus _MD_gethostname(char *name, int namelen);
+#define _MD_GETHOSTNAME _MD_gethostname
+
+/*
+ XXX _MD_WRITEV, _MD_SHUTDOWN & _MD_GETPEERNAME not done yet!!!
+*/
+#define _MD_WRITEV _MD_writev
+#define _MD_SHUTDOWN _MD_shutdown
+#define _MD_GETPEERNAME _MD_getpeername
+
+
+#ifdef OLD_MACSOCK_LIBRARY
+#define _MD_SOCKET macsock_socket
+#define _MD_LISTEN macsock_listen
+#define _MD_SEND(fd,buf,amount,flags,timeout) macsock_send(fd->secret->md.osfd,buf,amount,flags)
+#define _MD_SENDTO(fd,buf,amount,flags,addr,addrlen,timeout) macsock_sendto(fd->secret->md.osfd,buf,amount,flags,(struct sockaddr *)addr,addrlen)
+#define _MD_RECV(fd,buf,amount,flags,timeout) macsock_recv(fd->secret->md.osfd,buf,amount,flags)
+#define _MD_RECVFROM(fd,buf,amount,flags,addr,addrlen,timeout) macsock_recvfrom(fd->secret->md.osfd,buf,amount,flags,(struct sockaddr *)addr,addrlen)
+#define _MD_CLOSE_SOCKET macsock_close
+#define _MD_SHUTDOWN(a,b) (0)
+
+#define _MD_ACCEPT(fd,addr,addrlen,timeout) macsock_accept(fd->secret->md.osfd,(struct sockaddr *)addr,addrlen)
+#define _MD_CONNECT(fd,name,namelen,timeout) macsock_connect(fd->secret->md.osfd,(struct sockaddr *)name,namelen)
+#define _MD_BIND(fd,name,namelen) macsock_bind(fd->secret->md.osfd,(struct sockaddr *)name,namelen)
+#define _MD_GETSOCKNAME(fd,name,namelen) macsock_getsockname(fd->secret->md.osfd,(struct sockaddr *)name,namelen)
+#define _MD_GETPEERNAME(fd,name,namelen) macsock_getpeername(fd->secret->md.osfd,(struct sockaddr *)name,namelen)
+#define _MD_GETSOCKOPT(fd,level,optname,optval,optlen) macsock_getsockopt(fd->secret->md.osfd,level,optname,optval,optlen)
+#define _MD_SETSOCKOPT(fd,level,optname,optval,optlen) macsock_setsockopt(fd->secret->md.osfd,level,optname,optval,optlen)
+#define _MD_SOCKETAVAILABLE(fd,bytes) macsock_socketavailable(fd->secret->md.osfd,bytes)
+#endif
+
+/*
+** Memory Segements Related definitions
+*/
+
+#define _MD_INIT_SEGS()
+#define _MD_ALLOC_SEGMENT _MD_AllocSegment
+#define _MD_FREE_SEGMENT _MD_FreeSegment
+
+/*
+** Time Related definitions
+*/
+
+#define kMacTimerInMiliSecs 8L
+#define _MD_GET_INTERVAL _MD_GetInterval
+#define _MD_INTERVAL_PER_SEC() (PR_MSEC_PER_SEC/kMacTimerInMiliSecs)
+#define _MD_INTERVAL_INIT()
+
+/*
+** Environemnt Related definitions
+*/
+
+extern char *_MD_GetEnv(const char *name);
+#define _MD_GET_ENV _MD_GetEnv
+
+extern int _MD_PutEnv(const char *variableCopy);
+#define _MD_PUT_ENV _MD_PutEnv
+
+/*
+** Following is old stuff to be looked at.
+*/
+
+#define GCPTR
+#define CALLBACK
+typedef int (*FARPROC)();
+#define gcmemcpy(a,b,c) memcpy(a,b,c)
+
+
+#define MAX_NON_PRIMARY_TIME_SLICES 6
+
+extern long gTimeSlicesOnNonPrimaryThread;
+extern struct PRThread *gPrimaryThread;
+
+typedef short PROSFD;
+
+// Errors not found in the Mac StdCLib
+#define EACCES 13 // Permission denied
+#define ENOENT -43 // No such file or directory
+#define _OS_INVALID_FD_VALUE -1
+
+#define STDERR_FILENO 2
+
+#if !defined(MAC_NSPR_STANDALONE)
+#define PATH_SEPARATOR ':'
+#define PATH_SEPARATOR_STR ":"
+#define DIRECTORY_SEPARATOR '/'
+#define DIRECTORY_SEPARATOR_STR "/"
+#endif
+
+#define UNIX_THIS_DIRECTORY_STR "./"
+#define UNIX_PARENT_DIRECTORY_STR "../"
+
+
+// Alias a few names
+#define getenv PR_GetEnv
+#define putenv _MD_PutEnv
+
+#if defined(MAC_NSPR_STANDALONE)
+typedef unsigned char (*MemoryCacheFlusherProc)(size_t size);
+typedef void (*PreAllocationHookProc)(void);
+
+extern char *strdup(const char *source);
+
+extern void InstallPreAllocationHook(PreAllocationHookProc newHook);
+extern void InstallMemoryCacheFlusher(MemoryCacheFlusherProc newFlusher);
+#endif
+
+extern char *PR_GetDLLSearchPath(void);
+
+#if defined(MAC_NSPR_STANDALONE)
+extern int strcmp(const char *str1, const char *str2);
+extern int strcasecmp(const char *str1, const char *str2);
+#endif
+
+extern void MapFullToPartialMacFile(char *);
+extern char *MapPartialToFullMacFile(const char *);
+
+extern void ResetTimer(void);
+extern void PR_PeriodicIdle(void);
+extern void ActivateTimer(void);
+extern void DeactivateTimer(void);
+extern void PR_InitMemory(void);
+
+extern struct hostent *gethostbyaddr(const void *addr, int addrlen, int type);
+
+extern short GetVolumeRefNumFromName(const char *);
+
+#include <stdio.h> // Needed to get FILE typedef
+extern FILE *_OS_FOPEN(const char *filename, const char *mode);
+//
+// Macintosh only private parts.
+//
+
+#define dprintTrace ";dprintf;doTrace"
+#define dprintNoTrace ";dprintf"
+extern void dprintf(const char *format, ...);
+
+
+// Entry into the memory system's cache flushing
+#if defined(MAC_NSPR_STANDALONE)
+extern PRUint8 CallCacheFlushers(size_t blockSize);
+#endif
+
+#if defined(MAC_NSPR_STANDALONE)
+extern void* reallocSmaller(void* block, size_t newSize);
+#endif
+
+
+/*
+** PR_GetSystemInfo related definitions
+*/
+#define _PR_SI_SYSNAME "MacOS"
+#define _PR_SI_ARCHITECTURE "PowerPC"
+
+/*
+ * Memory-mapped files
+ */
+
+struct _MDFileMap {
+ PRInt8 unused;
+};
+
+extern PRStatus _MD_CreateFileMap(struct PRFileMap *fmap, PRInt64 size);
+#define _MD_CREATE_FILE_MAP _MD_CreateFileMap
+
+extern void * _MD_MemMap(struct PRFileMap *fmap, PRInt64 offset,
+ PRUint32 len);
+#define _MD_MEM_MAP _MD_MemMap
+
+extern PRStatus _MD_MemUnmap(void *addr, PRUint32 size);
+#define _MD_MEM_UNMAP _MD_MemUnmap
+
+extern PRStatus _MD_CloseFileMap(struct PRFileMap *fmap);
+#define _MD_CLOSE_FILE_MAP _MD_CloseFileMap
+
+extern void SetLogFileTypeCreator(const char *logFile);
+extern int _MD_mac_get_nonblocking_connect_error(PRInt32 osfd);
+
+#endif /* prmacos_h___ */
diff --git a/pr/include/md/_ncr.cfg b/pr/include/md/_ncr.cfg
new file mode 100644
index 00000000..dde85210
--- /dev/null
+++ b/pr/include/md/_ncr.cfg
@@ -0,0 +1,121 @@
+/* -*- 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_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef NCR
+#define NCR
+#endif
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#undef HAVE_LONG_LONG
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#define _PR_POLL_BACKCOMPAT
+
+#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/include/md/_ncr.h b/pr/include/md/_ncr.h
new file mode 100644
index 00000000..2c5ebb0d
--- /dev/null
+++ b/pr/include/md/_ncr.h
@@ -0,0 +1,212 @@
+/* -*- 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_unixware_defs_h___
+#define nspr_unixware_defs_h___
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "ncr"
+#define _PR_SI_SYSNAME "NCR"
+#define _PR_SI_ARCHITECTURE "x86"
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#define HAVE_DLL
+#define USE_DLFCN
+#define _PR_RECV_BROKEN /* recv doesn't work on Unix Domain Sockets */
+
+#if !defined (HAVE_STRERROR)
+#define HAVE_STRERROR
+#endif
+
+#ifndef HAVE_WEAK_IO_SYMBOLS
+#define HAVE_WEAK_IO_SYMBOLS
+#endif
+
+#define _PR_POLL_AVAILABLE
+#define _PR_USE_POLL
+#define _PR_NO_LARGE_FILES
+#define _PR_STAT_HAS_ST_ATIM_UNION
+
+#undef HAVE_STACK_GROWING_UP
+#define HAVE_NETCONFIG
+#define NEED_STRFTIME_LOCK
+#define NEED_TIME_R
+#define NEED_LOCALTIME_R
+#define NEED_GMTIME_R
+#define NEED_ASCTIME_R
+#define NEED_STRTOK_R
+#define NEED_CTIME_R
+#define _PR_NEED_STRCASECMP
+
+#define USE_SETJMP
+
+#include <setjmp.h>
+
+#define _SETJMP setjmp
+#define _LONGJMP longjmp
+#define _PR_CONTEXT_TYPE jmp_buf
+#define _MD_GET_SP(_t) (_t)->md.context[4]
+#define _PR_NUM_GCREGS _JBLEN
+
+#define CONTEXT(_th) ((_th)->md.context)
+
+/*
+** Initialize the thread context preparing it to execute _main.
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if(_SETJMP(CONTEXT(_thread))) (*_main)(); \
+ _MD_GET_SP(_thread) = (int) ((_sp) - 128); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!_SETJMP(CONTEXT(_thread))) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ _LONGJMP(CONTEXT(_thread), 1); \
+}
+
+/* Machine-dependent (MD) data structures.
+ * Don't use SVR4 native threads (yet).
+ */
+
+struct _MDThread {
+ _PR_CONTEXT_TYPE context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+/*
+ * The following are copied from _sunos.h, _aix.h. This means
+ * some of them should probably be moved into _unixos.h. But
+ * _irix.h seems to be quite different in regard to these macros.
+ */
+#define _MD_GET_INTERVAL _PR_UNIX_GetInterval
+#define _MD_INTERVAL_PER_SEC _PR_UNIX_TicksPerSecond
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread)
+#define _MD_RESUME_THREAD(thread)
+#define _MD_CLEAN_THREAD(_thread)
+
+/*
+ * We wrapped the select() call. _MD_SELECT refers to the built-in,
+ * unwrapped version.
+ */
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/select.h>
+extern int _select(int nfds, fd_set *readfds, fd_set *writefds,
+ fd_set *execptfds, struct timeval *timeout);
+#define _MD_SELECT _select
+
+#define _MD_POLL _poll
+extern int _poll(struct pollfd *fds, unsigned long nfds, int timeout);
+
+#endif /* nspr_ncr_defs_h */
diff --git a/pr/include/md/_nec.cfg b/pr/include/md/_nec.cfg
new file mode 100644
index 00000000..0dcef049
--- /dev/null
+++ b/pr/include/md/_nec.cfg
@@ -0,0 +1,121 @@
+/* -*- 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_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef NEC
+#define NEC
+#endif
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#undef HAVE_LONG_LONG
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#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/include/md/_nec.h b/pr/include/md/_nec.h
new file mode 100644
index 00000000..6833f505
--- /dev/null
+++ b/pr/include/md/_nec.h
@@ -0,0 +1,177 @@
+/* -*- 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_nec_defs_h___
+#define nspr_nec_defs_h___
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "nec"
+#define _PR_SI_SYSNAME "NEC"
+#define _PR_SI_ARCHITECTURE "mips"
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#undef HAVE_STACK_GROWING_UP
+#define HAVE_DLL
+#define USE_DLFCN
+#define NEED_TIME_R
+#define NEED_STRFTIME_LOCK
+#define _PR_POLL_AVAILABLE
+#define _PR_USE_POLL
+#define _PR_NO_LARGE_FILES
+#define _PR_STAT_HAS_ST_ATIM_UNION
+
+#include <ucontext.h>
+#include <sys/regset.h>
+
+#define PR_NUM_GCREGS NGREG
+#define PR_CONTEXT_TYPE ucontext_t
+
+#define CONTEXT(_thread) (&(_thread)->md.context)
+
+#define _MD_GET_SP(_t) (_t)->md.context.uc_mcontext.gregs[CXT_SP]
+
+/*
+** Initialize the thread context preparing it to execute "e(o,a)"
+*/
+#define _MD_INIT_CONTEXT(thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ getcontext(CONTEXT(thread)); \
+ CONTEXT(thread)->uc_stack.ss_sp = (char*) (thread)->stack->stackBottom; \
+ CONTEXT(thread)->uc_stack.ss_size = (thread)->stack->stackSize; \
+ _MD_GET_SP(thread) = (greg_t) _sp - 64; \
+ makecontext(CONTEXT(thread), _main, 0); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!getcontext(CONTEXT(_thread))) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ ucontext_t *uc = CONTEXT(_thread); \
+ uc->uc_mcontext.gregs[CXT_V0] = 1; \
+ uc->uc_mcontext.gregs[CXT_A3] = 0; \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ setcontext(uc); \
+}
+
+/* Machine-dependent (MD) data structures */
+
+struct _MDThread {
+ PR_CONTEXT_TYPE context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_CLEAN_THREAD(_thread)
+
+#define _MD_SELECT _select
+#define _MD_POLL _poll
+
+#define _MD_GET_INTERVAL _PR_UNIX_GetInterval
+#define _MD_INTERVAL_PER_SEC _PR_UNIX_TicksPerSecond
+
+#endif /* nspr_nec_defs_h___ */
diff --git a/pr/include/md/_netbsd.cfg b/pr/include/md/_netbsd.cfg
new file mode 100644
index 00000000..9c10f42f
--- /dev/null
+++ b/pr/include/md/_netbsd.cfg
@@ -0,0 +1,262 @@
+/* -*- 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_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef NETBSD
+#define NETBSD
+#endif
+
+#if defined(__i386__) || defined(__arm32__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define HAVE_LONG_LONG
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#elif defined(__sparc__)
+
+#undef IS_LITTLE_ENDIAN 1
+#define IS_BIG_ENDIAN 1
+#define HAVE_LONG_LONG
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+
+#elif defined(__alpha__)
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__powerpc__) || defined(__m68k__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#else
+
+#error Must define constants for type sizes here.
+
+#endif
+
+
+#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/include/md/_netbsd.h b/pr/include/md/_netbsd.h
new file mode 100644
index 00000000..497d658b
--- /dev/null
+++ b/pr/include/md/_netbsd.h
@@ -0,0 +1,211 @@
+/* -*- 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_netbsd_defs_h___
+#define nspr_netbsd_defs_h___
+
+#include <sys/syscall.h>
+
+#define PR_LINKER_ARCH "netbsd"
+#define _PR_SI_SYSNAME "NetBSD"
+#if defined(__i386__)
+#define _PR_SI_ARCHITECTURE "x86"
+#elif defined(__alpha__)
+#define _PR_SI_ARCHITECTURE "alpha"
+#elif defined(__m68k__)
+#define _PR_SI_ARCHITECTURE "m68k"
+#elif defined(__powerpc__)
+#define _PR_SI_ARCHITECTURE "powerpc"
+#elif defined(__sparc__)
+#define _PR_SI_ARCHITECTURE "sparc"
+#elif defined(__arm32__)
+#define _PR_SI_ARCHITECTURE "arm32"
+#endif
+
+#define PR_DLL_SUFFIX ".so.1.0"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#undef HAVE_STACK_GROWING_UP
+#define HAVE_DLL
+#define USE_DLFCN
+#define _PR_HAVE_SOCKADDR_LEN
+
+#define USE_SETJMP
+
+#ifndef _PR_PTHREADS
+#include <setjmp.h>
+
+#define PR_CONTEXT_TYPE sigjmp_buf
+
+#define CONTEXT(_th) ((_th)->md.context)
+
+#if defined(__i386__) || defined(__sparc__) || defined(__m68k__) || defined(__powerpc__)
+#define JB_SP_INDEX 2
+#elif defined(__alpha__)
+#define JB_SP_INDEX 34
+#elif defined(__arm32__)
+/*
+ * On the arm32, the jmpbuf regs underwent a name change after NetBSD 1.3.
+ */
+#ifdef JMPBUF_REG_R13
+#define JB_SP_INDEX JMPBUF_REG_R13
+#else
+#define JB_SP_INDEX _JB_REG_R13
+#endif
+#else
+#error "Need to define SP index in jmp_buf here"
+#endif
+#define _MD_GET_SP(_th) (_th)->md.context[JB_SP_INDEX]
+
+#define PR_NUM_GCREGS _JBLEN
+
+/*
+** Initialize a thread context to run "_main()" when started
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if (sigsetjmp(CONTEXT(_thread), 1)) { \
+ _main(); \
+ } \
+ _MD_GET_SP(_thread) = (unsigned char*) ((_sp) - 64); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!sigsetjmp(CONTEXT(_thread), 1)) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ siglongjmp(CONTEXT(_thread), 1); \
+}
+
+/* Machine-dependent (MD) data structures */
+
+struct _MDThread {
+ PR_CONTEXT_TYPE context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread) _MD_suspend_thread
+#define _MD_RESUME_THREAD(thread) _MD_resume_thread
+#define _MD_CLEAN_THREAD(_thread)
+
+#endif /* ! _PR_PTHREADS */
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_GET_INTERVAL _PR_UNIX_GetInterval
+#define _MD_INTERVAL_PER_SEC _PR_UNIX_TicksPerSecond
+
+/*
+ * We wrapped the select() call. _MD_SELECT refers to the built-in,
+ * unwrapped version.
+ */
+#define _MD_SELECT(nfds,r,w,e,tv) syscall(SYS_select,nfds,r,w,e,tv)
+#define _MD_POLL(fds,nfds,timeout) syscall(SYS_poll,fds,nfds,timeout)
+
+#if NetBSD1_3 == 1L
+typedef unsigned int nfds_t;
+#endif
+
+#endif /* nspr_netbsd_defs_h___ */
diff --git a/pr/include/md/_nspr_pthread.h b/pr/include/md/_nspr_pthread.h
new file mode 100644
index 00000000..1daeb61a
--- /dev/null
+++ b/pr/include/md/_nspr_pthread.h
@@ -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.
+ */
+
+#ifndef nspr_pthread_defs_h___
+#define nspr_pthread_defs_h___
+
+#include <pthread.h>
+#include "prthread.h"
+
+#if defined(PTHREADS_USER)
+/*
+** Thread Local Storage
+*/
+extern pthread_key_t current_thread_key;
+extern pthread_key_t current_cpu_key;
+extern pthread_key_t last_thread_key;
+extern pthread_key_t intsoff_key;
+
+#define _MD_CURRENT_THREAD() \
+ ((struct PRThread *) pthread_getspecific(current_thread_key))
+#define _MD_CURRENT_CPU() \
+ ((struct _PRCPU *) pthread_getspecific(current_cpu_key))
+#define _MD_LAST_THREAD() \
+ ((struct PRThread *) pthread_getspecific(last_thread_key))
+
+#define _MD_SET_CURRENT_THREAD(newval) \
+ pthread_setspecific(current_thread_key, (void *)newval)
+
+#define _MD_SET_CURRENT_CPU(newval) \
+ pthread_setspecific(current_cpu_key, (void *)newval)
+
+#define _MD_SET_LAST_THREAD(newval) \
+ pthread_setspecific(last_thread_key, (void *)newval)
+
+#define _MD_SET_INTSOFF(_val)
+#define _MD_GET_INTSOFF() 1
+
+/*
+** Initialize the thread context preparing it to execute _main.
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+ PR_BEGIN_MACRO \
+ *status = PR_TRUE; \
+ if (SAVE_CONTEXT(_thread)) { \
+ (*_main)(); \
+ } \
+ _MD_SET_THR_SP(_thread, _sp); \
+ _thread->no_sched = 0; \
+ PR_END_MACRO
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ PR_BEGIN_MACRO \
+ PR_ASSERT(_thread->no_sched); \
+ if (!SAVE_CONTEXT(_thread)) { \
+ (_thread)->md.errcode = errno; \
+ _MD_SET_LAST_THREAD(_thread); \
+ _PR_Schedule(); \
+ } else { \
+ (_MD_LAST_THREAD())->no_sched = 0; \
+ } \
+ PR_END_MACRO
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+ PR_BEGIN_MACRO \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ _thread->no_sched = 1; \
+ GOTO_CONTEXT(_thread); \
+ PR_END_MACRO
+
+
+/* Machine-dependent (MD) data structures */
+
+struct _MDThread {
+ jmp_buf jb;
+ int id;
+ int errcode;
+ pthread_t pthread;
+ pthread_mutex_t pthread_mutex;
+ pthread_cond_t pthread_cond;
+ int wait;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ pthread_mutex_t mutex;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ pthread_mutex_t mutex;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ jmp_buf jb;
+ pthread_t pthread;
+ struct _MDCPU_Unix md_unix;
+};
+
+/*
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+*/
+
+extern pthread_mutex_t _pr_heapLock;
+
+#define _PR_LOCK(lock) pthread_mutex_lock(lock)
+
+#define _PR_UNLOCK(lock) pthread_mutex_unlock(lock)
+
+
+#define _PR_LOCK_HEAP() { \
+ if (_pr_primordialCPU) { \
+ _PR_LOCK(_pr_heapLock); \
+ }
+
+#define _PR_UNLOCK_HEAP() if (_pr_primordialCPU) { \
+ _PR_UNLOCK(_pr_heapLock); \
+ } \
+ }
+
+PR_EXTERN(PRStatus) _MD_NEW_LOCK(struct _MDLock *md);
+PR_EXTERN(void) _MD_FREE_LOCK(struct _MDLock *lockp);
+
+#define _MD_LOCK(_lockp) _PR_LOCK(&(_lockp)->mutex)
+#define _MD_UNLOCK(_lockp) _PR_UNLOCK(&(_lockp)->mutex)
+
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+#define _MD_CHECK_FOR_EXIT()
+
+PR_EXTERN(PRStatus) _MD_InitThread(struct PRThread *thread);
+#define _MD_INIT_THREAD _MD_InitThread
+#define _MD_INIT_ATTACHED_THREAD _MD_InitThread
+
+PR_EXTERN(void) _MD_ExitThread(struct PRThread *thread);
+#define _MD_EXIT_THREAD _MD_ExitThread
+
+PR_EXTERN(void) _MD_SuspendThread(struct PRThread *thread);
+#define _MD_SUSPEND_THREAD _MD_SuspendThread
+
+PR_EXTERN(void) _MD_ResumeThread(struct PRThread *thread);
+#define _MD_RESUME_THREAD _MD_ResumeThread
+
+PR_EXTERN(void) _MD_SuspendCPU(struct _PRCPU *thread);
+#define _MD_SUSPEND_CPU _MD_SuspendCPU
+
+PR_EXTERN(void) _MD_ResumeCPU(struct _PRCPU *thread);
+#define _MD_RESUME_CPU _MD_ResumeCPU
+
+#define _MD_BEGIN_SUSPEND_ALL()
+#define _MD_END_SUSPEND_ALL()
+#define _MD_BEGIN_RESUME_ALL()
+#define _MD_END_RESUME_ALL()
+
+PR_EXTERN(void) _MD_EarlyInit(void);
+#define _MD_EARLY_INIT _MD_EarlyInit
+
+#define _MD_FINAL_INIT _PR_UnixInit
+
+PR_EXTERN(void) _MD_InitLocks(void);
+#define _MD_INIT_LOCKS _MD_InitLocks
+
+PR_EXTERN(void) _MD_CleanThread(struct PRThread *thread);
+#define _MD_CLEAN_THREAD _MD_CleanThread
+
+#define _MD_INIT_PRIMORDIAL_THREAD(threadp)
+
+PR_EXTERN(PRStatus) _MD_CreateThread(
+ struct PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize);
+#define _MD_CREATE_THREAD _MD_CreateThread
+
+extern void _MD_CleanupBeforeExit(void);
+#define _MD_CLEANUP_BEFORE_EXIT _MD_CleanupBeforeExit
+
+PR_EXTERN(void) _MD_InitRunningCPU(struct _PRCPU *cpu);
+#define _MD_INIT_RUNNING_CPU _MD_InitRunningCPU
+
+/* The _PR_MD_WAIT_LOCK and _PR_MD_WAKEUP_WAITER functions put to sleep and
+ * awaken a thread which is waiting on a lock or cvar.
+ */
+PR_EXTERN(PRStatus) _MD_wait(struct PRThread *, PRIntervalTime timeout);
+#define _MD_WAIT _MD_wait
+
+PR_EXTERN(PRStatus) _MD_WakeupWaiter(struct PRThread *);
+#define _MD_WAKEUP_WAITER _MD_WakeupWaiter
+
+PR_EXTERN(void) _MD_SetPriority(struct _MDThread *thread,
+ PRThreadPriority newPri);
+#define _MD_SET_PRIORITY _MD_SetPriority
+
+#endif /* PTHREADS_USER */
+
+#endif /* nspr_pthread_defs_h___ */
diff --git a/pr/include/md/_openbsd.cfg b/pr/include/md/_openbsd.cfg
new file mode 100644
index 00000000..967d36e5
--- /dev/null
+++ b/pr/include/md/_openbsd.cfg
@@ -0,0 +1,262 @@
+/* -*- 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_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef OPENBSD
+#define OPENBSD
+#endif
+
+#if defined(__i386__) || defined(__arm32__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define HAVE_LONG_LONG
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#elif defined(__sparc__)
+
+#undef IS_LITTLE_ENDIAN 1
+#define IS_BIG_ENDIAN 1
+#define HAVE_LONG_LONG
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+
+#elif defined(__alpha__)
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__powerpc__) || defined(__m68k__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#else
+
+#error Must define constants for type sizes here.
+
+#endif
+
+
+#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/include/md/_openbsd.h b/pr/include/md/_openbsd.h
new file mode 100644
index 00000000..d34d5e58
--- /dev/null
+++ b/pr/include/md/_openbsd.h
@@ -0,0 +1,171 @@
+/* -*- 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_openbsd_defs_h___
+#define nspr_openbsd_defs_h___
+
+#include <sys/syscall.h>
+
+#define PR_LINKER_ARCH "openbsd"
+#define _PR_SI_SYSNAME "OpenBSD"
+#if defined(__i386__)
+#define _PR_SI_ARCHITECTURE "x86"
+#elif defined(__alpha__)
+#define _PR_SI_ARCHITECTURE "alpha"
+#elif defined(__m68k__)
+#define _PR_SI_ARCHITECTURE "m68k"
+#elif defined(__powerpc__)
+#define _PR_SI_ARCHITECTURE "powerpc"
+#elif defined(__sparc__)
+#define _PR_SI_ARCHITECTURE "sparc"
+#elif defined(__arm32__)
+#define _PR_SI_ARCHITECTURE "arm32"
+#endif
+
+#define PR_DLL_SUFFIX ".so.1.0"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#undef HAVE_STACK_GROWING_UP
+#define HAVE_DLL
+#define USE_DLFCN
+#define _PR_HAVE_SOCKADDR_LEN
+
+#define USE_SETJMP
+
+#ifndef _PR_PTHREADS
+#include <setjmp.h>
+
+#define PR_CONTEXT_TYPE sigjmp_buf
+
+#define CONTEXT(_th) ((_th)->md.context)
+
+#if defined(__i386__) || defined(__sparc__) || defined(__m68k__) || defined(__powerpc__)
+#define JB_SP_INDEX 2
+#elif defined(__alpha__)
+#define JB_SP_INDEX 34
+#elif defined(__arm32__)
+/*
+ * On the arm32, the jmpbuf regs underwent a namechange after NetBSD 1.3
+ */
+#ifdef JMPBUF_REG_R13
+#define JB_SP_INDEX JMPBUF_REG_R13
+#else
+#define JB_SP_INDEX _JB_REG_R13
+#endif
+#else
+#error "Need to define SP index in jmp_buf here"
+#endif
+#define _MD_GET_SP(_th) (_th)->md.context[JB_SP_INDEX]
+
+#define PR_NUM_GCREGS _JBLEN
+
+/*
+** Initialize a thread context to run "_main()" when started
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if (sigsetjmp(CONTEXT(_thread), 1)) { \
+ _main(); \
+ } \
+ _MD_GET_SP(_thread) = (unsigned char*) ((_sp) - 64); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!sigsetjmp(CONTEXT(_thread), 1)) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ siglongjmp(CONTEXT(_thread), 1); \
+}
+
+/* Machine-dependent (MD) data structures */
+
+struct _MDThread {
+ PR_CONTEXT_TYPE context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread) _MD_suspend_thread
+#define _MD_RESUME_THREAD(thread) _MD_resume_thread
+#define _MD_CLEAN_THREAD(_thread)
+
+#endif /* ! _PR_PTHREADS */
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_GET_INTERVAL _PR_UNIX_GetInterval
+#define _MD_INTERVAL_PER_SEC _PR_UNIX_TicksPerSecond
+
+/*
+ * We wrapped the select() call. _MD_SELECT refers to the built-in,
+ * unwrapped version.
+ */
+#define _MD_SELECT(nfds,r,w,e,tv) syscall(SYS_select,nfds,r,w,e,tv)
+#define _MD_POLL(fds,nfds,timeout) syscall(SYS_poll,fds,nfds,timeout)
+
+#endif /* nspr_openbsd_defs_h___ */
diff --git a/pr/include/md/_os2.cfg b/pr/include/md/_os2.cfg
new file mode 100644
index 00000000..1271e0f1
--- /dev/null
+++ b/pr/include/md/_os2.cfg
@@ -0,0 +1,127 @@
+/* -*- 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_PC
+#define XP_PC
+#endif
+
+#ifndef XP_OS2
+#define XP_OS2
+#endif
+
+#ifndef OS2
+#define OS2
+#endif
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#undef HAVE_LONG_LONG
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_WORD 32
+#define PR_BITS_PER_DWORD 64
+#define PR_BITS_PER_DOUBLE 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_WORD_LOG2 5
+#define PR_BITS_PER_DWORD_LOG2 6
+#define PR_BITS_PER_DOUBLE_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_WORD 4
+#define PR_ALIGN_OF_DWORD 8
+#define PR_ALIGN_OF_POINTER 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 2
+
+#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/include/md/_os2.h b/pr/include/md/_os2.h
new file mode 100644
index 00000000..bd38535e
--- /dev/null
+++ b/pr/include/md/_os2.h
@@ -0,0 +1,498 @@
+/* -*- 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_os2_defs_h___
+#define nspr_os2_defs_h___
+
+#define INCL_DOS
+#define INCL_DOSERRORS
+#define INCL_WIN
+#define INCL_WPS
+#define TID OS2TID /* global rename in OS2 H's! */
+#include <os2.h>
+#undef TID /* and restore */
+#include <sys/select.h>
+
+#include "prio.h"
+
+#include <errno.h>
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "os2"
+#define _PR_SI_SYSNAME "OS2"
+#define _PR_SI_ARCHITECTURE "x86" /* XXXMB hardcode for now */
+
+#define HAVE_DLL
+#undef HAVE_THREAD_AFFINITY
+#define HAVE_SOCKET_REUSEADDR
+#define HAVE_SOCKET_KEEPALIVE
+
+/*********************************************
+/* We don't have atomic ops on OS/2 natively */
+/* #define _PR_HAVE_ATOMIC_OPS */
+/*********************************************/
+
+#define HANDLE unsigned long
+#define HINSTANCE HMODULE
+
+/* --- Common User-Thread/Native-Thread Definitions --------------------- */
+
+/* --- Globals --- */
+extern struct PRLock *_pr_schedLock;
+
+/* --- Typedefs --- */
+typedef void (*FiberFunc)(void *);
+
+#define PR_NUM_GCREGS 8
+typedef PRInt32 PR_CONTEXT_TYPE[PR_NUM_GCREGS];
+#define GC_VMBASE 0x40000000
+#define GC_VMLIMIT 0x00FFFFFF
+typedef int (*FARPROC)();
+
+#define _MD_MAGIC_THREAD 0x22222222
+#define _MD_MAGIC_THREADSTACK 0x33333333
+#define _MD_MAGIC_SEGMENT 0x44444444
+#define _MD_MAGIC_DIR 0x55555555
+#define _MD_MAGIC_CV 0x66666666
+
+#define CALLBACK
+
+typedef struct _MDSemaphore
+{
+ HEV sem;
+} MDSEM;
+
+struct _MDCPU {
+ int unused;
+};
+
+struct _MDThread {
+ MDSEM blocked_sema; /* Threads block on this when waiting
+ * for IO or CondVar.
+ */
+ PRBool inCVWaitQueue; /* PR_TRUE if the thread is in the
+ * wait queue of some cond var.
+ * PR_FALSE otherwise. */
+ OS2TID handle; /* OS/2 thread handle */
+ void *sp; /* only valid when suspended */
+ PRUint32 magic; /* for debugging */
+ PR_CONTEXT_TYPE gcContext; /* Thread context for GC */
+ struct PRThread *prev, *next; /* used by the cvar wait queue to
+ * chain the PRThread structures
+ * together */
+};
+
+struct _MDThreadStack {
+ PRUint32 magic; /* for debugging */
+};
+
+struct _MDSegment {
+ PRUint32 magic; /* for debugging */
+};
+
+#undef PROFILE_LOCKS
+
+struct _MDDir {
+ HDIR d_hdl;
+ FILEFINDBUF3 d_entry;
+ PRBool firstEntry; /* Is this the entry returned
+ * by FindFirstFile()? */
+ PRUint32 magic; /* for debugging */
+};
+
+struct _MDCVar {
+ PRUint32 magic;
+ struct PRThread *waitHead, *waitTail; /* the wait queue: a doubly-
+ * linked list of threads
+ * waiting on this condition
+ * variable */
+ PRIntn nwait; /* number of threads in the
+ * wait queue */
+};
+
+#define _MD_CV_NOTIFIED_LENGTH 6
+typedef struct _MDNotified _MDNotified;
+struct _MDNotified {
+ PRIntn length; /* # of used entries in this
+ * structure */
+ struct {
+ struct _MDCVar *cv; /* the condition variable notified */
+ PRIntn times; /* and the number of times notified */
+ struct PRThread *notifyHead; /* list of threads to wake up */
+ } cv[_MD_CV_NOTIFIED_LENGTH];
+ _MDNotified *link; /* link to another of these, or NULL */
+};
+
+struct _MDLock {
+ HMTX mutex; /* this is recursive on NT */
+
+ /*
+ * When notifying cvars, there is no point in actually
+ * waking up the threads waiting on the cvars until we've
+ * released the lock. So, we temporarily record the cvars.
+ * When doing an unlock, we'll then wake up the waiting threads.
+ */
+ struct _MDNotified notified; /* array of conditions notified */
+#ifdef PROFILE_LOCKS
+ PRInt32 hitcount;
+ PRInt32 misscount;
+#endif
+};
+
+struct _MDFileDesc {
+ PRInt32 osfd; /* The osfd can come from one of three spaces:
+ * - For stdin, stdout, and stderr, we are using
+ * the libc file handle (0, 1, 2), which is an int.
+ * - For files and pipes, we are using OS/2 handles,
+ * which is a void*.
+ * - For sockets, we are using int
+ */
+};
+
+struct _MDProcess {
+ PID pid;
+};
+
+/* --- Misc stuff --- */
+#define _MD_GET_SP(thread) (thread)->md.gcContext[6]
+
+/* --- IO stuff --- */
+
+#define _MD_OPEN _PR_MD_OPEN
+#define _MD_READ _PR_MD_READ
+#define _MD_WRITE _PR_MD_WRITE
+#define _MD_WRITEV _PR_MD_WRITEV
+#define _MD_LSEEK _PR_MD_LSEEK
+#define _MD_LSEEK64 _PR_MD_LSEEK64
+extern PRInt32 _MD_CloseFile(PRInt32 osfd);
+#define _MD_CLOSE_FILE _MD_CloseFile
+#define _MD_GETFILEINFO _PR_MD_GETFILEINFO
+#define _MD_GETOPENFILEINFO _PR_MD_GETOPENFILEINFO
+#define _MD_GETOPENFILEINFO64(a, b) _PR_MD_GETOPENFILEINFO64(a, b)
+#define _MD_STAT _PR_MD_STAT
+#define _MD_RENAME _PR_MD_RENAME
+#define _MD_ACCESS _PR_MD_ACCESS
+#define _MD_DELETE _PR_MD_DELETE
+#define _MD_MKDIR _PR_MD_MKDIR
+#define _MD_RMDIR _PR_MD_RMDIR
+#define _MD_LOCKFILE _PR_MD_LOCKFILE
+#define _MD_TLOCKFILE _PR_MD_TLOCKFILE
+#define _MD_UNLOCKFILE _PR_MD_UNLOCKFILE
+
+/* --- Socket IO stuff --- */
+
+/* The ones that don't map directly may need to be re-visited... */
+#define EPIPE EBADF
+#define EIO ECONNREFUSED
+#define _MD_EACCES EACCES
+#define _MD_EADDRINUSE EADDRINUSE
+#define _MD_EADDRNOTAVAIL EADDRNOTAVAIL
+#define _MD_EAFNOSUPPORT EAFNOSUPPORT
+#define _MD_EAGAIN EWOULDBLOCK
+#define _MD_EALREADY EALREADY
+#define _MD_EBADF EBADF
+#define _MD_ECONNREFUSED ECONNREFUSED
+#define _MD_ECONNRESET ECONNRESET
+#define _MD_EFAULT SOCEFAULT
+#define _MD_EINPROGRESS EINPROGRESS
+#define _MD_EINTR EINTR
+#define _MD_EINVAL EINVAL
+#define _MD_EISCONN EISCONN
+#define _MD_ENETUNREACH ENETUNREACH
+#define _MD_ENOENT ENOENT
+#define _MD_ENOTCONN ENOTCONN
+#define _MD_ENOTSOCK ENOTSOCK
+#define _MD_EOPNOTSUPP EOPNOTSUPP
+#define _MD_EWOULDBLOCK EWOULDBLOCK
+#define _MD_GET_SOCKET_ERROR() sock_errno()
+#ifndef INADDR_LOOPBACK /* For some reason this is not defined in OS2 tcpip */
+/* #define INADDR_LOOPBACK INADDR_ANY */
+#endif
+
+extern void _MD_MakeNonblock(PRFileDesc *f);
+#define _MD_MAKE_NONBLOCK _MD_MakeNonblock
+#define _MD_SHUTDOWN _PR_MD_SHUTDOWN
+#define _MD_LISTEN(s, backlog) listen(s->secret->md.osfd,backlog)
+extern PRInt32 _MD_CloseSocket(PRInt32 osfd);
+#define _MD_CLOSE_SOCKET _MD_CloseSocket
+#define _MD_SENDTO _PR_MD_SENDTO
+#define _MD_RECVFROM _PR_MD_RECVFROM
+#define _MD_SOCKETPAIR(s, type, proto, sv) -1
+#define _MD_GETSOCKNAME _PR_MD_GETSOCKNAME
+#define _MD_GETPEERNAME _PR_MD_GETPEERNAME
+#define _MD_GETSOCKOPT _PR_MD_GETSOCKOPT
+#define _MD_SETSOCKOPT _PR_MD_SETSOCKOPT
+#define _MD_SELECT select
+#define _MD_FSYNC _PR_MD_FSYNC
+
+#define _MD_INIT_ATOMIC _PR_MD_INIT_ATOMIC
+#define _MD_ATOMIC_INCREMENT(x) _PR_MD_ATOMIC_INCREMENT(x)
+#define _MD_ATOMIC_ADD(x,y) _PR_MD_ATOMIC_ADD(x,y)
+#define _MD_ATOMIC_DECREMENT(x) _PR_MD_ATOMIC_DECREMENT(x)
+#define _MD_ATOMIC_SET(x,y) _PR_MD_ATOMIC_SET(x, y)
+
+#define _MD_INIT_IO _PR_MD_INIT_IO
+#define _MD_TRANSMITFILE _PR_MD_TRANSMITFILE
+
+
+/* win95 doesn't have async IO */
+#define _MD_SOCKET _PR_MD_SOCKET
+extern PRInt32 _MD_SocketAvailable(PRFileDesc *fd);
+#define _MD_SOCKETAVAILABLE _MD_SocketAvailable
+#define _MD_CONNECT _PR_MD_CONNECT
+extern PRInt32 _MD_Accept(PRFileDesc *fd, PRNetAddr *raddr, PRUint32 *rlen,
+ PRIntervalTime timeout);
+#define _MD_ACCEPT _MD_Accept
+#define _MD_BIND _PR_MD_BIND
+#define _MD_RECV _PR_MD_RECV
+#define _MD_SEND _PR_MD_SEND
+
+/* --- Scheduler stuff --- */
+/* #define _MD_PAUSE_CPU _PR_MD_PAUSE_CPU */
+#define _MD_PAUSE_CPU
+
+/* --- DIR stuff --- */
+#define PR_DIRECTORY_SEPARATOR '\\'
+#define PR_DIRECTORY_SEPARATOR_STR "\\"
+#define PR_PATH_SEPARATOR ';'
+#define PR_PATH_SEPARATOR_STR ";"
+#define _MD_ERRNO() errno
+#define _MD_OPEN_DIR _PR_MD_OPEN_DIR
+#define _MD_CLOSE_DIR _PR_MD_CLOSE_DIR
+#define _MD_READ_DIR _PR_MD_READ_DIR
+
+/* --- Segment stuff --- */
+#define _MD_INIT_SEGS()
+#define _MD_ALLOC_SEGMENT(seg, size, vaddr) 0
+#define _MD_FREE_SEGMENT(seg)
+
+/* --- Environment Stuff --- */
+#define _MD_GET_ENV _PR_MD_GET_ENV
+#define _MD_PUT_ENV _PR_MD_PUT_ENV
+
+/* --- Threading Stuff --- */
+#define _MD_DEFAULT_STACK_SIZE 32767L
+#define _MD_INIT_THREAD _PR_MD_INIT_THREAD
+#define _MD_INIT_ATTACHED_THREAD _PR_MD_INIT_THREAD
+#define _MD_INIT_PRIMORDIAL_THREAD _PR_MD_INIT_PRIMORDIAL_THREAD
+#define _MD_CREATE_THREAD _PR_MD_CREATE_THREAD
+#define _MD_YIELD _PR_MD_YIELD
+#define _MD_SET_PRIORITY _PR_MD_SET_PRIORITY
+#define _MD_CLEAN_THREAD _PR_MD_CLEAN_THREAD
+#define _MD_SETTHREADAFFINITYMASK _PR_MD_SETTHREADAFFINITYMASK
+#define _MD_GETTHREADAFFINITYMASK _PR_MD_GETTHREADAFFINITYMASK
+#define _MD_EXIT_THREAD _PR_MD_EXIT_THREAD
+#define _MD_SUSPEND_THREAD _PR_MD_SUSPEND_THREAD
+#define _MD_RESUME_THREAD _PR_MD_RESUME_THREAD
+#define _MD_SUSPEND_CPU _PR_MD_SUSPEND_CPU
+#define _MD_RESUME_CPU _PR_MD_RESUME_CPU
+#define _MD_WAKEUP_CPUS _PR_MD_WAKEUP_CPUS
+#define _MD_BEGIN_SUSPEND_ALL()
+#define _MD_BEGIN_RESUME_ALL()
+#define _MD_END_SUSPEND_ALL()
+#define _MD_END_RESUME_ALL()
+
+/* --- Lock stuff --- */
+#define _PR_LOCK _MD_LOCK
+#define _PR_UNLOCK _MD_UNLOCK
+
+#define _MD_NEW_LOCK(lock) (DosCreateMutexSem(0, &((lock)->mutex), 0, 0),(lock)->notified.length=0,(lock)->notified.link=NULL,PR_SUCCESS)
+#define _MD_FREE_LOCK(lock) DosCloseMutexSem(((lock)->mutex))
+#define _MD_LOCK(lock) DosRequestMutexSem(((lock)->mutex), SEM_INDEFINITE_WAIT)
+#define _MD_TEST_AND_LOCK(lock) (DosRequestMutexSem(((lock)->mutex), SEM_INDEFINITE_WAIT),PR_SUCCESS)
+#define _MD_UNLOCK _PR_MD_UNLOCK
+
+/* --- lock and cv waiting --- */
+#define _MD_WAIT _PR_MD_WAIT
+#define _MD_WAKEUP_WAITER _PR_MD_WAKEUP_WAITER
+
+/* --- CVar ------------------- */
+#define _MD_WAIT_CV _PR_MD_WAIT_CV
+#define _MD_NEW_CV _PR_MD_NEW_CV
+#define _MD_FREE_CV _PR_MD_FREE_CV
+#define _MD_NOTIFY_CV _PR_MD_NOTIFY_CV
+#define _MD_NOTIFYALL_CV _PR_MD_NOTIFYALL_CV
+
+ /* XXXMB- the IOQ stuff is certainly not working correctly yet. */
+/* extern struct _MDLock _pr_ioq_lock; */
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+
+/* --- Initialization stuff --- */
+#define _MD_START_INTERRUPTS()
+#define _MD_STOP_INTERRUPTS()
+#define _MD_DISABLE_CLOCK_INTERRUPTS()
+#define _MD_ENABLE_CLOCK_INTERRUPTS()
+#define _MD_BLOCK_CLOCK_INTERRUPTS()
+#define _MD_UNBLOCK_CLOCK_INTERRUPTS()
+#define _MD_EARLY_INIT _PR_MD_EARLY_INIT
+#define _MD_FINAL_INIT()
+#define _MD_INIT_CPUS()
+#define _MD_INIT_RUNNING_CPU(cpu)
+
+struct PRProcess;
+struct PRProcessAttr;
+
+#define _MD_CREATE_PROCESS _PR_CreateOS2Process
+extern struct PRProcess * _PR_CreateOS2Process(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const struct PRProcessAttr *attr
+);
+
+#define _MD_DETACH_PROCESS _PR_DetachOS2Process
+extern PRStatus _PR_DetachOS2Process(struct PRProcess *process);
+
+/* --- Wait for a child process to terminate --- */
+#define _MD_WAIT_PROCESS _PR_WaitOS2Process
+extern PRStatus _PR_WaitOS2Process(struct PRProcess *process,
+ PRInt32 *exitCode);
+
+#define _MD_KILL_PROCESS _PR_KillOS2Process
+extern PRStatus _PR_KillOS2Process(struct PRProcess *process);
+
+#define _MD_CLEANUP_BEFORE_EXIT()
+#define _MD_INIT_CONTEXT
+#define _MD_SWITCH_CONTEXT
+#define _MD_RESTORE_CONTEXT
+
+/* --- Intervals --- */
+#define _MD_INTERVAL_INIT _PR_MD_INTERVAL_INIT
+#define _MD_GET_INTERVAL _PR_MD_GET_INTERVAL
+#define _MD_INTERVAL_PER_SEC _PR_MD_INTERVAL_PER_SEC
+#define _MD_INTERVAL_PER_MILLISEC() (_PR_MD_INTERVAL_PER_SEC() / 1000)
+#define _MD_INTERVAL_PER_MICROSEC() (_PR_MD_INTERVAL_PER_SEC() / 1000000)
+
+/* --- Native-Thread Specific Definitions ------------------------------- */
+
+typedef struct __NSPR_TLS
+{
+ struct PRThread *_pr_thread_last_run;
+ struct PRThread *_pr_currentThread;
+ struct _PRCPU *_pr_currentCPU;
+} _NSPR_TLS;
+
+extern _NSPR_TLS* pThreadLocalStorage;
+PR_IMPLEMENT(void) _PR_MD_ENSURE_TLS();
+
+#define _MD_CURRENT_THREAD() pThreadLocalStorage->_pr_currentThread
+#define _MD_SET_CURRENT_THREAD(_thread) _PR_MD_ENSURE_TLS(); pThreadLocalStorage->_pr_currentThread = (_thread)
+
+#define _MD_LAST_THREAD() pThreadLocalStorage->_pr_thread_last_run
+#define _MD_SET_LAST_THREAD(_thread) _PR_MD_ENSURE_TLS(); pThreadLocalStorage->_pr_thread_last_run = (_thread)
+
+#define _MD_CURRENT_CPU() pThreadLocalStorage->_pr_currentCPU
+#define _MD_SET_CURRENT_CPU(_cpu) _PR_MD_ENSURE_TLS(); pThreadLocalStorage->_pr_currentCPU = (_cpu)
+
+/* --- Scheduler stuff --- */
+#define LOCK_SCHEDULER() 0
+#define UNLOCK_SCHEDULER() 0
+#define _PR_LockSched() 0
+#define _PR_UnlockSched() 0
+
+/* --- Initialization stuff --- */
+#define _MD_INIT_LOCKS()
+
+/* --- Stack stuff --- */
+#define _MD_INIT_STACK(stack, redzone)
+#define _MD_CLEAR_STACK(stack)
+
+/* --- Memory-mapped files stuff --- not implemented on OS/2 */
+
+struct _MDFileMap {
+ PRInt8 unused;
+};
+
+extern PRStatus _MD_CreateFileMap(struct PRFileMap *fmap, PRInt64 size);
+#define _MD_CREATE_FILE_MAP _MD_CreateFileMap
+
+extern void * _MD_MemMap(struct PRFileMap *fmap, PRInt64 offset,
+ PRUint32 len);
+#define _MD_MEM_MAP _MD_MemMap
+
+extern PRStatus _MD_MemUnmap(void *addr, PRUint32 size);
+#define _MD_MEM_UNMAP _MD_MemUnmap
+
+extern PRStatus _MD_CloseFileMap(struct PRFileMap *fmap);
+#define _MD_CLOSE_FILE_MAP _MD_CloseFileMap
+
+/* Some stuff for setting up thread contexts */
+#undef DWORD
+#undef PDWORD
+typedef unsigned long DWORD;
+typedef unsigned long *PDWORD;
+
+/* The following definitions and two structures are new in OS/2 Warp 4.0.
+ */
+#ifndef CONTEXT_CONTROL
+#define CONTEXT_CONTROL 0x00000001
+#define CONTEXT_INTEGER 0x00000002
+#define CONTEXT_SEGMENTS 0x00000004
+#define CONTEXT_FLOATING_POINT 0x00000008
+#define CONTEXT_FULL 0x0000000F
+
+#pragma pack(2)
+typedef struct _FPREG {
+ ULONG losig; /* Low 32-bits of the significand. */
+ ULONG hisig; /* High 32-bits of the significand. */
+ USHORT signexp; /* Sign and exponent. */
+} FPREG;
+typedef struct _CONTEXTRECORD {
+ ULONG ContextFlags;
+ ULONG ctx_env[7];
+ FPREG ctx_stack[8];
+ ULONG ctx_SegGs; /* GS register. */
+ ULONG ctx_SegFs; /* FS register. */
+ ULONG ctx_SegEs; /* ES register. */
+ ULONG ctx_SegDs; /* DS register. */
+ ULONG ctx_RegEdi; /* EDI register. */
+ ULONG ctx_RegEsi; /* ESI register. */
+ ULONG ctx_RegEax; /* EAX register. */
+ ULONG ctx_RegEbx; /* EBX register. */
+ ULONG ctx_RegEcx; /* ECX register. */
+ ULONG ctx_RegEdx; /* EDX register. */
+ ULONG ctx_RegEbp; /* EBP register. */
+ ULONG ctx_RegEip; /* EIP register. */
+ ULONG ctx_SegCs; /* CS register. */
+ ULONG ctx_EFlags; /* EFLAGS register. */
+ ULONG ctx_RegEsp; /* ESP register. */
+ ULONG ctx_SegSs; /* SS register. */
+} CONTEXTRECORD, *PCONTEXTRECORD;
+#pragma pack()
+#endif
+
+extern APIRET (* APIENTRY QueryThreadContext)(OS2TID, ULONG, PCONTEXTRECORD);
+
+/*
+#define _pr_tid (((PTIB2)_getTIBvalue(offsetof(TIB, tib_ptib2)))->tib2_ultid)
+#define _pr_current_Thread (_system_tls[_pr_tid-1].__pr_current_thread)
+*/
+
+/* Some simple mappings of Windows API's to OS/2 API's to make our lives a
+ * little bit easier. Only add one here if it is a DIRECT mapping. We are
+ * not emulating anything. Just mapping.
+ */
+#define FreeLibrary(x) DosFreeModule(x)
+#define OutputDebugString(str) ;
+
+#endif /* nspr_os2_defs_h___ */
diff --git a/pr/include/md/_os2_errors.h b/pr/include/md/_os2_errors.h
new file mode 100644
index 00000000..17b33129
--- /dev/null
+++ b/pr/include/md/_os2_errors.h
@@ -0,0 +1,133 @@
+/* -*- 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_os2_errors_h___
+#define nspr_os2_errors_h___
+
+#include "md/_os2.h"
+#ifndef assert
+ #include <assert.h>
+#endif
+#include <nerrno.h>
+
+PR_EXTERN(void) _MD_os2_map_opendir_error(PRInt32 err);
+#define _PR_MD_MAP_OPENDIR_ERROR _MD_os2_map_opendir_error
+
+PR_EXTERN(void) _MD_os2_map_closedir_error(PRInt32 err);
+#define _PR_MD_MAP_CLOSEDIR_ERROR _MD_os2_map_closedir_error
+
+PR_EXTERN(void) _MD_unix_readdir_error(PRInt32 err);
+#define _PR_MD_MAP_READDIR_ERROR _MD_unix_readdir_error
+
+PR_EXTERN(void) _MD_os2_map_delete_error(PRInt32 err);
+#define _PR_MD_MAP_DELETE_ERROR _MD_os2_map_delete_error
+
+PR_EXTERN(void) _MD_os2_map_stat_error(PRInt32 err);
+#define _PR_MD_MAP_STAT_ERROR _MD_os2_map_stat_error
+
+PR_EXTERN(void) _MD_os2_map_fstat_error(PRInt32 err);
+#define _PR_MD_MAP_FSTAT_ERROR _MD_os2_map_fstat_error
+
+PR_EXTERN(void) _MD_os2_map_rename_error(PRInt32 err);
+#define _PR_MD_MAP_RENAME_ERROR _MD_os2_map_rename_error
+
+PR_EXTERN(void) _MD_os2_map_access_error(PRInt32 err);
+#define _PR_MD_MAP_ACCESS_ERROR _MD_os2_map_access_error
+
+PR_EXTERN(void) _MD_os2_map_mkdir_error(PRInt32 err);
+#define _PR_MD_MAP_MKDIR_ERROR _MD_os2_map_mkdir_error
+
+PR_EXTERN(void) _MD_os2_map_rmdir_error(PRInt32 err);
+#define _PR_MD_MAP_RMDIR_ERROR _MD_os2_map_rmdir_error
+
+PR_EXTERN(void) _MD_os2_map_read_error(PRInt32 err);
+#define _PR_MD_MAP_READ_ERROR _MD_os2_map_read_error
+
+PR_EXTERN(void) _MD_os2_map_transmitfile_error(PRInt32 err);
+#define _PR_MD_MAP_TRANSMITFILE_ERROR _MD_os2_map_transmitfile_error
+
+PR_EXTERN(void) _MD_os2_map_write_error(PRInt32 err);
+#define _PR_MD_MAP_WRITE_ERROR _MD_os2_map_write_error
+
+PR_EXTERN(void) _MD_os2_map_lseek_error(PRInt32 err);
+#define _PR_MD_MAP_LSEEK_ERROR _MD_os2_map_lseek_error
+
+PR_EXTERN(void) _MD_os2_map_fsync_error(PRInt32 err);
+#define _PR_MD_MAP_FSYNC_ERROR _MD_os2_map_fsync_error
+
+PR_EXTERN(void) _MD_os2_map_close_error(PRInt32 err);
+#define _PR_MD_MAP_CLOSE_ERROR _MD_os2_map_close_error
+
+PR_EXTERN(void) _MD_os2_map_socket_error(PRInt32 err);
+#define _PR_MD_MAP_SOCKET_ERROR _MD_os2_map_socket_error
+
+PR_EXTERN(void) _MD_os2_map_recv_error(PRInt32 err);
+#define _PR_MD_MAP_RECV_ERROR _MD_os2_map_recv_error
+
+PR_EXTERN(void) _MD_os2_map_recvfrom_error(PRInt32 err);
+#define _PR_MD_MAP_RECVFROM_ERROR _MD_os2_map_recvfrom_error
+
+PR_EXTERN(void) _MD_os2_map_send_error(PRInt32 err);
+#define _PR_MD_MAP_SEND_ERROR _MD_os2_map_send_error
+
+PR_EXTERN(void) _MD_os2_map_sendto_error(PRInt32 err);
+#define _PR_MD_MAP_SENDTO_ERROR _MD_os2_map_sendto_error
+
+PR_EXTERN(void) _MD_os2_map_accept_error(PRInt32 err);
+#define _PR_MD_MAP_ACCEPT_ERROR _MD_os2_map_accept_error
+
+PR_EXTERN(void) _MD_os2_map_acceptex_error(PRInt32 err);
+#define _PR_MD_MAP_ACCEPTEX_ERROR _MD_os2_map_acceptex_error
+
+PR_EXTERN(void) _MD_os2_map_connect_error(PRInt32 err);
+#define _PR_MD_MAP_CONNECT_ERROR _MD_os2_map_connect_error
+
+PR_EXTERN(void) _MD_os2_map_bind_error(PRInt32 err);
+#define _PR_MD_MAP_BIND_ERROR _MD_os2_map_bind_error
+
+PR_EXTERN(void) _MD_os2_map_listen_error(PRInt32 err);
+#define _PR_MD_MAP_LISTEN_ERROR _MD_os2_map_listen_error
+
+PR_EXTERN(void) _MD_os2_map_shutdown_error(PRInt32 err);
+#define _PR_MD_MAP_SHUTDOWN_ERROR _MD_os2_map_shutdown_error
+
+PR_EXTERN(void) _MD_os2_map_getsockname_error(PRInt32 err);
+#define _PR_MD_MAP_GETSOCKNAME_ERROR _MD_os2_map_getsockname_error
+
+PR_EXTERN(void) _MD_os2_map_getpeername_error(PRInt32 err);
+#define _PR_MD_MAP_GETPEERNAME_ERROR _MD_os2_map_getpeername_error
+
+PR_EXTERN(void) _MD_os2_map_getsockopt_error(PRInt32 err);
+#define _PR_MD_MAP_GETSOCKOPT_ERROR _MD_os2_map_getsockopt_error
+
+PR_EXTERN(void) _MD_os2_map_setsockopt_error(PRInt32 err);
+#define _PR_MD_MAP_SETSOCKOPT_ERROR _MD_os2_map_setsockopt_error
+
+PR_EXTERN(void) _MD_os2_map_open_error(PRInt32 err);
+#define _PR_MD_MAP_OPEN_ERROR _MD_os2_map_open_error
+
+PR_EXTERN(void) _MD_os2_map_gethostname_error(PRInt32 err);
+#define _PR_MD_MAP_GETHOSTNAME_ERROR _MD_os2_map_gethostname_error
+
+PR_EXTERN(void) _MD_os2_map_select_error(PRInt32 err);
+#define _PR_MD_MAP_SELECT_ERROR _MD_os2_map_select_error
+
+PR_EXTERN(void) _MD_os2_map_lockf_error(int err);
+#define _PR_MD_MAP_LOCKF_ERROR _MD_os2_map_lockf_error
+
+#endif /* nspr_os2_errors_h___ */
diff --git a/pr/include/md/_osf1.cfg b/pr/include/md/_osf1.cfg
new file mode 100644
index 00000000..27ef81ca
--- /dev/null
+++ b/pr/include/md/_osf1.cfg
@@ -0,0 +1,125 @@
+/* -*- 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_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef OSF1
+#define OSF1
+#endif
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define HAVE_LONG_LONG
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+#ifndef IS_64
+#define IS_64
+#endif
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#define _PR_POLL_BACKCOMPAT
+
+#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/include/md/_osf1.h b/pr/include/md/_osf1.h
new file mode 100644
index 00000000..ab2baadd
--- /dev/null
+++ b/pr/include/md/_osf1.h
@@ -0,0 +1,209 @@
+/* -*- 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_osf1_defs_h___
+#define nspr_osf1_defs_h___
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "osf"
+#define _PR_SI_SYSNAME "OSF"
+#define _PR_SI_ARCHITECTURE "alpha"
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 131072L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#undef HAVE_STACK_GROWING_UP
+#undef HAVE_WEAK_IO_SYMBOLS
+#undef HAVE_WEAK_MALLOC_SYMBOLS
+#define HAVE_DLL
+#define HAVE_BSD_FLOCK
+
+#define NEED_TIME_R
+#define USE_DLFCN
+
+#define _PR_HAVE_ATOMIC_OPS
+#define _PR_HAVE_ATOMIC_CAS
+#define _PR_POLL_AVAILABLE
+#define _PR_USE_POLL
+#define _PR_STAT_HAS_ONLY_ST_ATIME
+#define _PR_HAVE_LARGE_OFF_T
+
+#define USE_SETJMP
+
+#include <setjmp.h>
+
+/*
+ * A jmp_buf is actually a struct sigcontext. The sc_sp field of
+ * struct sigcontext is the stack pointer.
+ */
+#define _MD_GET_SP(_t) (((struct sigcontext *) (_t)->md.context)->sc_sp)
+#define PR_NUM_GCREGS _JBLEN
+#define CONTEXT(_th) ((_th)->md.context)
+
+/*
+** Initialize a thread context to run "_main()" when started
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if (setjmp(CONTEXT(_thread))) { \
+ (*_main)(); \
+ } \
+ _MD_GET_SP(_thread) = (long) ((_sp) - 64); \
+ _MD_GET_SP(_thread) &= ~15; \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!setjmp(CONTEXT(_thread))) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ longjmp(CONTEXT(_thread), 1); \
+}
+
+/* Machine-dependent (MD) data structures */
+
+struct _MDThread {
+ jmp_buf context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#ifndef _PR_PTHREADS
+#define _MD_INIT_LOCKS()
+#endif
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+/*
+ * The following are copied from _sunos.h, _aix.h. This means
+ * some of them should probably be moved into _unixos.h. But
+ * _irix.h seems to be quite different in regard to these macros.
+ */
+#define _MD_GET_INTERVAL _PR_UNIX_GetInterval
+#define _MD_INTERVAL_PER_SEC _PR_UNIX_TicksPerSecond
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread)
+#define _MD_RESUME_THREAD(thread)
+#define _MD_CLEAN_THREAD(_thread)
+
+/* The following defines unwrapped versions of select() and poll(). */
+extern int __select (int, fd_set *, fd_set *, fd_set *, struct timeval *);
+#define _MD_SELECT __select
+
+#include <sys/poll.h>
+#define _MD_POLL __poll
+extern int __poll(struct pollfd filedes[], unsigned int nfds, int timeout);
+
+/*
+ * Atomic operations
+ */
+#ifdef _PR_HAVE_ATOMIC_OPS
+#include <machine/builtins.h>
+#define _MD_INIT_ATOMIC()
+#define _MD_ATOMIC_INCREMENT(val) (__ATOMIC_INCREMENT_LONG(val) + 1)
+#define _MD_ATOMIC_ADD(ptr, val) (__ATOMIC_ADD_LONG(ptr, val) + val)
+#define _MD_ATOMIC_DECREMENT(val) (__ATOMIC_DECREMENT_LONG(val) - 1)
+#define _MD_ATOMIC_SET(val, newval) __ATOMIC_EXCH_LONG(val, newval)
+#endif /* _PR_HAVE_ATOMIC_OPS */
+
+#endif /* nspr_osf1_defs_h___ */
diff --git a/pr/include/md/_pcos.h b/pr/include/md/_pcos.h
new file mode 100644
index 00000000..6e325b36
--- /dev/null
+++ b/pr/include/md/_pcos.h
@@ -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.
+ */
+
+#ifndef prpcos_h___
+#define prpcos_h___
+
+#define PR_DLL_SUFFIX ".dll"
+
+#ifndef OS2
+#include <windows.h>
+#endif
+#include <setjmp.h>
+#include <stdlib.h>
+
+#define USE_SETJMP
+#define DIRECTORY_SEPARATOR '\\'
+#define DIRECTORY_SEPARATOR_STR "\\"
+#define PATH_SEPARATOR ';'
+
+#ifdef WIN16
+#define GCPTR __far
+#else
+#define GCPTR
+#endif
+
+/*
+** Routines for processing command line arguments
+*/
+PR_BEGIN_EXTERN_C
+
+extern char *optarg;
+extern int optind;
+extern int getopt(int argc, char **argv, char *spec);
+
+PR_END_EXTERN_C
+
+#define gcmemcpy(a,b,c) memcpy(a,b,c)
+
+
+/*
+** Definitions of directory structures amd functions
+** These definitions are from:
+** <dirent.h>
+*/
+#ifdef MOZ_BITS
+#include <time.h>
+#endif
+#include <sys/stat.h>
+#include <io.h>
+#include <fcntl.h> /* O_BINARY */
+
+typedef int PROSFD;
+
+/*
+** Undo the macro define in the Microsoft header files...
+*/
+#ifndef XP_OS2 /* Uh... this seems a bit insane in itself to we OS/2 folk */
+#ifdef _stat
+#undef _stat
+#endif
+#endif
+
+#ifdef OS2
+extern PRStatus _MD_OS2GetHostName(char *name, PRUint32 namelen);
+#define _MD_GETHOSTNAME _MD_OS2GetHostName
+#else
+extern PRStatus _MD_WindowsGetHostName(char *name, PRUint32 namelen);
+#define _MD_GETHOSTNAME _MD_WindowsGetHostName
+#endif
+
+#endif /* prpcos_h___ */
diff --git a/pr/include/md/_pth.h b/pr/include/md/_pth.h
new file mode 100644
index 00000000..9ca91a74
--- /dev/null
+++ b/pr/include/md/_pth.h
@@ -0,0 +1,223 @@
+/* -*- 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_pth_defs_h_
+#define nspr_pth_defs_h_
+
+/*
+** Appropriate definitions of entry points not used in a pthreads world
+*/
+#define _PR_MD_BLOCK_CLOCK_INTERRUPTS()
+#define _PR_MD_UNBLOCK_CLOCK_INTERRUPTS()
+#define _PR_MD_DISABLE_CLOCK_INTERRUPTS()
+#define _PR_MD_ENABLE_CLOCK_INTERRUPTS()
+
+/* In good standards fashion, the DCE threads (based on posix-4) are not
+ * quite the same as newer posix implementations. These are mostly name
+ * changes and small differences, so macros usually do the trick
+ */
+#ifdef _PR_DCETHREADS
+#define PTHREAD_MUTEXATTR_INIT pthread_mutexattr_create
+#define PTHREAD_MUTEXATTR_DESTROY pthread_mutexattr_delete
+#define PTHREAD_MUTEX_INIT(m, a) pthread_mutex_init(&(m), a)
+#define PTHREAD_MUTEX_IS_LOCKED(m) (0 == pthread_mutex_trylock(&(m)))
+#define PTHREAD_CONDATTR_INIT pthread_condattr_create
+#define PTHREAD_COND_INIT(m, a) pthread_cond_init(&(m), a)
+#define PTHREAD_CONDATTR_DESTROY pthread_condattr_delete
+
+/* Notes about differences between DCE threads and pthreads 10:
+ * 1. pthread_mutex_trylock returns 1 when it locks the mutex
+ * 0 when it does not. The latest pthreads has a set of errno-like
+ * return values.
+ * 2. return values from pthread_cond_timedwait are different.
+ *
+ *
+ *
+ */
+#else
+#define PTHREAD_MUTEXATTR_INIT pthread_mutexattr_init
+#define PTHREAD_MUTEXATTR_DESTROY pthread_mutexattr_destroy
+#define PTHREAD_MUTEX_INIT(m, a) pthread_mutex_init(&(m), &(a))
+#define PTHREAD_MUTEX_IS_LOCKED(m) (EBUSY == pthread_mutex_trylock(&(m)))
+#define PTHREAD_CONDATTR_INIT pthread_condattr_init
+#define PTHREAD_CONDATTR_DESTROY pthread_condattr_destroy
+#define PTHREAD_COND_INIT(m, a) pthread_cond_init(&(m), &(a))
+#endif
+
+/* The pthread_t handle used to identify a thread can vary in size
+ * with different implementations of pthreads. This macro defines
+ * a way to get a unique identifier from the handle. This may be
+ * more of a problem as we adapt to more pthreads implementations.
+ */
+#if defined(_PR_DCETHREADS)
+#define PTHREAD_ZERO_THR_HANDLE(t) memset(&(t), 0, sizeof(pthread_t))
+#define PTHREAD_THR_HANDLE_IS_ZERO(t) \
+ (!memcmp(&(t), &pt_zero_tid, sizeof(pthread_t)))
+#define PTHREAD_COPY_THR_HANDLE(st, dt) (dt) = (st)
+#elif defined(IRIX) || defined(OSF1) || defined(AIX) || defined(SOLARIS) \
+ || defined(HPUX) || defined(LINUX) || defined(FREEBSD) \
+ || defined(NETBSD) || defined(OPENBSD)
+#define PTHREAD_ZERO_THR_HANDLE(t) (t) = 0
+#define PTHREAD_THR_HANDLE_IS_ZERO(t) (t) == 0
+#define PTHREAD_COPY_THR_HANDLE(st, dt) (dt) = (st)
+#else
+#error "pthreads is not supported for this architecture"
+#endif
+
+#if defined(_PR_DCETHREADS)
+#define PTHREAD_ATTR_INIT pthread_attr_create
+#define PTHREAD_ATTR_DESTROY pthread_attr_delete
+#define PTHREAD_CREATE(t, a, f, r) pthread_create(t, a, f, r)
+#define PTHREAD_KEY_CREATE pthread_keycreate
+#define PTHREAD_ATTR_SETSCHEDPOLICY pthread_attr_setsched
+#define PTHREAD_ATTR_GETSTACKSIZE(a, s) \
+ (*(s) = pthread_attr_getstacksize(*(a)), 0)
+#define PTHREAD_GETSPECIFIC(k, r) \
+ pthread_getspecific((k), (pthread_addr_t *) &(r))
+#elif defined(_PR_PTHREADS)
+#define PTHREAD_ATTR_INIT pthread_attr_init
+#define PTHREAD_ATTR_DESTROY pthread_attr_destroy
+#define PTHREAD_CREATE(t, a, f, r) pthread_create(t, &a, f, r)
+#define PTHREAD_KEY_CREATE pthread_key_create
+#define PTHREAD_ATTR_SETSCHEDPOLICY pthread_attr_setschedpolicy
+#define PTHREAD_ATTR_GETSTACKSIZE(a, s) pthread_attr_getstacksize(a, s)
+#define PTHREAD_GETSPECIFIC(k, r) (r) = pthread_getspecific(k)
+#else
+#error "Cannot determine pthread strategy"
+#endif
+
+#if defined(_PR_DCETHREADS)
+#define PTHREAD_EXPLICIT_SCHED PTHREAD_DEFAULT_SCHED
+#endif
+
+/*
+ * pthread_mutex_trylock returns different values in DCE threads and
+ * pthreads.
+ */
+#if defined(_PR_DCETHREADS)
+#define PT_TRYLOCK_SUCCESS 1
+#define PT_TRYLOCK_BUSY 0
+#else
+#define PT_TRYLOCK_SUCCESS 0
+#define PT_TRYLOCK_BUSY EBUSY
+#endif
+
+/*
+ * These platforms don't have pthread_atfork()
+ */
+#if defined(_PR_DCETHREADS) || defined(FREEBSD) \
+ || (defined(LINUX) && defined(__alpha)) \
+ || defined(NETBSD) || defined(OPENBSD)
+#define PT_NO_ATFORK
+#endif
+
+/*
+ * These platforms don't have sigtimedwait()
+ */
+#if (defined(AIX) && !defined(AIX4_3)) || defined(LINUX) \
+ || defined(FREEBSD) || defined(NETBSD) || defined(OPENBSD)
+#define PT_NO_SIGTIMEDWAIT
+#endif
+
+#if defined(OSF1)
+#define PT_PRIO_MIN PRI_OTHER_MIN
+#define PT_PRIO_MAX PRI_OTHER_MAX
+#elif defined(IRIX)
+#include <sys/sched.h>
+#define PT_PRIO_MIN PX_PRIO_MIN
+#define PT_PRIO_MAX PX_PRIO_MAX
+#elif defined(AIX)
+#include <sys/priv.h>
+#include <sys/sched.h>
+#ifndef PTHREAD_CREATE_JOINABLE
+#define PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED
+#endif
+#define PT_PRIO_MIN DEFAULT_PRIO
+#define PT_PRIO_MAX DEFAULT_PRIO
+#elif defined(HPUX)
+
+#if defined(_PR_DCETHREADS)
+#define PT_PRIO_MIN PRI_OTHER_MIN
+#define PT_PRIO_MAX PRI_OTHER_MAX
+#else /* defined(_PR_DCETHREADS) */
+#include <sys/sched.h>
+#define PT_PRIO_MIN sched_get_priority_min(SCHED_OTHER)
+#define PT_PRIO_MAX sched_get_priority_max(SCHED_OTHER)
+#endif /* defined(_PR_DCETHREADS) */
+
+#elif defined(LINUX)
+#define PT_PRIO_MIN sched_get_priority_min(SCHED_OTHER)
+#define PT_PRIO_MAX sched_get_priority_max(SCHED_OTHER)
+#elif defined(SOLARIS)
+/*
+ * Solaris doesn't seem to have macros for the min/max priorities.
+ * The range of 0-127 is mentioned in the pthread_setschedparam(3T)
+ * man pages, and pthread_setschedparam indeed allows 0-127. However,
+ * pthread_attr_setschedparam does not allow 0; it allows 1-127.
+ */
+#define PT_PRIO_MIN 1
+#define PT_PRIO_MAX 127
+#elif defined(FREEBSD) || defined(NETBSD) || defined(OPENBSD) /* XXX */
+#define PT_PRIO_MIN 0
+#define PT_PRIO_MAX 126
+#else
+#error "pthreads is not supported for this architecture"
+#endif
+
+/*
+ * The PTHREAD_YIELD function is called from a signal handler.
+ * Needed for garbage collection -- Look at PR_Suspend/PR_Resume
+ * implementation.
+ */
+#if defined(_PR_DCETHREADS)
+#define PTHREAD_YIELD() pthread_yield()
+#elif defined(OSF1)
+/*
+ * sched_yield can't be called from a signal handler. Must use
+ * the _np version.
+ */
+#define PTHREAD_YIELD() pthread_yield_np()
+#elif defined(AIX)
+extern int (*_PT_aix_yield_fcn)();
+#define PTHREAD_YIELD() (*_PT_aix_yield_fcn)()
+#elif defined(IRIX)
+#include <time.h>
+#define PTHREAD_YIELD() \
+ PR_BEGIN_MACRO \
+ struct timespec onemillisec = {0}; \
+ onemillisec.tv_nsec = 1000000L; \
+ nanosleep(&onemillisec,NULL); \
+ PR_END_MACRO
+#elif defined(HPUX) || defined(LINUX) || defined(SOLARIS) \
+ || defined(FREEBSD) || defined(NETBSD) || defined(OPENBSD)
+#define PTHREAD_YIELD() sched_yield()
+#else
+#error "Need to define PTHREAD_YIELD for this platform"
+#endif
+
+/*
+** And when you really wanted hardcore locking w/o any fluff ...
+**
+** ... and why would you want that????
+*/
+#define _PR_LOCK_LOCK(_lock) pthread_mutex_lock(&_lock->mutex);
+#define _PR_LOCK_UNLOCK(_lock) pthread_mutex_unlock(&_lock->mutex);
+
+#define _MD_INIT_LOCKS()
+
+#endif /* nspr_pth_defs_h_ */
diff --git a/pr/include/md/_reliantunix.cfg b/pr/include/md/_reliantunix.cfg
new file mode 100644
index 00000000..9cf253d8
--- /dev/null
+++ b/pr/include/md/_reliantunix.cfg
@@ -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.
+ */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef RELIANTUNIX
+#define RELIANTUNIX
+#endif
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#undef HAVE_LONG_LONG
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_DOUBLE 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_WORD 32
+#define PR_BITS_PER_DWORD 64
+#define PR_BITS_PER_DOUBLE 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_WORD_LOG2 5
+#define PR_BITS_PER_DWORD_LOG2 6
+#define PR_BITS_PER_DOUBLE_LOG2 6
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+#define PR_WORDS_PER_DWORD_LOG2 1
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_WORD 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+
+#define _PR_POLL_BACKCOMPAT
+
+#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/include/md/_reliantunix.h b/pr/include/md/_reliantunix.h
new file mode 100644
index 00000000..a327b122
--- /dev/null
+++ b/pr/include/md/_reliantunix.h
@@ -0,0 +1,249 @@
+/* -*- 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.
+ */
+
+/*
+ * reliantunix.h
+ * 5/18/96 Taken from nec.h -- chrisk@netscape.com
+ * 3/14/97 Modified for nspr20 -- chrisk@netscape.com
+ */
+#ifndef nspr_reliantunix_defs_h___
+#define nspr_reliantunix_defs_h___
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "sinix"
+#define _PR_SI_SYSNAME "SINIX"
+#define _PR_SI_ARCHITECTURE "mips"
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE (2*65536L)
+#define _MD_MMAP_FLAGS MAP_PRIVATE|MAP_FIXED
+
+#undef HAVE_STACK_GROWING_UP
+#define HAVE_DLL
+#define USE_DLFCN
+#define NEED_STRFTIME_LOCK
+#define NEED_TIME_R
+#define HAVE_NETCONFIG
+#define HAVE_WEAK_IO_SYMBOLS
+#define HAVE_WEAK_MALLOC_SYMBOLS
+#define _PR_RECV_BROKEN /* recv doesn't work on Unix Domain Sockets */
+#define _PR_POLL_AVAILABLE
+#define _PR_USE_POLL
+
+/*
+ * Mike Patnode indicated that it is possibly safe now to use context-switching
+ * calls that do not change the signal mask, like setjmp vs. sigsetjmp.
+ * So we'll use our homegrown, getcontext/setcontext-compatible stuff which
+ * will save us the getcontext/setcontext system calls at each context switch.
+ * It already works in FastTrack 2.01, so it should do it here :-)
+ * - chrisk 040497
+ */
+#define USE_SETCXT /* temporarily disabled... */
+
+#include <ucontext.h>
+
+#ifdef USE_SETCXT
+/* use non-syscall machine language replacement */
+#define _GETCONTEXT getcxt
+#define _SETCONTEXT setcxt
+/* defined in os_ReliantUNIX.s */
+extern int getcxt(ucontext_t *);
+extern int setcxt(ucontext_t *);
+#else
+#define _GETCONTEXT getcontext
+#define _SETCONTEXT setcontext
+#endif
+
+#define _MD_GET_SP(_t) (_t)->md.context.uc_mcontext.gpregs[CXT_SP]
+#define _PR_CONTEXT_TYPE ucontext_t
+#define _PR_NUM_GCREGS NGREG
+
+#define CONTEXT(_thread) (&(_thread)->md.context)
+
+#define _PR_IS_NATIVE_THREAD_SUPPORTED() 0
+
+/*
+** Machine-dependent (MD) data structures.
+*/
+struct _MDThread {
+ _PR_CONTEXT_TYPE context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+/*
+** Initialize the thread context preparing it to execute "_main()"
+** - get a nice, fresh context
+** - set its SP to the stack we allcoated for it
+** - set it to start things at "e"
+*/
+#define _MD_INIT_CONTEXT(thread, _sp, _main, status) \
+ PR_BEGIN_MACRO \
+ *status = PR_TRUE; \
+ _GETCONTEXT(CONTEXT(thread)); \
+ /* this is supposed to point to the stack BASE, not to SP */ \
+ CONTEXT(thread)->uc_stack.ss_sp = thread->stack->stackBottom; \
+ CONTEXT(thread)->uc_stack.ss_size = thread->stack->stackSize; \
+ CONTEXT(thread)->uc_mcontext.gpregs[CXT_SP] = ((unsigned long)_sp - 128) & 0xfffffff8; \
+ CONTEXT(thread)->uc_mcontext.gpregs[CXT_T9] = _main; \
+ CONTEXT(thread)->uc_mcontext.gpregs[CXT_EPC] = _main; \
+ CONTEXT(thread)->uc_mcontext.gpregs[CXT_RA] = 0; \
+ thread->no_sched = 0; \
+ PR_END_MACRO
+
+/*
+** Save current context as it is scheduled away
+*/
+#define _MD_SWITCH_CONTEXT(_thread) \
+ PR_BEGIN_MACRO \
+ if (!_GETCONTEXT(CONTEXT(_thread))) { \
+ _MD_SAVE_ERRNO(_thread); \
+ _MD_SET_LAST_THREAD(_thread); \
+ _PR_Schedule(); \
+ } \
+ PR_END_MACRO
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT or set up
+** by _MD_INIT_CONTEXT
+** CXT_V0 is the register that holds the return value.
+** We must set it to 1 so that we can see if the return from
+** getcontext() is the result of calling getcontext() or
+** setcontext()...
+** setting a context got with getcontext() appears to
+** return from getcontext(), too!
+** CXT_A3 is the register that holds status when returning
+** from a syscall. It is set to 0 to indicate success,
+** because we want getcontext() on the other side of the magic
+** door to be ok.
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+ PR_BEGIN_MACRO \
+ ucontext_t *uc = CONTEXT(_thread); \
+ uc->uc_mcontext.gpregs[CXT_V0] = 1;\
+ uc->uc_mcontext.gpregs[CXT_A3] = 0;\
+ _MD_RESTORE_ERRNO(_thread); \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ _SETCONTEXT(uc); \
+ PR_END_MACRO
+
+#define _MD_SAVE_ERRNO(t) (t)->md.errcode = errno;
+#define _MD_RESTORE_ERRNO(t) errno = (t)->md.errcode;
+
+#define _MD_GET_INTERVAL _PR_UNIX_GetInterval
+#define _MD_INTERVAL_PER_SEC _PR_UNIX_TicksPerSecond
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread)
+#define _MD_RESUME_THREAD(thread)
+#define _MD_CLEAN_THREAD(_thread)
+
+#if !defined(S_ISSOCK) && defined(S_IFSOCK)
+#define S_ISSOCK(mode) ((mode&0xF000) == 0xC000)
+#endif
+#if !defined(S_ISLNK) && defined(S_IFLNK)
+#define S_ISLNK(mode) ((mode&0xA000) == 0xC000)
+#endif
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/select.h>
+extern int _select(int nfds, fd_set *readfds, fd_set *writefds,
+ fd_set *execptfds, struct timeval *timeout);
+#define _MD_SELECT(nfds,r,w,e,tv) _select(nfds,r,w,e,tv)
+#define _MD_POLL _poll
+
+#endif /* nspr_reliantunix_defs_h___ */
diff --git a/pr/include/md/_rhapsody.cfg b/pr/include/md/_rhapsody.cfg
new file mode 100644
index 00000000..a0787fc9
--- /dev/null
+++ b/pr/include/md/_rhapsody.cfg
@@ -0,0 +1,127 @@
+/* -*- 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_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef RHAPOSDY
+#define RHAPOSDY
+#endif
+
+#if defined(i386)
+#undef IS_BIG_ENDIAN
+#define IS_LITTLE_ENDIAN 1
+#else
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#endif
+
+#define HAVE_LONG_LONG
+#undef HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+#define PR_BITS_PER_DWORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#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/include/md/_rhapsody.h b/pr/include/md/_rhapsody.h
new file mode 100644
index 00000000..476b8e38
--- /dev/null
+++ b/pr/include/md/_rhapsody.h
@@ -0,0 +1,205 @@
+/* -*- 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_rhapsody_defs_h___
+#define nspr_rhapsody_defs_h___
+
+#include "prthread.h"
+
+#include <sys/syscall.h>
+
+#define PR_LINKER_ARCH "rhapsody"
+#define _PR_SI_SYSNAME "RHAPSODY"
+#ifdef i386
+#define _PR_SI_ARCHITECTURE "x86"
+#else
+#define _PR_SI_ARCHITECTURE "ppc"
+#endif
+#define PR_DLL_SUFFIX ".dylib"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#undef HAVE_STACK_GROWING_UP
+#define HAVE_DLL
+#define _PR_HAVE_SOCKADDR_LEN
+#define _PR_STAT_HAS_ST_ATIMESPEC
+#define _PR_TIMESPEC_HAS_TS_SEC
+#define _PR_NO_LARGE_FILES
+
+#define USE_SETJMP
+
+#if !defined(_PR_PTHREADS)
+
+#include <setjmp.h>
+
+#define PR_CONTEXT_TYPE jmp_buf
+
+#define CONTEXT(_th) ((_th)->md.context)
+#define _MD_GET_SP(_th) (((struct sigcontext *) (_th)->md.context)->sc_onstack)
+#define PR_NUM_GCREGS _JBLEN
+
+/*
+** Initialize a thread context to run "_main()" when started
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if (setjmp(CONTEXT(_thread))) { \
+ _main(); \
+ } \
+ _MD_GET_SP(_thread) = (unsigned char*) ((_sp) - 64); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!setjmp(CONTEXT(_thread))) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ longjmp(CONTEXT(_thread), 1); \
+}
+
+/* Machine-dependent (MD) data structures */
+
+struct _MDThread {
+ PR_CONTEXT_TYPE context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+extern PRStatus _MD_InitializeThread(PRThread *thread);
+
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread) _MD_suspend_thread
+#define _MD_RESUME_THREAD(thread) _MD_resume_thread
+#define _MD_CLEAN_THREAD(_thread)
+
+extern PRStatus _MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize);
+extern void _MD_SET_PRIORITY(struct _MDThread *thread, PRUintn newPri);
+extern PRStatus _MD_WAIT(PRThread *, PRIntervalTime timeout);
+extern PRStatus _MD_WAKEUP_WAITER(PRThread *);
+extern void _MD_YIELD(void);
+
+#endif /* ! _PR_PTHREADS */
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_GET_INTERVAL _PR_UNIX_GetInterval
+#define _MD_INTERVAL_PER_SEC _PR_UNIX_TicksPerSecond
+
+extern void _MD_EarlyInit(void);
+extern PRIntervalTime _PR_UNIX_GetInterval(void);
+extern PRIntervalTime _PR_UNIX_TicksPerSecond(void);
+
+/*
+ * We wrapped the select() call. _MD_SELECT refers to the built-in,
+ * unwrapped version.
+ */
+#define _MD_SELECT(nfds,r,w,e,tv) syscall(SYS_select,nfds,r,w,e,tv)
+
+/* For writev() */
+#include <sys/uio.h>
+
+#endif /* nspr_rhapsody_defs_h___ */
diff --git a/pr/include/md/_scoos.cfg b/pr/include/md/_scoos.cfg
new file mode 100644
index 00000000..6ef8fa90
--- /dev/null
+++ b/pr/include/md/_scoos.cfg
@@ -0,0 +1,121 @@
+/* -*- 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_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef SCO
+#define SCO
+#endif
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#undef HAVE_LONG_LONG
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#define _PR_POLL_BACKCOMPAT
+
+#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/include/md/_scoos.h b/pr/include/md/_scoos.h
new file mode 100644
index 00000000..515cd233
--- /dev/null
+++ b/pr/include/md/_scoos.h
@@ -0,0 +1,185 @@
+/* -*- 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_scoos5_defs_h___
+#define nspr_scoos5_defs_h___
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "scoos5"
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_SI_SYSNAME "SCO"
+#define _PR_SI_ARCHITECTURE "x86"
+#define _PR_STACK_VMBASE 0x50000000
+
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#undef HAVE_STACK_GROWING_UP
+#define HAVE_DLL
+#define USE_DLFCN
+
+#if !defined (HAVE_STRERROR)
+#define HAVE_STRERROR
+#endif
+
+#ifndef HAVE_WEAK_IO_SYMBOLS
+#define HAVE_WEAK_IO_SYMBOLS
+#endif
+
+#define _PR_POLL_AVAILABLE
+#define _PR_USE_POLL
+#define _PR_NO_LARGE_FILES
+#define _PR_STAT_HAS_ONLY_ST_ATIME
+
+#define NEED_STRFTIME_LOCK
+#define NEED_TIME_R
+#define _PR_RECV_BROKEN /* recv doesn't work on Unix Domain Sockets */
+
+#define USE_SETJMP
+
+#ifdef _PR_LOCAL_THREADS_ONLY
+#include <setjmp.h>
+
+#define _MD_GET_SP(_t) (_t)->md.jb[4]
+#define PR_NUM_GCREGS _SIGJBLEN
+#define PR_CONTEXT_TYPE sigjmp_buf
+
+#define CONTEXT(_th) ((_th)->md.jb)
+
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if (sigsetjmp(CONTEXT(_thread),1)) { \
+ (*_main)(); \
+ } \
+ _MD_GET_SP(_thread) = (int) ((_sp) - 64); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!sigsetjmp(CONTEXT(_thread), 1)) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->osErrorCode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ siglongjmp(CONTEXT(_thread), 1); \
+}
+
+#endif /* _PR_LOCAL_THREADS_ONLY */
+
+struct _MDThread {
+ jmp_buf jb;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread)
+#define _MD_RESUME_THREAD(thread)
+#define _MD_CLEAN_THREAD(_thread)
+
+#define _MD_GET_INTERVAL _PR_UNIX_GetInterval
+#define _MD_INTERVAL_PER_SEC _PR_UNIX_TicksPerSecond
+
+#define _MD_SELECT _select
+#define _MD_POLL _poll
+
+#endif /* nspr_scoos5_defs_h___ */
diff --git a/pr/include/md/_solaris.cfg b/pr/include/md/_solaris.cfg
new file mode 100644
index 00000000..d41f1b53
--- /dev/null
+++ b/pr/include/md/_solaris.cfg
@@ -0,0 +1,129 @@
+/* -*- 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_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef SOLARIS
+#define SOLARIS
+#endif
+
+#ifdef sparc
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_DOUBLE 8
+#elif defined(i386)
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_DOUBLE 4
+#else
+#error unknown processor
+#endif
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_POINTER 4
+
+#define HAVE_LONG_LONG
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+
+#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 /* ifndef nspr_cpucfg___ */
diff --git a/pr/include/md/_solaris.h b/pr/include/md/_solaris.h
new file mode 100644
index 00000000..616c666b
--- /dev/null
+++ b/pr/include/md/_solaris.h
@@ -0,0 +1,709 @@
+/* -*- 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_solaris_defs_h___
+#define nspr_solaris_defs_h___
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "solaris"
+#define _PR_SI_SYSNAME "SOLARIS"
+#define _PR_SI_ARCHITECTURE "sparc"
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE (2*65536L)
+#define _MD_MMAP_FLAGS MAP_SHARED
+
+#undef HAVE_STACK_GROWING_UP
+
+#ifndef HAVE_WEAK_IO_SYMBOLS
+#define HAVE_WEAK_IO_SYMBOLS
+#endif
+
+#undef HAVE_WEAK_MALLOC_SYMBOLS
+#define HAVE_DLL
+#define USE_DLFCN
+#define NEED_STRFTIME_LOCK
+
+#ifdef _PR_LOCAL_THREADS_ONLY
+#undef _PR_HAVE_ATOMIC_OPS
+#else
+#define _PR_HAVE_ATOMIC_OPS
+#define _PR_HAVE_ATOMIC_CAS
+#endif
+
+#define _PR_POLL_AVAILABLE
+#define _PR_USE_POLL
+#define _PR_STAT_HAS_ST_ATIM
+
+#include "prinrval.h"
+PR_EXTERN(PRIntervalTime) _MD_Solaris_GetInterval(void);
+#define _MD_GET_INTERVAL _MD_Solaris_GetInterval
+PR_EXTERN(PRIntervalTime) _MD_Solaris_TicksPerSecond(void);
+#define _MD_INTERVAL_PER_SEC _MD_Solaris_TicksPerSecond
+
+#if defined(_PR_HAVE_ATOMIC_OPS)
+/*
+** Atomic Operations
+*/
+#define _MD_INIT_ATOMIC()
+
+PR_EXTERN(PRInt32) _MD_AtomicIncrement(PRInt32 *val);
+#define _MD_ATOMIC_INCREMENT _MD_AtomicIncrement
+
+PR_EXTERN(PRInt32) _MD_AtomicAdd(PRInt32 *ptr, PRInt32 val);
+#define _MD_ATOMIC_ADD _MD_AtomicAdd
+
+PR_EXTERN(PRInt32) _MD_AtomicDecrement(PRInt32 *val);
+#define _MD_ATOMIC_DECREMENT _MD_AtomicDecrement
+
+PR_EXTERN(PRInt32) _MD_AtomicSet(PRInt32 *val, PRInt32 newval);
+#define _MD_ATOMIC_SET _MD_AtomicSet
+#endif /* _PR_HAVE_ATOMIC_OPS */
+
+#if defined(_PR_PTHREADS)
+
+PR_EXTERN(void) _MD_EarlyInit(void);
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+
+#elif defined(_PR_GLOBAL_THREADS_ONLY)
+
+#include "prthread.h"
+
+#include <ucontext.h>
+
+/*
+** Iinitialization Related definitions
+*/
+
+PR_EXTERN(void) _MD_EarlyInit(void);
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+
+#define _MD_GET_SP(threadp) threadp->md.sp
+
+/*
+** Clean-up the thread machine dependent data structure
+*/
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_INIT_ATTACHED_THREAD _MD_InitializeThread
+
+PR_EXTERN(PRStatus) _MD_CreateThread(PRThread *thread,
+ void (*start)(void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize);
+#define _MD_CREATE_THREAD _MD_CreateThread
+
+#define _PR_CONTEXT_TYPE ucontext_t
+
+#define CONTEXT(_thread) (&(_thread)->md.context)
+
+#include <thread.h>
+#include <sys/lwp.h>
+#include <synch.h>
+
+extern struct PRLock *_pr_schedLock;
+
+/*
+** Thread Local Storage
+*/
+
+#define THREAD_KEY_T thread_key_t
+
+extern struct PRThread *_pr_current_thread_tls();
+extern struct _PRCPU *_pr_current_cpu_tls();
+extern struct PRThread *_pr_last_thread_tls();
+
+extern THREAD_KEY_T threadid_key;
+extern THREAD_KEY_T cpuid_key;
+extern THREAD_KEY_T last_thread_key;
+
+#define _MD_CURRENT_THREAD() _pr_current_thread_tls()
+#define _MD_CURRENT_CPU() _pr_current_cpu_tls()
+#define _MD_LAST_THREAD() _pr_last_thread_tls()
+
+#define _MD_SET_CURRENT_THREAD(newval) \
+ PR_BEGIN_MACRO \
+ thr_setspecific(threadid_key, (void *)newval); \
+ PR_END_MACRO
+
+#define _MD_SET_CURRENT_CPU(newval) \
+ PR_BEGIN_MACRO \
+ thr_setspecific(cpuid_key, (void *)newval); \
+ PR_END_MACRO
+
+#define _MD_SET_LAST_THREAD(newval) \
+ PR_BEGIN_MACRO \
+ thr_setspecific(last_thread_key, (void *)newval); \
+ PR_END_MACRO
+
+#define _MD_CLEAN_THREAD(_thread) _MD_cleanup_thread(_thread)
+extern void _MD_exit_thread(PRThread *thread);
+#define _MD_EXIT_THREAD(thread) _MD_exit_thread(thread)
+
+#define _MD_SUSPEND_THREAD(thread) _MD_Suspend(thread)
+#define _MD_RESUME_THREAD(thread) thr_continue((thread)->md.handle)
+
+/* XXXX Needs to be defined - Prashant */
+#define _MD_SUSPEND_CPU(cpu)
+#define _MD_RESUME_CPU(cpu)
+
+extern void _MD_Begin_SuspendAll(void);
+extern void _MD_End_SuspendAll(void);
+extern void _MD_End_ResumeAll(void);
+#define _MD_BEGIN_SUSPEND_ALL() _MD_Begin_SuspendAll()
+#define _MD_BEGIN_RESUME_ALL()
+#define _MD_END_SUSPEND_ALL() _MD_End_SuspendAll()
+#define _MD_END_RESUME_ALL() _MD_End_ResumeAll()
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(md_lockp) (mutex_init(&((md_lockp)->lock),USYNC_THREAD,NULL) ? PR_FAILURE : PR_SUCCESS)
+#define _MD_FREE_LOCK(md_lockp) mutex_destroy(&((md_lockp)->lock))
+#define _MD_UNLOCK(md_lockp) mutex_unlock(&((md_lockp)->lock))
+#define _MD_TEST_AND_LOCK(md_lockp) mutex_trylock(&((md_lockp)->lock))
+struct _MDLock;
+PR_EXTERN(void) _MD_lock(struct _MDLock *md_lock);
+#undef PROFILE_LOCKS
+#ifndef PROFILE_LOCKS
+#define _MD_LOCK(md_lockp) _MD_lock(md_lockp)
+#else
+#define _MD_LOCK(md_lockp) \
+ PR_BEGIN_MACRO \
+ int rv = _MD_TEST_AND_LOCK(md_lockp); \
+ if (rv == 0) { \
+ (md_lockp)->hitcount++; \
+ } else { \
+ (md_lockp)->misscount++; \
+ _MD_lock(md_lockp); \
+ } \
+ PR_END_MACRO
+#endif
+
+#define _PR_LOCK_HEAP() if (_pr_heapLock) _MD_LOCK(&_pr_heapLock->md)
+#define _PR_UNLOCK_HEAP() if (_pr_heapLock) _MD_UNLOCK(&_pr_heapLock->md)
+
+#define _MD_ATTACH_THREAD(threadp)
+
+
+#define THR_KEYCREATE thr_keycreate
+#define THR_SELF thr_self
+#define _MD_NEW_CV(condp) cond_init(&((condp)->cv), USYNC_THREAD, 0)
+#define COND_WAIT(condp, mutexp) cond_wait(condp, mutexp)
+#define COND_TIMEDWAIT(condp, mutexp, tspec) \
+ cond_timedwait(condp, mutexp, tspec)
+#define _MD_NOTIFY_CV(condp, lockp) cond_signal(&((condp)->cv))
+#define _MD_NOTIFYALL_CV(condp,unused) cond_broadcast(&((condp)->cv))
+#define _MD_FREE_CV(condp) cond_destroy(&((condp)->cv))
+#define _MD_YIELD() thr_yield()
+#include <time.h>
+/*
+ * 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().
+ */
+#if defined(i386) && defined(SOLARIS2_4)
+extern int _pr_solx86_clock_gettime(clockid_t clock_id, struct timespec *tp);
+#define GETTIME(tt) _pr_solx86_clock_gettime(CLOCK_REALTIME, (tt))
+#else
+#define GETTIME(tt) clock_gettime(CLOCK_REALTIME, (tt))
+#endif /* i386 && SOLARIS2_4 */
+
+#define MUTEX_T mutex_t
+#define COND_T cond_t
+
+#define _MD_NEW_SEM(md_semp,_val) sema_init(&((md_semp)->sem),_val,USYNC_THREAD,NULL)
+#define _MD_DESTROY_SEM(md_semp) sema_destroy(&((md_semp)->sem))
+#define _MD_WAIT_SEM(md_semp) sema_wait(&((md_semp)->sem))
+#define _MD_POST_SEM(md_semp) sema_post(&((md_semp)->sem))
+
+#define _MD_SAVE_ERRNO(_thread)
+#define _MD_RESTORE_ERRNO(_thread)
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+
+extern struct _MDLock _pr_ioq_lock;
+#define _MD_IOQ_LOCK() _MD_LOCK(&_pr_ioq_lock)
+#define _MD_IOQ_UNLOCK() _MD_UNLOCK(&_pr_ioq_lock)
+
+extern PRStatus _MD_wait(struct PRThread *, PRIntervalTime timeout);
+#define _MD_WAIT _MD_wait
+
+extern PRStatus _MD_WakeupWaiter(struct PRThread *);
+#define _MD_WAKEUP_WAITER _MD_WakeupWaiter
+
+PR_EXTERN(void) _MD_InitIO(void);
+#define _MD_INIT_IO _MD_InitIO
+
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status)
+#define _MD_SWITCH_CONTEXT(_thread)
+#define _MD_RESTORE_CONTEXT(_newThread)
+
+struct _MDLock {
+ MUTEX_T lock;
+#ifdef PROFILE_LOCKS
+ PRInt32 hitcount;
+ PRInt32 misscount;
+#endif
+};
+
+struct _MDCVar {
+ COND_T cv;
+};
+
+struct _MDSemaphore {
+ sema_t sem;
+};
+
+struct _MDThread {
+ _PR_CONTEXT_TYPE context;
+ thread_t handle;
+ lwpid_t lwpid;
+ uint_t sp; /* stack pointer */
+ uint_t threadID; /* ptr to solaris-internal thread id structures */
+ struct _MDSemaphore waiter_sem;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field, common to all Unix platforms
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+/* The following defines the unwrapped versions of select() and poll(). */
+extern int _select(int nfds, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds, struct timeval *timeout);
+#define _MD_SELECT _select
+
+#include <poll.h>
+#define _MD_POLL _poll
+extern int _poll(struct pollfd *fds, unsigned long nfds, int timeout);
+
+PR_BEGIN_EXTERN_C
+
+/*
+** Missing function prototypes
+*/
+extern int gethostname (char *name, int namelen);
+
+PR_END_EXTERN_C
+
+#else /* _PR_GLOBAL_THREADS_ONLY */
+
+/*
+ * LOCAL_THREADS_ONLY implementation on Solaris
+ */
+
+#include "prthread.h"
+
+#include <errno.h>
+#include <ucontext.h>
+#include <sys/stack.h>
+#include <synch.h>
+
+/*
+** Iinitialization Related definitions
+*/
+
+PR_EXTERN(void) _MD_EarlyInit(void);
+PR_EXTERN(void) _MD_SolarisInit();
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _MD_SolarisInit
+#define _MD_INIT_THREAD _MD_InitializeThread
+
+#ifdef USE_SETJMP
+
+#include <setjmp.h>
+
+#define _PR_CONTEXT_TYPE jmp_buf
+
+#ifdef sparc
+#define _MD_GET_SP(_t) (_t)->md.context[2]
+#else
+#define _MD_GET_SP(_t) (_t)->md.context[4]
+#endif
+
+#define PR_NUM_GCREGS _JBLEN
+#define CONTEXT(_thread) (_thread)->md.context
+
+#else /* ! USE_SETJMP */
+
+#ifdef sparc
+#define _PR_CONTEXT_TYPE ucontext_t
+#define _MD_GET_SP(_t) (_t)->md.context.uc_mcontext.gregs[REG_SP]
+/*
+** Sparc's use register windows. the _MD_GetRegisters for the sparc's
+** doesn't actually store anything into the argument buffer; instead the
+** register windows are homed to the stack. I assume that the stack
+** always has room for the registers to spill to...
+*/
+#define PR_NUM_GCREGS 0
+#else
+#define _PR_CONTEXT_TYPE unsigned int edi; sigset_t oldMask, blockMask; ucontext_t
+#define _MD_GET_SP(_t) (_t)->md.context.uc_mcontext.gregs[USP]
+#define PR_NUM_GCREGS _JBLEN
+#endif
+
+#define CONTEXT(_thread) (&(_thread)->md.context)
+
+#endif /* ! USE_SETJMP */
+
+#include <time.h>
+/*
+ * Because clock_gettime() on Solaris/x86 always generates a
+ * segmentation fault, we use an emulated version _pr_solx86_clock_gettime(),
+ * which is implemented using gettimeofday().
+ */
+#ifdef i386
+#define GETTIME(tt) _pr_solx86_clock_gettime(CLOCK_REALTIME, (tt))
+#else
+#define GETTIME(tt) clock_gettime(CLOCK_REALTIME, (tt))
+#endif /* i386 */
+
+#define _MD_SAVE_ERRNO(_thread) (_thread)->md.errcode = errno;
+#define _MD_RESTORE_ERRNO(_thread) errno = (_thread)->md.errcode;
+
+#ifdef sparc
+
+#ifdef USE_SETJMP
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+ PR_BEGIN_MACRO \
+ int *context = (_thread)->md.context; \
+ *status = PR_TRUE; \
+ (void) setjmp(context); \
+ (_thread)->md.context[1] = (int) ((_sp) - 64); \
+ (_thread)->md.context[2] = (int) _main; \
+ (_thread)->md.context[3] = (int) _main + 4; \
+ _thread->no_sched = 0; \
+ PR_END_MACRO
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!setjmp(CONTEXT(_thread))) { \
+ _MD_SAVE_ERRNO(_thread) \
+ _MD_SET_LAST_THREAD(_thread); \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ _PR_Schedule(); \
+ }
+
+#define _MD_RESTORE_CONTEXT(_newThread) \
+{ \
+ _MD_RESTORE_ERRNO(_newThread) \
+ _MD_SET_CURRENT_THREAD(_newThread); \
+ longjmp(CONTEXT(_newThread), 1); \
+}
+
+#else
+/*
+** Initialize the thread context preparing it to execute _main.
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+ PR_BEGIN_MACRO \
+ ucontext_t *uc = CONTEXT(_thread); \
+ *status = PR_TRUE; \
+ getcontext(uc); \
+ uc->uc_stack.ss_sp = (char *) ((unsigned long)(_sp - WINDOWSIZE - SA(MINFRAME)) & 0xfffffff8); \
+ uc->uc_stack.ss_size = _thread->stack->stackSize; \
+ uc->uc_stack.ss_flags = 0; /* ? */ \
+ uc->uc_mcontext.gregs[REG_SP] = (unsigned int) uc->uc_stack.ss_sp; \
+ uc->uc_mcontext.gregs[REG_PC] = (unsigned int) _main; \
+ uc->uc_mcontext.gregs[REG_nPC] = (unsigned int) ((char*)_main)+4; \
+ uc->uc_flags = UC_ALL; \
+ _thread->no_sched = 0; \
+ PR_END_MACRO
+
+/*
+** Switch away from the current thread context by saving its state and
+** calling the thread scheduler. Reload cpu when we come back from the
+** context switch because it might have changed.
+*/
+#define _MD_SWITCH_CONTEXT(_thread) \
+ PR_BEGIN_MACRO \
+ if (!getcontext(CONTEXT(_thread))) { \
+ _MD_SAVE_ERRNO(_thread); \
+ _MD_SET_LAST_THREAD(_thread); \
+ _PR_Schedule(); \
+ } \
+ PR_END_MACRO
+
+/*
+** Restore a thread context that was saved by _MD_SWITCH_CONTEXT or
+** initialized by _MD_INIT_CONTEXT.
+*/
+#define _MD_RESTORE_CONTEXT(_newThread) \
+ PR_BEGIN_MACRO \
+ ucontext_t *uc = CONTEXT(_newThread); \
+ uc->uc_mcontext.gregs[11] = 1; \
+ _MD_RESTORE_ERRNO(_newThread); \
+ _MD_SET_CURRENT_THREAD(_newThread); \
+ setcontext(uc); \
+ PR_END_MACRO
+#endif
+
+#else /* x86 solaris */
+
+#ifdef USE_SETJMP
+
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+ PR_BEGIN_MACRO \
+ *status = PR_TRUE; \
+ if (setjmp(CONTEXT(_thread))) _main(); \
+ _MD_GET_SP(_thread) = (int) ((_sp) - 64); \
+ PR_END_MACRO
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!setjmp(CONTEXT(_thread))) { \
+ _MD_SAVE_ERRNO(_thread) \
+ _PR_Schedule(); \
+ }
+
+#define _MD_RESTORE_CONTEXT(_newThread) \
+{ \
+ _MD_RESTORE_ERRNO(_newThread) \
+ _MD_SET_CURRENT_THREAD(_newThread); \
+ longjmp(CONTEXT(_newThread), 1); \
+}
+
+#else /* USE_SETJMP */
+
+#define WINDOWSIZE 0
+
+int getedi(void);
+void setedi(int);
+
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+ PR_BEGIN_MACRO \
+ ucontext_t *uc = CONTEXT(_thread); \
+ *status = PR_TRUE; \
+ getcontext(uc); \
+ /* Force sp to be double aligned! */ \
+ uc->uc_mcontext.gregs[USP] = (int) ((unsigned long)(_sp - WINDOWSIZE - SA(MINFRAME)) & 0xfffffff8); \
+ uc->uc_mcontext.gregs[PC] = (int) _main; \
+ (_thread)->no_sched = 0; \
+ PR_END_MACRO
+
+/* getcontext() may return 1, contrary to what the man page says */
+#define _MD_SWITCH_CONTEXT(_thread) \
+ PR_BEGIN_MACRO \
+ ucontext_t *uc = CONTEXT(_thread); \
+ PR_ASSERT(_thread->no_sched); \
+ sigfillset(&((_thread)->md.blockMask)); \
+ sigprocmask(SIG_BLOCK, &((_thread)->md.blockMask), \
+ &((_thread)->md.oldMask)); \
+ (_thread)->md.edi = getedi(); \
+ if (! getcontext(uc)) { \
+ sigprocmask(SIG_SETMASK, &((_thread)->md.oldMask), NULL); \
+ uc->uc_mcontext.gregs[EDI] = (_thread)->md.edi; \
+ _MD_SAVE_ERRNO(_thread) \
+ _MD_SET_LAST_THREAD(_thread); \
+ _PR_Schedule(); \
+ } else { \
+ sigprocmask(SIG_SETMASK, &((_thread)->md.oldMask), NULL); \
+ setedi((_thread)->md.edi); \
+ PR_ASSERT(_MD_LAST_THREAD() !=_MD_CURRENT_THREAD()); \
+ _MD_LAST_THREAD()->no_sched = 0; \
+ } \
+ PR_END_MACRO
+
+/*
+** Restore a thread context, saved by _PR_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_newthread) \
+ PR_BEGIN_MACRO \
+ ucontext_t *uc = CONTEXT(_newthread); \
+ uc->uc_mcontext.gregs[EAX] = 1; \
+ _MD_RESTORE_ERRNO(_newthread) \
+ _MD_SET_CURRENT_THREAD(_newthread); \
+ (_newthread)->no_sched = 1; \
+ setcontext(uc); \
+ PR_END_MACRO
+#endif /* USE_SETJMP */
+
+#endif /* sparc */
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDThread {
+ _PR_CONTEXT_TYPE context;
+ int errcode;
+ int id;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#ifndef _PR_PTHREADS
+#define _MD_INIT_LOCKS()
+#endif
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread)
+#define _MD_RESUME_THREAD(thread)
+#define _MD_CLEAN_THREAD(_thread)
+
+extern PRStatus _MD_WAIT(struct PRThread *, PRIntervalTime timeout);
+extern PRStatus _MD_WAKEUP_WAITER(struct PRThread *);
+extern void _MD_YIELD(void);
+extern PRStatus _MD_InitializeThread(PRThread *thread);
+extern void _MD_SET_PRIORITY(struct _MDThread *thread,
+ PRThreadPriority newPri);
+extern PRStatus _MD_CREATE_THREAD(PRThread *thread, void (*start) (void *),
+ PRThreadPriority priority, PRThreadScope scope, PRThreadState state,
+ PRUint32 stackSize);
+
+PR_EXTERN(PRIntervalTime) _MD_Solaris_GetInterval(void);
+#define _MD_GET_INTERVAL _MD_Solaris_GetInterval
+PR_EXTERN(PRIntervalTime) _MD_Solaris_TicksPerSecond(void);
+#define _MD_INTERVAL_PER_SEC _MD_Solaris_TicksPerSecond
+
+/* The following defines the unwrapped versions of select() and poll(). */
+extern int _select(int nfds, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds, struct timeval *timeout);
+#define _MD_SELECT _select
+
+#include <stropts.h>
+#include <poll.h>
+#define _MD_POLL _poll
+extern int _poll(struct pollfd *fds, unsigned long nfds, int timeout);
+
+PR_BEGIN_EXTERN_C
+
+/*
+** Missing function prototypes
+*/
+extern int gethostname (char *name, int namelen);
+
+PR_END_EXTERN_C
+
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+
+#endif /* nspr_solaris_defs_h___ */
+
diff --git a/pr/include/md/_sony.cfg b/pr/include/md/_sony.cfg
new file mode 100644
index 00000000..1683df1d
--- /dev/null
+++ b/pr/include/md/_sony.cfg
@@ -0,0 +1,121 @@
+/* -*- 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_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef SONY
+#define SONY
+#endif
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#undef HAVE_LONG_LONG
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#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/include/md/_sony.h b/pr/include/md/_sony.h
new file mode 100644
index 00000000..1ff68977
--- /dev/null
+++ b/pr/include/md/_sony.h
@@ -0,0 +1,185 @@
+/* -*- 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_sony_defs_h___
+#define nspr_sony_defs_h___
+
+#define PR_LINKER_ARCH "sony"
+#define _PR_SI_SYSNAME "SONY"
+#define _PR_SI_ARCHITECTURE "mips"
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#define _MD_GET_INTERVAL _PR_UNIX_GetInterval
+#define _MD_INTERVAL_PER_SEC _PR_UNIX_TicksPerSecond
+
+#if defined(_PR_LOCAL_THREADS_ONLY)
+#include <ucontext.h>
+#include <sys/regset.h>
+
+#define PR_NUM_GCREGS NGREG
+#define PR_CONTEXT_TYPE ucontext_t
+
+#define CONTEXT(_thread) (&(_thread)->md.context)
+
+#define _MD_GET_SP(_t) (_t)->md.context.uc_mcontext.gregs[CXT_SP]
+
+/*
+** Initialize the thread context preparing it to execute _main()
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ getcontext(CONTEXT(_thread)); \
+ CONTEXT(_thread)->uc_stack.ss_sp = (char*) (_thread)->stack->stackBottom; \
+ CONTEXT(_thread)->uc_stack.ss_size = (_thread)->stack->stackSize; \
+ _MD_GET_SP(_thread) = (greg_t) (_sp) - 64; \
+ makecontext(CONTEXT(_thread), _main, 0); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!getcontext(CONTEXT(_thread))) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ ucontext_t *uc = CONTEXT(_thread); \
+ uc->uc_mcontext.gregs[CXT_V0] = 1; \
+ uc->uc_mcontext.gregs[CXT_A3] = 0; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ errno = (_thread)->md.errcode; \
+ setcontext(uc); \
+}
+
+/* Machine-dependent (MD) data structures */
+
+struct _MDThread {
+ PR_CONTEXT_TYPE context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread)
+#define _MD_RESUME_THREAD(thread)
+#define _MD_CLEAN_THREAD(_thread)
+
+/* The following defines unwrapped versions of select() and poll(). */
+extern int _select (int, fd_set *, fd_set *, fd_set *, struct timeval *);
+#define _MD_SELECT _select
+
+#include <sys/poll.h>
+extern int _poll(struct pollfd *fds, unsigned long nfds, int timeout);
+#define _MD_POLL _poll
+
+#endif /* _PR_LOCAL_THREADS_ONLY */
+
+#undef HAVE_STACK_GROWING_UP
+#define HAVE_DLL
+#define USE_DLFCN
+#define NEED_TIME_R
+#define NEED_STRFTIME_LOCK
+
+/*
+** Missing function prototypes
+*/
+extern int gethostname(char *name, int namelen);
+
+#endif /* nspr_sony_defs_h___ */
diff --git a/pr/include/md/_sunos4.cfg b/pr/include/md/_sunos4.cfg
new file mode 100644
index 00000000..0ac6fe64
--- /dev/null
+++ b/pr/include/md/_sunos4.cfg
@@ -0,0 +1,119 @@
+/* -*- 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_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef SUNOS4
+#define SUNOS4
+#endif
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#undef HAVE_LONG_LONG
+#define HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+
+#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 /* ifndef nspr_cpucfg___ */
diff --git a/pr/include/md/_sunos4.h b/pr/include/md/_sunos4.h
new file mode 100644
index 00000000..41d4258c
--- /dev/null
+++ b/pr/include/md/_sunos4.h
@@ -0,0 +1,217 @@
+/* -*- 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_sunos_defs_h___
+#define nspr_sunos_defs_h___
+
+#include "md/sunos4.h"
+
+/* On SunOS 4, memset is declared in memory.h */
+#include <memory.h>
+#include <errno.h>
+#include <sys/syscall.h>
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "sunos"
+#define _PR_SI_SYSNAME "SUNOS"
+#define _PR_SI_ARCHITECTURE "sparc"
+#define PR_DLL_SUFFIX ".so.1.0"
+
+/*
+** For sunos type machines, don't specify an address because the
+** NetBSD/SPARC O.S. does the wrong thing.
+*/
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#undef HAVE_STACK_GROWING_UP
+#undef HAVE_WEAK_IO_SYMBOLS
+#undef HAVE_WEAK_MALLOC_SYMBOLS
+#define HAVE_DLL
+#define USE_DLFCN
+#define NEED_STRFTIME_LOCK
+#define NEED_TIME_R
+#define HAVE_BSD_FLOCK
+#define _PR_NO_LARGE_FILES
+#define _PR_STAT_HAS_ONLY_ST_ATIME
+
+#define _MD_GET_INTERVAL _PR_UNIX_GetInterval
+#define _MD_INTERVAL_PER_SEC _PR_UNIX_TicksPerSecond
+
+#define USE_SETJMP
+
+#include <setjmp.h>
+
+#define _MD_GET_SP(_t) (_t)->md.context[2]
+
+#define PR_NUM_GCREGS _JBLEN
+
+#define CONTEXT(_th) ((_th)->md.context)
+
+/*
+** Initialize the thread context preparing it to execute _main.
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+ PR_BEGIN_MACRO \
+ int *context = (_thread)->md.context; \
+ *status = PR_TRUE; \
+ asm("ta 3"); \
+ (void) setjmp(context); \
+ (_thread)->md.context[2] = (int) ((_sp) - 64); \
+ (_thread)->md.context[2] &= ~7; \
+ (_thread)->md.context[3] = (int) _main; \
+ (_thread)->md.context[4] = (int) _main + 4; \
+ PR_END_MACRO
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ asm("ta 3"); \
+ if (!setjmp(CONTEXT(_thread))) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ longjmp(CONTEXT(_thread), 1); \
+}
+
+#pragma unknown_control_flow(longjmp)
+#pragma unknown_control_flow(setjmp)
+#pragma unknown_control_flow(_PR_Schedule)
+
+/*
+** Missing function prototypes
+*/
+
+extern int socket (int domain, int type, int protocol);
+extern int getsockname (int s, struct sockaddr *name, int *namelen);
+extern int getpeername (int s, struct sockaddr *name, int *namelen);
+extern int getsockopt (int s, int level, int optname, char* optval, int* optlen);
+extern int setsockopt (int s, int level, int optname, const char* optval, int optlen);
+extern int accept (int s, struct sockaddr *addr, int *addrlen);
+extern int listen (int s, int backlog);
+extern int brk(void *);
+extern void *sbrk(int);
+
+
+/* Machine-dependent (MD) data structures. SunOS 4 has no native threads. */
+
+struct _MDThread {
+ jmp_buf context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+/* These are copied from _solaris.h */
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread)
+#define _MD_RESUME_THREAD(thread)
+#define _MD_CLEAN_THREAD(_thread)
+
+/*
+ * We wrapped the select() call. _MD_SELECT refers to the built-in,
+ * unwrapped version.
+ */
+#define _MD_SELECT(nfds,r,w,e,tv) syscall(SYS_select,nfds,r,w,e,tv)
+#define _MD_POLL(fds,nfds,timeout) syscall(SYS_poll,fds,nfds,timeout)
+
+#endif /* nspr_sparc_defs_h___ */
diff --git a/pr/include/md/_unix_errors.h b/pr/include/md/_unix_errors.h
new file mode 100644
index 00000000..03ca7a02
--- /dev/null
+++ b/pr/include/md/_unix_errors.h
@@ -0,0 +1,152 @@
+/* -*- 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 prunixerrors_h___
+#define prunixerrors_h___
+
+#include <unistd.h>
+#include <stddef.h>
+
+PR_BEGIN_EXTERN_C
+
+PR_EXTERN(PRStatus) _MD_gethostname(char *name, PRUint32 namelen);
+#define _MD_GETHOSTNAME _MD_gethostname
+
+PR_EXTERN(void) _MD_unix_map_opendir_error(int err);
+#define _PR_MD_MAP_OPENDIR_ERROR _MD_unix_map_opendir_error
+
+PR_EXTERN(void) _MD_unix_map_closedir_error(int err);
+#define _PR_MD_MAP_CLOSEDIR_ERROR _MD_unix_map_closedir_error
+
+PR_EXTERN(void) _MD_unix_readdir_error(int err);
+#define _PR_MD_MAP_READDIR_ERROR _MD_unix_readdir_error
+
+PR_EXTERN(void) _MD_unix_map_unlink_error(int err);
+#define _PR_MD_MAP_UNLINK_ERROR _MD_unix_map_unlink_error
+
+PR_EXTERN(void) _MD_unix_map_stat_error(int err);
+#define _PR_MD_MAP_STAT_ERROR _MD_unix_map_stat_error
+
+PR_EXTERN(void) _MD_unix_map_fstat_error(int err);
+#define _PR_MD_MAP_FSTAT_ERROR _MD_unix_map_fstat_error
+
+PR_EXTERN(void) _MD_unix_map_rename_error(int err);
+#define _PR_MD_MAP_RENAME_ERROR _MD_unix_map_rename_error
+
+PR_EXTERN(void) _MD_unix_map_access_error(int err);
+#define _PR_MD_MAP_ACCESS_ERROR _MD_unix_map_access_error
+
+PR_EXTERN(void) _MD_unix_map_mkdir_error(int err);
+#define _PR_MD_MAP_MKDIR_ERROR _MD_unix_map_mkdir_error
+
+PR_EXTERN(void) _MD_unix_map_rmdir_error(int err);
+#define _PR_MD_MAP_RMDIR_ERROR _MD_unix_map_rmdir_error
+
+PR_EXTERN(void) _MD_unix_map_read_error(int err);
+#define _PR_MD_MAP_READ_ERROR _MD_unix_map_read_error
+
+PR_EXTERN(void) _MD_unix_map_write_error(int err);
+#define _PR_MD_MAP_WRITE_ERROR _MD_unix_map_write_error
+
+PR_EXTERN(void) _MD_unix_map_lseek_error(int err);
+#define _PR_MD_MAP_LSEEK_ERROR _MD_unix_map_lseek_error
+
+PR_EXTERN(void) _MD_unix_map_fsync_error(int err);
+#define _PR_MD_MAP_FSYNC_ERROR _MD_unix_map_fsync_error
+
+PR_EXTERN(void) _MD_unix_map_close_error(int err);
+#define _PR_MD_MAP_CLOSE_ERROR _MD_unix_map_close_error
+
+PR_EXTERN(void) _MD_unix_map_socket_error(int err);
+#define _PR_MD_MAP_SOCKET_ERROR _MD_unix_map_socket_error
+
+PR_EXTERN(void) _MD_unix_map_socketavailable_error(int err);
+#define _PR_MD_MAP_SOCKETAVAILABLE_ERROR _MD_unix_map_socketavailable_error
+
+PR_EXTERN(void) _MD_unix_map_recv_error(int err);
+#define _PR_MD_MAP_RECV_ERROR _MD_unix_map_recv_error
+
+PR_EXTERN(void) _MD_unix_map_recvfrom_error(int err);
+#define _PR_MD_MAP_RECVFROM_ERROR _MD_unix_map_recvfrom_error
+
+PR_EXTERN(void) _MD_unix_map_send_error(int err);
+#define _PR_MD_MAP_SEND_ERROR _MD_unix_map_send_error
+
+PR_EXTERN(void) _MD_unix_map_sendto_error(int err);
+#define _PR_MD_MAP_SENDTO_ERROR _MD_unix_map_sendto_error
+
+PR_EXTERN(void) _MD_unix_map_writev_error(int err);
+#define _PR_MD_MAP_WRITEV_ERROR _MD_unix_map_writev_error
+
+PR_EXTERN(void) _MD_unix_map_accept_error(int err);
+#define _PR_MD_MAP_ACCEPT_ERROR _MD_unix_map_accept_error
+
+PR_EXTERN(void) _MD_unix_map_connect_error(int err);
+#define _PR_MD_MAP_CONNECT_ERROR _MD_unix_map_connect_error
+
+PR_EXTERN(void) _MD_unix_map_bind_error(int err);
+#define _PR_MD_MAP_BIND_ERROR _MD_unix_map_bind_error
+
+PR_EXTERN(void) _MD_unix_map_listen_error(int err);
+#define _PR_MD_MAP_LISTEN_ERROR _MD_unix_map_listen_error
+
+PR_EXTERN(void) _MD_unix_map_shutdown_error(int err);
+#define _PR_MD_MAP_SHUTDOWN_ERROR _MD_unix_map_shutdown_error
+
+PR_EXTERN(void) _MD_unix_map_socketpair_error(int err);
+#define _PR_MD_MAP_SOCKETPAIR_ERROR _MD_unix_map_socketpair_error
+
+PR_EXTERN(void) _MD_unix_map_getsockname_error(int err);
+#define _PR_MD_MAP_GETSOCKNAME_ERROR _MD_unix_map_getsockname_error
+
+PR_EXTERN(void) _MD_unix_map_getpeername_error(int err);
+#define _PR_MD_MAP_GETPEERNAME_ERROR _MD_unix_map_getpeername_error
+
+PR_EXTERN(void) _MD_unix_map_getsockopt_error(int err);
+#define _PR_MD_MAP_GETSOCKOPT_ERROR _MD_unix_map_getsockopt_error
+
+PR_EXTERN(void) _MD_unix_map_setsockopt_error(int err);
+#define _PR_MD_MAP_SETSOCKOPT_ERROR _MD_unix_map_setsockopt_error
+
+PR_EXTERN(void) _MD_unix_map_open_error(int err);
+#define _PR_MD_MAP_OPEN_ERROR _MD_unix_map_open_error
+
+PR_EXTERN(void) _MD_unix_map_mmap_error(int err);
+#define _PR_MD_MAP_MMAP_ERROR _MD_unix_map_mmap_error
+
+PR_EXTERN(void) _MD_unix_map_gethostname_error(int err);
+#define _PR_MD_MAP_GETHOSTNAME_ERROR _MD_unix_map_gethostname_error
+
+PR_EXTERN(void) _MD_unix_map_select_error(int err);
+#define _PR_MD_MAP_SELECT_ERROR _MD_unix_map_select_error
+
+PR_EXTERN(void) _MD_unix_map_poll_error(int err);
+#define _PR_MD_MAP_POLL_ERROR _MD_unix_map_poll_error
+
+PR_EXTERN(void) _MD_unix_map_poll_revents_error(int err);
+#define _PR_MD_MAP_POLL_REVENTS_ERROR _MD_unix_map_poll_revents_error
+
+PR_EXTERN(void) _MD_unix_map_flock_error(int err);
+#define _PR_MD_MAP_FLOCK_ERROR _MD_unix_map_flock_error
+
+PR_EXTERN(void) _MD_unix_map_lockf_error(int err);
+#define _PR_MD_MAP_LOCKF_ERROR _MD_unix_map_lockf_error
+
+PR_END_EXTERN_C
+
+#endif /* prunixerrors_h___ */
diff --git a/pr/include/md/_unixos.h b/pr/include/md/_unixos.h
new file mode 100644
index 00000000..a801553f
--- /dev/null
+++ b/pr/include/md/_unixos.h
@@ -0,0 +1,571 @@
+/* -*- 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 prunixos_h___
+#define prunixos_h___
+
+/*
+ * If FD_SETSIZE is not defined on the command line, set the default value
+ * before include select.h
+ */
+/*
+ * Linux: FD_SETSIZE is defined in /usr/include/sys/select.h and should
+ * not be redefined.
+ */
+#if !defined(LINUX) && !defined(RHAPSODY)
+#ifndef FD_SETSIZE
+#define FD_SETSIZE 4096
+#endif
+#endif
+
+#include <unistd.h>
+#include <stddef.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include "prio.h"
+#include "prmem.h"
+#include "prclist.h"
+
+/*
+ * For select(), fd_set, and struct timeval.
+ *
+ * In The Single UNIX(R) Specification, Version 2,
+ * the header file for select() is <sys/time.h>.
+ *
+ * fd_set is defined in <sys/types.h>. Usually
+ * <sys/time.h> includes <sys/types.h>, but on some
+ * older systems <sys/time.h> does not include
+ * <sys/types.h>, so we include it explicitly.
+ */
+#include <sys/time.h>
+#include <sys/types.h>
+#if defined(AIX) /* Only pre-4.2 AIX needs it, but for simplicity... */
+#include <sys/select.h>
+#endif
+
+#define PR_DIRECTORY_SEPARATOR '/'
+#define PR_DIRECTORY_SEPARATOR_STR "/"
+#define PR_PATH_SEPARATOR ':'
+#define PR_PATH_SEPARATOR_STR ":"
+#define GCPTR
+typedef int (*FARPROC)();
+
+/*
+ * intervals at which GLOBAL threads wakeup to check for pending interrupt
+ */
+#define _PR_INTERRUPT_CHECK_INTERVAL_SECS 5
+extern PRIntervalTime intr_timeout_ticks;
+
+/*
+ * The bit flags for the in_flags and out_flags fields
+ * of _PR_UnixPollDesc
+ */
+#ifdef _PR_USE_POLL
+#define _PR_UNIX_POLL_READ POLLIN
+#define _PR_UNIX_POLL_WRITE POLLOUT
+#define _PR_UNIX_POLL_EXCEPT POLLPRI
+#define _PR_UNIX_POLL_ERR POLLERR
+#define _PR_UNIX_POLL_NVAL POLLNVAL
+#define _PR_UNIX_POLL_HUP POLLHUP
+#else /* _PR_USE_POLL */
+#define _PR_UNIX_POLL_READ 0x1
+#define _PR_UNIX_POLL_WRITE 0x2
+#define _PR_UNIX_POLL_EXCEPT 0x4
+#define _PR_UNIX_POLL_ERR 0x8
+#define _PR_UNIX_POLL_NVAL 0x10
+#define _PR_UNIX_POLL_HUP 0x20
+#endif /* _PR_USE_POLL */
+
+typedef struct _PRUnixPollDesc {
+ PRInt32 osfd;
+ PRInt16 in_flags;
+ PRInt16 out_flags;
+} _PRUnixPollDesc;
+
+typedef struct PRPollQueue {
+ PRCList links; /* for linking PRPollQueue's together */
+ _PRUnixPollDesc *pds; /* array of poll descriptors */
+ PRUintn npds; /* length of the array */
+ PRPackedBool on_ioq; /* is this on the async i/o work q? */
+ PRIntervalTime timeout; /* timeout, in ticks */
+ struct PRThread *thr;
+} PRPollQueue;
+
+#define _PR_POLLQUEUE_PTR(_qp) \
+ ((PRPollQueue*) ((char*) (_qp) - offsetof(PRPollQueue,links)))
+
+
+extern PRInt32 _PR_WaitForMultipleFDs(
+ _PRUnixPollDesc *unixpds,
+ PRInt32 pdcnt,
+ PRIntervalTime timeout);
+extern void _PR_Unblock_IO_Wait(struct PRThread *thr);
+
+#if defined(_PR_LOCAL_THREADS_ONLY) || defined(_PR_GLOBAL_THREADS_ONLY)
+#define _MD_CHECK_FOR_EXIT()
+#endif
+
+extern fd_set _pr_md_read_set, _pr_md_write_set, _pr_md_exception_set;
+extern PRInt16 _pr_md_read_cnt[], _pr_md_write_cnt[], _pr_md_exception_cnt[];
+extern PRInt32 _pr_md_ioq_max_osfd;
+extern PRUint32 _pr_md_ioq_timeout;
+
+struct _MDFileDesc {
+ int osfd;
+};
+
+struct _MDDir {
+ DIR *d;
+};
+
+struct _PRCPU;
+extern void _MD_unix_init_running_cpu(struct _PRCPU *cpu);
+
+/*
+** Make a redzone at both ends of the stack segment. Disallow access
+** to those pages of memory. It's ok if the mprotect call's don't
+** work - it just means that we don't really have a functional
+** redzone.
+*/
+#if defined(DEBUG) && !defined(RHAPSODY)
+#if !defined(SOLARIS)
+#include <string.h> /* for memset() */
+#define _MD_INIT_STACK(ts,REDZONE) \
+ PR_BEGIN_MACRO \
+ (void) mprotect((void*)ts->seg->vaddr, REDZONE, PROT_NONE); \
+ (void) mprotect((void*) ((char*)ts->seg->vaddr + REDZONE + ts->stackSize),\
+ REDZONE, PROT_NONE); \
+ /* \
+ ** Fill stack memory with something that turns into an illegal \
+ ** pointer value. This will sometimes find runtime references to \
+ ** uninitialized pointers. We don't do this for solaris because we \
+ ** can use purify instead. \
+ */ \
+ if (_pr_debugStacks) { \
+ memset(ts->allocBase + REDZONE, 0xf7, ts->stackSize); \
+ } \
+ PR_END_MACRO
+#else /* !SOLARIS */
+#define _MD_INIT_STACK(ts,REDZONE) \
+ PR_BEGIN_MACRO \
+ (void) mprotect((void*)ts->seg->vaddr, REDZONE, PROT_NONE); \
+ (void) mprotect((void*) ((char*)ts->seg->vaddr + REDZONE + ts->stackSize),\
+ REDZONE, PROT_NONE); \
+ PR_END_MACRO
+#endif /* !SOLARIS */
+
+/*
+ * _MD_CLEAR_STACK
+ * Allow access to the redzone pages; the access was turned off in
+ * _MD_INIT_STACK.
+ */
+#define _MD_CLEAR_STACK(ts) \
+ PR_BEGIN_MACRO \
+ (void) mprotect((void*)ts->seg->vaddr, REDZONE, PROT_READ|PROT_WRITE);\
+ (void) mprotect((void*) ((char*)ts->seg->vaddr + REDZONE + ts->stackSize),\
+ REDZONE, PROT_READ|PROT_WRITE); \
+ PR_END_MACRO
+
+#else /* DEBUG */
+
+#define _MD_INIT_STACK(ts,REDZONE)
+#define _MD_CLEAR_STACK(ts)
+
+#endif /* DEBUG */
+
+#if !defined(SOLARIS)
+
+#define PR_SET_INTSOFF(newval)
+
+#endif
+
+/************************************************************************/
+
+extern void _PR_UnixInit(void);
+
+/************************************************************************/
+
+struct _MDProcess {
+ pid_t pid;
+};
+
+struct PRProcess;
+struct PRProcessAttr;
+
+/* Create a new process (fork() + exec()) */
+#define _MD_CREATE_PROCESS _MD_CreateUnixProcess
+extern struct PRProcess * _MD_CreateUnixProcess(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const struct PRProcessAttr *attr
+);
+
+#define _MD_DETACH_PROCESS _MD_DetachUnixProcess
+extern PRStatus _MD_DetachUnixProcess(struct PRProcess *process);
+
+/* Wait for a child process to terminate */
+#define _MD_WAIT_PROCESS _MD_WaitUnixProcess
+extern PRStatus _MD_WaitUnixProcess(struct PRProcess *process,
+ PRInt32 *exitCode);
+
+#define _MD_KILL_PROCESS _MD_KillUnixProcess
+extern PRStatus _MD_KillUnixProcess(struct PRProcess *process);
+
+/************************************************************************/
+
+#define _MD_START_INTERRUPTS _MD_StartInterrupts
+#define _MD_STOP_INTERRUPTS _MD_StopInterrupts
+#define _MD_DISABLE_CLOCK_INTERRUPTS _MD_DisableClockInterrupts
+#define _MD_ENABLE_CLOCK_INTERRUPTS _MD_EnableClockInterrupts
+#define _MD_BLOCK_CLOCK_INTERRUPTS _MD_BlockClockInterrupts
+#define _MD_UNBLOCK_CLOCK_INTERRUPTS _MD_UnblockClockInterrupts
+
+/************************************************************************/
+
+extern void _MD_InitCPUS(void);
+#define _MD_INIT_CPUS _MD_InitCPUS
+
+extern void _MD_Wakeup_CPUs(void);
+#define _MD_WAKEUP_CPUS _MD_Wakeup_CPUs
+
+#define _MD_PAUSE_CPU _MD_PauseCPU
+
+#if defined(_PR_LOCAL_THREADS_ONLY) || defined(_PR_GLOBAL_THREADS_ONLY)
+#define _MD_CLEANUP_BEFORE_EXIT()
+#endif
+
+#ifndef IRIX
+#define _MD_EXIT(status) _exit(status)
+#endif
+
+/************************************************************************/
+
+#define _MD_GET_ENV getenv
+#define _MD_PUT_ENV putenv
+
+/************************************************************************/
+
+extern void _MD_MakeNonblock(PRFileDesc *fd);
+#define _MD_MAKE_NONBLOCK _MD_MakeNonblock
+
+/************************************************************************/
+
+extern PRStatus _MD_AllocSegment(PRSegment *seg, PRUint32 size,
+ void *vaddr);
+extern void _MD_FreeSegment(PRSegment *seg);
+
+#define _MD_INIT_SEGS _MD_InitSegs
+#define _MD_ALLOC_SEGMENT _MD_AllocSegment
+#define _MD_FREE_SEGMENT _MD_FreeSegment
+
+/************************************************************************/
+
+#if !defined(HPUX_LW_TIMER)
+#define _MD_INTERVAL_INIT()
+#endif
+#define _MD_INTERVAL_PER_MILLISEC() (_PR_MD_INTERVAL_PER_SEC() / 1000)
+#define _MD_INTERVAL_PER_MICROSEC() (_PR_MD_INTERVAL_PER_SEC() / 1000000)
+
+/************************************************************************/
+
+#define _MD_ERRNO() (errno)
+#define _MD_GET_SOCKET_ERROR() (errno)
+
+/************************************************************************/
+
+extern PRInt32 _MD_AvailableSocket(PRInt32 osfd);
+
+extern void _MD_InitSegs(void);
+extern void _MD_StartInterrupts(void);
+extern void _MD_StopInterrupts(void);
+extern void _MD_DisableClockInterrupts(void);
+extern void _MD_BlockClockInterrupts(void);
+extern void _MD_UnblockClockInterrupts(void);
+extern void _MD_PauseCPU(PRIntervalTime timeout);
+
+extern PRStatus _MD_open_dir(struct _MDDir *, const char *);
+extern PRInt32 _MD_close_dir(struct _MDDir *);
+extern char * _MD_read_dir(struct _MDDir *, PRIntn);
+extern PRInt32 _MD_open(const char *name, PRIntn osflags, PRIntn mode);
+extern PRInt32 _MD_delete(const char *name);
+extern PRInt32 _MD_getfileinfo(const char *fn, PRFileInfo *info);
+extern PRInt32 _MD_getfileinfo64(const char *fn, PRFileInfo64 *info);
+extern PRInt32 _MD_getopenfileinfo(const PRFileDesc *fd, PRFileInfo *info);
+extern PRInt32 _MD_getopenfileinfo64(const PRFileDesc *fd, PRFileInfo64 *info);
+extern PRInt32 _MD_rename(const char *from, const char *to);
+extern PRInt32 _MD_access(const char *name, PRAccessHow how);
+extern PRInt32 _MD_mkdir(const char *name, PRIntn mode);
+extern PRInt32 _MD_rmdir(const char *name);
+extern PRInt32 _MD_accept_read(PRInt32 sock, PRInt32 *newSock,
+ PRNetAddr **raddr, void *buf, PRInt32 amount);
+extern PRInt32 _PR_UnixTransmitFile(PRFileDesc *sd, PRFileDesc *fd,
+ const void *headers, PRInt32 hlen,
+ PRTransmitFileFlags flags, PRIntervalTime timeout);
+
+extern PRStatus _MD_LockFile(PRInt32 osfd);
+extern PRStatus _MD_TLockFile(PRInt32 osfd);
+extern PRStatus _MD_UnlockFile(PRInt32 osfd);
+
+#define _MD_OPEN_DIR(dir, name) _MD_open_dir(dir, name)
+#define _MD_CLOSE_DIR(dir) _MD_close_dir(dir)
+#define _MD_READ_DIR(dir, flags) _MD_read_dir(dir, flags)
+#define _MD_OPEN(name, osflags, mode ) _MD_open(name, osflags, mode)
+extern PRInt32 _MD_read(PRFileDesc *fd, void *buf, PRInt32 amount);
+#define _MD_READ(fd,buf,amount) _MD_read(fd,buf,amount)
+extern PRInt32 _MD_write(PRFileDesc *fd, const void *buf, PRInt32 amount);
+#define _MD_WRITE(fd,buf,amount) _MD_write(fd,buf,amount)
+#define _MD_DELETE(name) _MD_delete(name)
+#define _MD_GETFILEINFO(fn, info) _MD_getfileinfo(fn, info)
+#define _MD_GETFILEINFO64(fn, info) _MD_getfileinfo64(fn, info)
+#define _MD_GETOPENFILEINFO(fd, info) _MD_getopenfileinfo(fd, info)
+#define _MD_GETOPENFILEINFO64(fd, info) _MD_getopenfileinfo64(fd, info)
+#define _MD_RENAME(from, to) _MD_rename(from, to)
+#define _MD_ACCESS(name, how) _MD_access(name, how)
+#define _MD_MKDIR(name, mode) _MD_mkdir(name, mode)
+#define _MD_RMDIR(name) _MD_rmdir(name)
+#define _MD_ACCEPT_READ(sock, newSock, raddr, buf, amount) _MD_accept_read(sock, newSock, raddr, buf, amount)
+
+#define _MD_LOCKFILE _MD_LockFile
+#define _MD_TLOCKFILE _MD_TLockFile
+#define _MD_UNLOCKFILE _MD_UnlockFile
+
+
+extern PRInt32 _MD_socket(int af, int type, int flags);
+#define _MD_SOCKET _MD_socket
+extern PRInt32 _MD_connect(PRFileDesc *fd, const PRNetAddr *addr,
+ PRUint32 addrlen, PRIntervalTime timeout);
+#define _MD_CONNECT _MD_connect
+extern PRInt32 _MD_accept(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen,
+ PRIntervalTime timeout);
+#define _MD_ACCEPT _MD_accept
+extern PRInt32 _MD_bind(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen);
+#define _MD_BIND _MD_bind
+extern PRInt32 _MD_listen(PRFileDesc *fd, PRIntn backlog);
+#define _MD_LISTEN _MD_listen
+extern PRInt32 _MD_shutdown(PRFileDesc *fd, PRIntn how);
+#define _MD_SHUTDOWN _MD_shutdown
+
+extern PRInt32 _MD_recv(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout);
+#define _MD_RECV _MD_recv
+extern PRInt32 _MD_send(PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout);
+#define _MD_SEND _MD_send
+extern PRInt32 _MD_recvfrom(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRNetAddr *addr, PRUint32 *addrlen,
+ PRIntervalTime timeout);
+#define _MD_RECVFROM _MD_recvfrom
+extern PRInt32 _MD_sendto(PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, const PRNetAddr *addr, PRUint32 addrlen,
+ PRIntervalTime timeout);
+#define _MD_SENDTO _MD_sendto
+extern PRInt32 _MD_writev(PRFileDesc *fd, struct PRIOVec *iov,
+ PRInt32 iov_size, PRIntervalTime timeout);
+#define _MD_WRITEV _MD_writev
+
+extern PRInt32 _MD_socketavailable(PRFileDesc *fd);
+#define _MD_SOCKETAVAILABLE _MD_socketavailable
+extern PRInt64 _MD_socketavailable64(PRFileDesc *fd);
+#define _MD_SOCKETAVAILABLE64 _MD_socketavailable64
+
+extern PRInt32 _MD_pr_poll(PRPollDesc *pds, PRIntn npds,
+ PRIntervalTime timeout);
+#define _MD_PR_POLL _MD_pr_poll
+
+extern PRInt32 _MD_close(PRInt32 osfd);
+#define _MD_CLOSE_FILE _MD_close
+extern PRInt32 _MD_lseek(PRFileDesc*, PRInt32, PRSeekWhence);
+#define _MD_LSEEK _MD_lseek
+extern PRInt64 _MD_lseek64(PRFileDesc*, PRInt64, PRSeekWhence);
+#define _MD_LSEEK64 _MD_lseek64
+extern PRInt32 _MD_fsync(PRFileDesc *fd);
+#define _MD_FSYNC _MD_fsync
+
+extern PRInt32 _MD_socketpair(int af, int type, int flags, PRInt32 *osfd);
+#define _MD_SOCKETPAIR _MD_socketpair
+
+#define _MD_CLOSE_SOCKET _MD_close
+
+#ifndef NO_NSPR_10_SUPPORT
+#define _MD_STAT stat
+#endif
+
+extern PRStatus _MD_getpeername(PRFileDesc *fd, PRNetAddr *addr,
+ PRUint32 *addrlen);
+#define _MD_GETPEERNAME _MD_getpeername
+extern PRStatus _MD_getsockname(PRFileDesc *fd, PRNetAddr *addr,
+ PRUint32 *addrlen);
+#define _MD_GETSOCKNAME _MD_getsockname
+
+extern PRStatus _MD_getsockopt(PRFileDesc *fd, PRInt32 level,
+ PRInt32 optname, char* optval, PRInt32* optlen);
+#define _MD_GETSOCKOPT _MD_getsockopt
+extern PRStatus _MD_setsockopt(PRFileDesc *fd, PRInt32 level,
+ PRInt32 optname, const char* optval, PRInt32 optlen);
+#define _MD_SETSOCKOPT _MD_setsockopt
+
+extern PRStatus _MD_gethostname(char *name, PRUint32 namelen);
+#define _MD_GETHOSTNAME _MD_gethostname
+
+extern int _MD_unix_get_nonblocking_connect_error(int osfd);
+
+#define HAVE_SOCKET_REUSEADDR
+#define HAVE_SOCKET_KEEPALIVE
+
+/* Memory-mapped files */
+
+struct _MDFileMap {
+ PRIntn prot;
+ PRIntn flags;
+};
+
+extern PRStatus _MD_CreateFileMap(struct PRFileMap *fmap, PRInt64 size);
+#define _MD_CREATE_FILE_MAP _MD_CreateFileMap
+
+extern void * _MD_MemMap(struct PRFileMap *fmap, PRInt64 offset,
+ PRUint32 len);
+#define _MD_MEM_MAP _MD_MemMap
+
+extern PRStatus _MD_MemUnmap(void *addr, PRUint32 size);
+#define _MD_MEM_UNMAP _MD_MemUnmap
+
+extern PRStatus _MD_CloseFileMap(struct PRFileMap *fmap);
+#define _MD_CLOSE_FILE_MAP _MD_CloseFileMap
+
+/*
+ * The standard (XPG4) gettimeofday() (from BSD) takes two arguments.
+ * On some SVR4 derivatives, gettimeofday() takes only one argument.
+ * The GETTIMEOFDAY macro is intended to hide this difference.
+ */
+#ifdef HAVE_SVID_GETTOD
+#define GETTIMEOFDAY(tp) gettimeofday(tp)
+#else
+#define GETTIMEOFDAY(tp) gettimeofday((tp), NULL)
+#endif
+
+#if defined(LINUX) && defined(_PR_PTHREADS) && !(__GLIBC__ >= 2)
+#define _PR_NEED_FAKE_POLL
+#endif
+
+#if defined(_PR_NEED_FAKE_POLL)
+
+/*
+ * Some platforms don't have poll(), but our pthreads code calls poll().
+ * As a temporary measure, I implemented a fake poll() using select().
+ * Here are the struct and macro definitions copied from sys/poll.h
+ * on Solaris 2.5.
+ */
+
+struct pollfd {
+ int fd;
+ short events;
+ short revents;
+};
+
+/* poll events */
+
+#define POLLIN 0x0001 /* fd is readable */
+#define POLLPRI 0x0002 /* high priority info at fd */
+#define POLLOUT 0x0004 /* fd is writeable (won't block) */
+#define POLLRDNORM 0x0040 /* normal data is readable */
+#define POLLWRNORM POLLOUT
+#define POLLRDBAND 0x0080 /* out-of-band data is readable */
+#define POLLWRBAND 0x0100 /* out-of-band data is writeable */
+
+#define POLLNORM POLLRDNORM
+
+#define POLLERR 0x0008 /* fd has error condition */
+#define POLLHUP 0x0010 /* fd has been hung up on */
+#define POLLNVAL 0x0020 /* invalid pollfd entry */
+
+extern int poll(struct pollfd *, unsigned long, int);
+
+#endif /* _PR_NEED_FAKE_POLL */
+
+/*
+** A vector of the UNIX I/O calls we use. These are here to smooth over
+** the rough edges needed for large files. All of NSPR's implmentaions
+** go through this vector using syntax of the form
+** result = _md_iovector.xxx64(args);
+*/
+
+#if defined(SOLARIS2_5)
+/*
+** Special case: Solaris 2.5.1
+** Solaris starts to have 64-bit file I/O in 2.6. We build on Solaris
+** 2.5.1 so that we can use the same binaries on both Solaris 2.5.1 and
+** 2.6. At run time, we detect whether 64-bit file I/O is available by
+** looking up the 64-bit file function symbols in libc. At build time,
+** we need to define the 64-bit file I/O datatypes that are compatible
+** with their definitions on Solaris 2.6.
+*/
+typedef PRInt64 off64_t;
+typedef PRUint64 ino64_t;
+typedef PRUint64 blkcnt64_t;
+struct stat64 {
+ dev_t st_dev;
+ long st_pad1[3];
+ ino64_t st_ino;
+ mode_t st_mode;
+ nlink_t st_nlink;
+ uid_t st_uid;
+ gid_t st_gid;
+ dev_t st_rdev;
+ long t_pad2[2];
+ off64_t st_size;
+ timestruc_t st_atim;
+ timestruc_t st_mtim;
+ timestruc_t st_ctim;
+ long st_blksize;
+ blkcnt64_t st_blocks;
+ char st_fstype[_ST_FSTYPSZ];
+ long st_pad4[8];
+};
+typedef struct stat64 _MDStat64;
+
+#elif defined(_PR_HAVE_OFF64_T)
+typedef struct stat64 _MDStat64;
+#elif defined(_PR_HAVE_LARGE_OFF_T) || defined(_PR_NO_LARGE_FILES)
+typedef struct stat _MDStat64;
+#else
+#error "I don't know yet"
+#endif
+
+typedef PRIntn (*_MD_Fstat64)(PRIntn osfd, _MDStat64 *buf);
+typedef PRIntn (*_MD_Open64)(const char *path, int oflag, ...);
+typedef PRIntn (*_MD_Stat64)(const char *path, _MDStat64 *buf);
+typedef PRInt64 (*_MD_Lseek64)(PRIntn osfd, PRInt64, PRIntn whence);
+typedef PRIntn (*_MD_Lockf64)(PRIntn osfd, PRIntn function, PRInt64 size);
+typedef void* (*_MD_Mmap64)(
+ void *addr, PRSize len, PRIntn prot, PRIntn flags,
+ PRIntn fildes, PRInt64 offset);
+struct _MD_IOVector
+{
+ _MD_Open64 _open64;
+ _MD_Mmap64 _mmap64;
+ _MD_Stat64 _stat64;
+ _MD_Fstat64 _fstat64;
+ _MD_Lockf64 _lockf64;
+ _MD_Lseek64 _lseek64;
+};
+extern struct _MD_IOVector _md_iovector;
+
+#endif /* prunixos_h___ */
diff --git a/pr/include/md/_unixware.cfg b/pr/include/md/_unixware.cfg
new file mode 100644
index 00000000..fd96a083
--- /dev/null
+++ b/pr/include/md/_unixware.cfg
@@ -0,0 +1,121 @@
+/* -*- 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_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef UNIXWARE
+#define UNIXWARE
+#endif
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#undef HAVE_LONG_LONG
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#define _PR_POLL_BACKCOMPAT
+
+#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/include/md/_unixware.h b/pr/include/md/_unixware.h
new file mode 100644
index 00000000..8d8828c2
--- /dev/null
+++ b/pr/include/md/_unixware.h
@@ -0,0 +1,200 @@
+/* -*- 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_unixware_defs_h___
+#define nspr_unixware_defs_h___
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "unixware"
+#define _PR_SI_SYSNAME "UnixWare"
+#define _PR_SI_ARCHITECTURE "x86"
+#define PR_DLL_SUFFIX ".so"
+
+#define _PR_VMBASE 0x30000000
+#define _PR_STACK_VMBASE 0x50000000
+#define _MD_DEFAULT_STACK_SIZE 65536L
+#define _MD_MMAP_FLAGS MAP_PRIVATE
+
+#ifndef HAVE_WEAK_IO_SYMBOLS
+#define HAVE_WEAK_IO_SYMBOLS
+#endif
+#define _PR_POLL_AVAILABLE
+#define _PR_USE_POLL
+#define _PR_STAT_HAS_ST_ATIM_UNION
+
+#undef HAVE_STACK_GROWING_UP
+#define HAVE_NETCONFIG
+#define HAVE_DLL
+#define USE_DLFCN
+#define HAVE_STRERROR
+#define NEED_STRFTIME_LOCK
+#define NEED_TIME_R
+#define _PR_NEED_STRCASECMP
+
+#define USE_SETJMP
+
+#include <setjmp.h>
+
+#define _SETJMP setjmp
+#define _LONGJMP longjmp
+#define _PR_CONTEXT_TYPE jmp_buf
+#define _MD_GET_SP(_t) (_t)->md.context[4]
+#define _PR_NUM_GCREGS _JBLEN
+
+#define CONTEXT(_th) ((_th)->md.context)
+
+/*
+** Initialize the thread context preparing it to execute _main.
+*/
+#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \
+{ \
+ *status = PR_TRUE; \
+ if(_SETJMP(CONTEXT(_thread))) (*_main)(); \
+ _MD_GET_SP(_thread) = (int) ((_sp) - 128); \
+}
+
+#define _MD_SWITCH_CONTEXT(_thread) \
+ if (!_SETJMP(CONTEXT(_thread))) { \
+ (_thread)->md.errcode = errno; \
+ _PR_Schedule(); \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _MD_RESTORE_CONTEXT(_thread) \
+{ \
+ errno = (_thread)->md.errcode; \
+ _MD_SET_CURRENT_THREAD(_thread); \
+ _LONGJMP(CONTEXT(_thread), 1); \
+}
+
+/* Machine-dependent (MD) data structures.
+ * Don't use SVR4 native threads (yet).
+ */
+
+struct _MDThread {
+ _PR_CONTEXT_TYPE context;
+ int id;
+ int errcode;
+};
+
+struct _MDThreadStack {
+ PRInt8 notused;
+};
+
+struct _MDLock {
+ PRInt8 notused;
+};
+
+struct _MDSemaphore {
+ PRInt8 notused;
+};
+
+struct _MDCVar {
+ PRInt8 notused;
+};
+
+struct _MDSegment {
+ PRInt8 notused;
+};
+
+/*
+ * md-specific cpu structure field
+ */
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+
+struct _MDCPU_Unix {
+ PRCList ioQ;
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+ PRInt32 ioq_osfd_cnt;
+#ifndef _PR_USE_POLL
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+#else
+ struct pollfd *ioq_pollfds;
+ int ioq_pollfds_size;
+#endif /* _PR_USE_POLL */
+};
+
+#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd)
+#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt)
+#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds)
+#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size)
+
+#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32
+
+struct _MDCPU {
+ struct _MDCPU_Unix md_unix;
+};
+
+#define _MD_INIT_LOCKS()
+#define _MD_NEW_LOCK(lock) PR_SUCCESS
+#define _MD_FREE_LOCK(lock)
+#define _MD_LOCK(lock)
+#define _MD_UNLOCK(lock)
+#define _MD_INIT_IO()
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+/*
+ * The following are copied from _sunos.h, _aix.h. This means
+ * some of them should probably be moved into _unixos.h. But
+ * _irix.h seems to be quite different in regard to these macros.
+ */
+#define _MD_GET_INTERVAL _PR_UNIX_GetInterval
+#define _MD_INTERVAL_PER_SEC _PR_UNIX_TicksPerSecond
+
+#define _MD_EARLY_INIT _MD_EarlyInit
+#define _MD_FINAL_INIT _PR_UnixInit
+#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu)
+#define _MD_INIT_THREAD _MD_InitializeThread
+#define _MD_EXIT_THREAD(thread)
+#define _MD_SUSPEND_THREAD(thread)
+#define _MD_RESUME_THREAD(thread)
+#define _MD_CLEAN_THREAD(_thread)
+
+/*
+ * We wrapped the select() call. _MD_SELECT refers to the built-in,
+ * unwrapped version.
+ */
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/select.h>
+extern int _select(int nfds, fd_set *readfds, fd_set *writefds,
+ fd_set *execptfds, struct timeval *timeout);
+#define _MD_SELECT _select
+
+#define _MD_POLL _poll
+extern int _poll(struct pollfd *fds, unsigned long nfds, int timeout);
+
+#endif /* nspr_unixware_defs_h___ */
diff --git a/pr/include/md/_win16.cfg b/pr/include/md/_win16.cfg
new file mode 100644
index 00000000..e52a7541
--- /dev/null
+++ b/pr/include/md/_win16.cfg
@@ -0,0 +1,158 @@
+/* -*- 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.
+ */
+
+/*
+** _win16.cfg -- prcpucfg.h for win16
+**
+**
+** lth. 14-Apr-1997. New. Made from _win95.cfg
+*/
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_PC
+#define XP_PC
+#endif
+
+#ifndef WIN16
+#define WIN16
+#undef WIN32
+#endif
+
+#if defined(_M_IX86) || defined(_X86_)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 2
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_DOUBLE 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 16
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_WORD 32
+#define PR_BITS_PER_DWORD 64
+#define PR_BITS_PER_DOUBLE 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 4
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_WORD_LOG2 4
+#define PR_BITS_PER_DWORD_LOG2 6
+#define PR_BITS_PER_DOUBLE_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 2
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_WORD 2
+#define PR_ALIGN_OF_DWORD 8
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 2
+
+#else /* defined(_M_IX86) || defined(_X86_) */
+
+#error unknown processor architecture
+
+#endif /* defined(_M_IX86) || defined(_X86_) */
+
+#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 */
+
+#undef HAVE_LONG_LONG
+
+/*
+** HAVE_WATCOM_BUG_1
+** When HAVE_WATCOM_BUG_1 is defined, special case code is
+** used to circumvent the bug.
+** Functions declared __cdecl in DLLs returning floating point types
+** generate bad return code and will not return the intended result.
+*/
+#define HAVE_WATCOM_BUG_1
+
+/*
+** HAVE_WATCOM_BUG_2
+** When HAVE_WATCOM_BUG_2 is defined, special case code is
+** used to circumvent the bug.
+** Functions declared __cdecl in DLLs returning a structure by value
+** generate bad return values.
+** Yes, similar to Watcom Bug 1.
+*/
+#define HAVE_WATCOM_BUG_2
+
+#endif /* nspr_cpucfg___ */
diff --git a/pr/include/md/_win16.h b/pr/include/md/_win16.h
new file mode 100644
index 00000000..399b7c6d
--- /dev/null
+++ b/pr/include/md/_win16.h
@@ -0,0 +1,556 @@
+/* -*- 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_win16_defs_h___
+#define nspr_win16_defs_h___
+
+#include <windows.h>
+#include <winsock.h>
+#include <errno.h>
+#include <direct.h>
+
+#include "nspr.h"
+/* $$ fix this */
+#define Remind(x)
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "win16"
+#define _PR_SI_SYSNAME "WIN16"
+#define _PR_SI_ARCHITECTURE "x86" /* XXXMB hardcode for now */
+
+#define HAVE_DLL
+#define _PR_NO_PREEMPT
+#define _PR_LOCAL_THREADS_ONLY
+#undef _PR_GLOBAL_THREADS_ONLY
+#undef HAVE_THREAD_AFFINITY
+#define HAVE_SOCKET_REUSEADDR
+#define HAVE_SOCKET_KEEPALIVE
+#define _PR_HAVE_ATOMIC_OPS
+
+/* --- Common User-Thread/Native-Thread Definitions --------------------- */
+
+extern struct PRLock *_pr_schedLock;
+extern char * _pr_top_of_task_stack;
+
+
+/* --- Typedefs --- */
+
+#define PR_NUM_GCREGS 9
+typedef PRInt32 PR_CONTEXT_TYPE[PR_NUM_GCREGS];
+
+#define _MD_MAGIC_THREAD 0x22222222
+#define _MD_MAGIC_THREADSTACK 0x33333333
+#define _MD_MAGIC_SEGMENT 0x44444444
+#define _MD_MAGIC_DIR 0x55555555
+#define _MD_MAGIC_CV 0x66666666
+
+
+typedef struct _PRWin16PollDesc
+{
+ PRInt32 osfd;
+ PRInt16 in_flags;
+ PRInt16 out_flags;
+} _PRWin16PollDesc;
+
+typedef struct PRPollQueue
+{
+ PRCList links; /* for linking PRPollQueue's together */
+ _PRWin16PollDesc *pds; /* array of poll descriptors */
+ PRUintn npds; /* length of the array */
+ PRPackedBool on_ioq; /* is this on the async i/o work q? */
+ PRIntervalTime timeout; /* timeout, in ticks */
+ struct PRThread *thr;
+} PRPollQueue;
+
+#define _PR_POLLQUEUE_PTR(_qp) \
+ ((PRPollQueue *) ((char*) (_qp) - offsetof(PRPollQueue,links)))
+
+PR_EXTERN(PRInt32) _PR_WaitForFD(PRInt32 osfd, PRUintn how,
+ PRIntervalTime timeout);
+PR_EXTERN(void) _PR_Unblock_IO_Wait(struct PRThread *thr);
+
+#define _PR_MD_MAX_OSFD FD_SETSIZE
+#define _PR_IOQ(_cpu) ((_cpu)->md.ioQ)
+#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu))
+#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.fd_read_set)
+#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.fd_read_cnt)
+#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.fd_write_set)
+#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.fd_write_cnt)
+#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.fd_exception_set)
+#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.fd_exception_cnt)
+#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.ioq_timeout)
+#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.ioq_max_osfd)
+
+struct _MDCPU {
+ PRCList ioQ;
+ fd_set fd_read_set, fd_write_set, fd_exception_set;
+ PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD],
+ fd_exception_cnt[_PR_MD_MAX_OSFD];
+ PRUint32 ioq_timeout;
+ PRInt32 ioq_max_osfd;
+};
+
+struct _MDThread {
+ /* The overlapped structure must be first! */
+ HANDLE blocked_sema; /* Threads block on this when waiting
+ * for IO or CondVar.
+ */
+ PRInt32 errcode; /* preserved errno for this thread */
+ CATCHBUF context; /* thread context for Throw() */
+ void *SP; /* Stack pointer, used only by GarbColl */
+ int threadNumber; /* instrumentation: order of creation */
+ _PRWin16PollDesc thr_pd; /* poll descriptor for i/o */
+ PRPollQueue thr_pq; /* i/o parameters */
+ void *exceptionContext; /* mfc exception context */
+ char guardBand[24]; /* don't overwrite this */
+ PRUint32 magic; /* self identifier, for debug */
+};
+
+struct _MDThreadStack {
+ PRUint32 magic; /* for debugging */
+ PRIntn cxByteCount; /* number of stack bytes to move */
+ char * stackTop; /* high address on stack */
+};
+
+struct _MDSegment {
+ PRUint32 magic; /* for debugging */
+};
+
+
+struct _MDLock {
+ PRUint32 magic; /* for debugging */
+ PRUint32 mutex;
+};
+
+struct _MDDir {
+ PRUint32 magic; /* for debugging */
+ struct dirent *dir;
+};
+
+struct _MDCVar {
+ PRUint32 magic;
+};
+
+struct _MDSemaphore {
+ PRInt32 unused;
+};
+
+struct _MDFileDesc {
+ PRInt32 osfd;
+};
+
+struct _MDProcess {
+ HANDLE handle;
+ DWORD id;
+};
+
+/*
+** Microsoft 'struct _stat'
+** ... taken directly from msvc 1.52c's header file sys/stat.h
+** see PR_Stat() implemented in w16io.c
+** See BugSplat: 98516
+*/
+#pragma pack(push)
+#pragma pack(2)
+
+typedef unsigned short _ino_t;
+typedef short _dev_t;
+typedef long _off_t;
+
+typedef struct _MDMSStat {
+ _dev_t st_dev;
+ _ino_t st_ino;
+ unsigned short st_mode;
+ short st_nlink;
+ short st_uid;
+ short st_gid;
+ _dev_t st_rdev;
+ _off_t st_size;
+ time_t st_atime;
+ time_t st_mtime;
+ time_t st_ctime;
+} _MDMSStat;
+#pragma pack(pop)
+
+/* --- Errors --- */
+ /* These are NSPR generated error codes which need to be unique from
+ * OS error codes.
+ */
+#define _MD_UNIQUEBASE 50000
+#define _MD_EINTERRUPTED _MD_UNIQUEBASE + 1
+#define _MD_ETIMEDOUT _MD_UNIQUEBASE + 2
+#define _MD_EIO _MD_UNIQUEBASE + 3
+
+struct PRProcess;
+struct PRProcessAttr;
+
+/* --- Create a new process --- */
+#define _MD_CREATE_PROCESS _PR_CreateWindowsProcess
+extern struct PRProcess * _PR_CreateWindowsProcess(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const struct PRProcessAttr *attr
+);
+
+#define _MD_DETACH_PROCESS _PR_DetachWindowsProcess
+extern PRStatus _PR_DetachWindowsProcess(struct PRProcess *process);
+
+/* --- Wait for a child process to terminate --- */
+#define _MD_WAIT_PROCESS _PR_WaitWindowsProcess
+extern PRStatus _PR_WaitWindowsProcess(struct PRProcess *process,
+ PRInt32 *exitCode);
+
+#define _MD_KILL_PROCESS _PR_KillWindowsProcess
+extern PRStatus _PR_KillWindowsProcess(struct PRProcess *process);
+
+
+/* --- Misc stuff --- */
+
+#define MD_ASSERTINT( x ) PR_ASSERT( (x) < 65535 )
+
+/* --- IO stuff --- */
+#define MAX_PATH 256
+#define _MD_ERRNO() errno
+#define GetLastError() errno
+
+#define _MD_GET_FILE_ERROR() errno
+#define _MD_SET_FILE_ERROR(_err) errno = (_err)
+
+#define _MD_OPEN _PR_MD_OPEN
+#define _MD_READ _PR_MD_READ
+#define _MD_WRITE _PR_MD_WRITE
+#define _MD_WRITEV _PR_MD_WRITEV
+#define _MD_LSEEK _PR_MD_LSEEK
+#define _MD_LSEEK64 _PR_MD_LSEEK64
+#define _MD_CLOSE_FILE _PR_MD_CLOSE_FILE
+#define _MD_GETFILEINFO _PR_MD_GETFILEINFO
+#define _MD_GETOPENFILEINFO _PR_MD_GETOPENFILEINFO
+#define _MD_STAT _PR_MD_STAT
+#define _MD_RENAME _PR_MD_RENAME
+#define _MD_ACCESS _PR_MD_ACCESS
+#define _MD_DELETE _PR_MD_DELETE
+#define _MD_MKDIR _PR_MD_MKDIR
+#define _MD_RMDIR _PR_MD_RMDIR
+#define _MD_LOCKFILE _PR_MD_LOCKFILE
+#define _MD_TLOCKFILE _PR_MD_TLOCKFILE
+#define _MD_UNLOCKFILE _PR_MD_UNLOCKFILE
+
+
+/* --- Socket IO stuff --- */
+#define _MD_EACCES WSAEACCES
+#define _MD_EADDRINUSE WSAEADDRINUSE
+#define _MD_EADDRNOTAVAIL WSAEADDRNOTAVAIL
+#define _MD_EAFNOSUPPORT WSAEAFNOSUPPORT
+#define _MD_EAGAIN WSAEWOULDBLOCK
+#define _MD_EALREADY WSAEALREADY
+#define _MD_EBADF WSAEBADF
+#define _MD_ECONNREFUSED WSAECONNREFUSED
+#define _MD_ECONNRESET WSAECONNRESET
+#define _MD_EFAULT WSAEFAULT
+#define _MD_EINPROGRESS WSAEINPROGRESS
+#define _MD_EINTR WSAEINTR
+#define _MD_EINVAL EINVAL
+#define _MD_EISCONN WSAEISCONN
+#define _MD_ENETUNREACH WSAENETUNREACH
+#define _MD_ENOENT ENOENT
+#define _MD_ENOTCONN WSAENOTCONN
+#define _MD_ENOTSOCK WSAENOTSOCK
+#define _MD_EOPNOTSUPP WSAEOPNOTSUPP
+#define _MD_EWOULDBLOCK WSAEWOULDBLOCK
+#define _MD_GET_SOCKET_ERROR() WSAGetLastError()
+#define _MD_SET_SOCKET_ERROR(_err) WSASetLastError(_err)
+
+#define _MD_MAKE_NONBLOCK _PR_MD_MAKE_NONBLOCK
+#define _MD_SHUTDOWN _PR_MD_SHUTDOWN
+#define _MD_LISTEN _PR_MD_LISTEN
+#define _MD_CLOSE_SOCKET _PR_MD_CLOSE_SOCKET
+#define _MD_SENDTO _PR_MD_SENDTO
+#define _MD_RECVFROM _PR_MD_RECVFROM
+#define _MD_SOCKETPAIR(s, type, proto, sv) -1
+#define _MD_GETSOCKNAME _PR_MD_GETSOCKNAME
+#define _MD_GETPEERNAME _PR_MD_GETPEERNAME
+#define _MD_GETSOCKOPT _PR_MD_GETSOCKOPT
+#define _MD_SETSOCKOPT _PR_MD_SETSOCKOPT
+#define _MD_SELECT select
+#define _MD_FSYNC _PR_MD_FSYNC
+#define _MD_SOCKETAVAILABLE _PR_MD_SOCKETAVAILABLE
+
+#define _MD_INIT_ATOMIC()
+#define _MD_ATOMIC_INCREMENT(x) (*x++)
+#define _MD_ATOMIC_ADD(ptr, val) ((*x) += val)
+#define _MD_ATOMIC_DECREMENT(x) (*x--)
+#define _MD_ATOMIC_SET(x,y) (*x, y)
+
+#define _MD_INIT_IO _PR_MD_INIT_IO
+#define _MD_TRANSMITFILE _PR_MD_TRANSMITFILE
+
+/* win95 doesn't have async IO */
+#define _MD_SOCKET _PR_MD_SOCKET
+#define _MD_CONNECT _PR_MD_CONNECT
+#define _MD_ACCEPT _PR_MD_ACCEPT
+#define _MD_BIND _PR_MD_BIND
+#define _MD_RECV _PR_MD_RECV
+#define _MD_SEND _PR_MD_SEND
+
+#define _MD_CHECK_FOR_EXIT()
+
+/* --- Scheduler stuff --- */
+#define _MD_PAUSE_CPU _PR_MD_PAUSE_CPU
+
+/* --- DIR stuff --- */
+#define PR_DIRECTORY_SEPARATOR '\\'
+#define PR_DIRECTORY_SEPARATOR_STR "\\"
+#define PR_PATH_SEPARATOR ';'
+#define PR_PATH_SEPARATOR_STR ";"
+#define _MD_OPEN_DIR _PR_MD_OPEN_DIR
+#define _MD_CLOSE_DIR _PR_MD_CLOSE_DIR
+#define _MD_READ_DIR _PR_MD_READ_DIR
+
+/* --- Segment stuff --- */
+#define _MD_INIT_SEGS()
+#define _MD_ALLOC_SEGMENT _MD_AllocSegment
+#define _MD_FREE_SEGMENT _MD_FreeSegment
+
+/* --- Environment Stuff --- */
+#define _MD_GET_ENV _PR_MD_GET_ENV
+#define _MD_PUT_ENV _PR_MD_PUT_ENV
+
+/* --- Threading Stuff --- */
+#define _MD_DEFAULT_STACK_SIZE 32767L
+#define _MD_INIT_THREAD _PR_MD_INIT_THREAD
+#define _MD_INIT_PRIMORDIAL_THREAD _PR_MD_INIT_PRIMORDIAL_THREAD
+#define _MD_CREATE_THREAD(t,f,p,sc,st,stsiz) (PR_SUCCESS)
+#define _MD_YIELD _PR_MD_YIELD
+#define _MD_SET_PRIORITY(t,p)
+#define _MD_CLEAN_THREAD(t)
+#define _MD_SETTHREADAFFINITYMASK _PR_MD_SETTHREADAFFINITYMASK
+#define _MD_GETTHREADAFFINITYMASK _PR_MD_GETTHREADAFFINITYMASK
+#define _MD_EXIT_THREAD
+#define _MD_SUSPEND_THREAD _PR_MD_SUSPEND_THREAD
+#define _MD_RESUME_THREAD _PR_MD_RESUME_THREAD
+#define _MD_SUSPEND_CPU _PR_MD_SUSPEND_CPU
+#define _MD_RESUME_CPU _PR_MD_RESUME_CPU
+#define _MD_BEGIN_SUSPEND_ALL()
+#define _MD_BEGIN_RESUME_ALL()
+#define _MD_END_SUSPEND_ALL()
+#define _MD_END_RESUME_ALL()
+
+/* --- Lock stuff --- */
+/*
+** Win16 does not need MD locks.
+*/
+#define _PR_LOCK _MD_LOCK
+#define _PR_UNLOCK _MD_UNLOCK
+
+#define _MD_NEW_LOCK(l) (PR_SUCCESS)
+#define _MD_FREE_LOCK(l)
+#define _MD_LOCK(l)
+#define _MD_TEST_AND_LOCK(l) (PR_FAILURE)
+#define _MD_UNLOCK(l)
+
+/* --- lock and cv waiting --- */
+#define _MD_WAIT _PR_MD_WAIT
+#define _MD_WAKEUP_WAITER(a)
+#define _MD_WAKEUP_CPUS _PR_MD_WAKEUP_CPUS
+
+/* --- CVar ------------------- */
+#define _MD_WAIT_CV _PR_MD_WAIT_CV
+#define _MD_NEW_CV _PR_MD_NEW_CV
+#define _MD_FREE_CV _PR_MD_FREE_CV
+#define _MD_NOTIFY_CV _PR_MD_NOTIFY_CV
+#define _MD_NOTIFYALL_CV _PR_MD_NOTIFYALL_CV
+
+ /* XXXMB- the IOQ stuff is certainly not working correctly yet. */
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+
+/* --- Initialization stuff --- */
+PR_EXTERN(void) _MD_INIT_RUNNING_CPU(struct _PRCPU *cpu );
+#define _MD_START_INTERRUPTS()
+#define _MD_STOP_INTERRUPTS()
+#define _MD_DISABLE_CLOCK_INTERRUPTS()
+#define _MD_ENABLE_CLOCK_INTERRUPTS()
+#define _MD_BLOCK_CLOCK_INTERRUPTS()
+#define _MD_UNBLOCK_CLOCK_INTERRUPTS()
+#define _MD_EARLY_INIT _PR_MD_EARLY_INIT
+#define _MD_FINAL_INIT _PR_MD_FINAL_INIT
+#define _MD_INIT_CPUS()
+
+/* --- User Threading stuff --- */
+#define _MD_EXIT
+
+#define _MD_CLEANUP_BEFORE_EXIT _PR_MD_CLEANUP_BEFORE_EXIT
+
+/* --- Intervals --- */
+#define _MD_INTERVAL_INIT _PR_MD_INTERVAL_INIT
+#define _MD_GET_INTERVAL _PR_MD_GET_INTERVAL
+#define _MD_INTERVAL_PER_SEC _PR_MD_INTERVAL_PER_SEC
+#define _MD_INTERVAL_PER_MILLISEC() (_PR_MD_INTERVAL_PER_SEC() / 1000)
+#define _MD_INTERVAL_PER_MICROSEC() (_PR_MD_INTERVAL_PER_SEC() / 1000000)
+
+/* --- Scheduler stuff --- */
+#define LOCK_SCHEDULER() 0
+#define UNLOCK_SCHEDULER() 0
+#define _PR_LockSched() 0
+#define _PR_UnlockSched() 0
+
+/* --- Initialization stuff --- */
+#define _MD_INIT_LOCKS()
+
+/* --- Stack stuff --- */
+#define _MD_INIT_STACK _PR_MD_INIT_STACK
+#define _MD_CLEAR_STACK(stack)
+
+/*
+** Watcom needs to see this to make the linker work.
+**
+*/
+PR_EXTERN(void) _PR_NativeDestroyThread(PRThread *thread);
+PR_EXTERN(void) _PR_UserDestroyThread(PRThread *thread);
+
+
+/*
+ * _PR_ShutdownLinker unloads all dlls loaded by the application via
+ * calls to PR_LoadLibrary
+ */
+void _PR_ShutdownLinker(void);
+
+
+/*
+** If thread emulation is used, then setjmp/longjmp stores the register
+** state of each thread.
+**
+** CatchBuf layout:
+** context[0] - IP
+** context[1] - CS
+** context[2] - SP
+** context[3] - BP
+** context[4] - SI
+** context[5] - DI
+** context[6] - DS
+** context[7] - ?? (maybe flags)
+** context[8] - SS
+*/
+#define PR_CONTEXT_TYPE CATCHBUF
+#define PR_NUM_GCREGS 9
+
+#define _MD_GET_SP(thread) ((thread)->md.SP)
+#define CONTEXT(_t) ((_t)->md.context)
+
+/*
+** Initialize a thread context to run "e(o,a)" when started
+*/
+#define _MD_INIT_CONTEXT(_t, sp, epa, stat ) \
+{ \
+ *(stat) = PR_TRUE; \
+ Catch((_t)->md.context ); \
+ (_t)->md.context[0] = OFFSETOF(epa); \
+ (_t)->md.context[1] = SELECTOROF(epa); \
+ (_t)->md.context[2] = OFFSETOF(_pr_top_of_task_stack - 64); \
+ (_t)->md.context[3] = 0; \
+}
+
+#define _MD_SWITCH_CONTEXT(_t) \
+ if (!Catch((_t)->md.context)) { \
+ int garbCollPlaceHolder; \
+ (_t)->md.errcode = errno; \
+ (_t)->md.SP = &garbCollPlaceHolder; \
+ _PR_Schedule(); \
+ }
+
+#define _MD_SAVE_CONTEXT(_t) \
+ { \
+ int garbCollPlaceHolder; \
+ Catch((_t)->md.context); \
+ (_t)->md.errcode = errno; \
+ (_t)->md.SP = &garbCollPlaceHolder; \
+ }
+
+/*
+** Restore a thread context, saved by _MD_SWITCH_CONTEXT
+*/
+#define _PR_MD_RESTORE_CONTEXT _MD_RESTORE_CONTEXT
+
+/*
+ * Memory-mapped files
+ */
+
+struct _MDFileMap {
+ PRInt8 unused;
+};
+
+extern PRStatus _MD_CreateFileMap(struct PRFileMap *fmap, PRInt64 size);
+#define _MD_CREATE_FILE_MAP _MD_CreateFileMap
+
+extern void * _MD_MemMap(struct PRFileMap *fmap, PRInt64 offset,
+ PRUint32 len);
+#define _MD_MEM_MAP _MD_MemMap
+
+extern PRStatus _MD_MemUnmap(void *addr, PRUint32 size);
+#define _MD_MEM_UNMAP _MD_MemUnmap
+
+extern PRStatus _MD_CloseFileMap(struct PRFileMap *fmap);
+#define _MD_CLOSE_FILE_MAP _MD_CloseFileMap
+
+
+/* --- Error mapping ----------------------------------- */
+extern void _PR_MD_map_error( int err );
+
+#define _PR_MD_MAP_OPENDIR_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_CLOSEDIR_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_READDIR_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_DELETE_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_STAT_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_FSTAT_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_RENAME_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_ACCESS_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_MKDIR_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_RMDIR_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_READ_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_TRANSMITFILE_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_WRITE_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_LSEEK_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_FSYNC_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_CLOSE_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_SOCKET_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_RECV_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_RECVFROM_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_SEND_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_SENDTO_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_ACCEPT_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_ACCEPTEX_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_CONNECT_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_BIND_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_LISTEN_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_SHUTDOWN_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_GETSOCKNAME_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_GETPEERNAME_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_GETSOCKOPT_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_SETSOCKOPT_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_OPEN_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_GETHOSTNAME_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_SELECT_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_LOCKF_ERROR _PR_MD_map_error
+#define _PR_MD_MAP_WSASTARTUP_ERROR _PR_MD_map_error
+
+#endif /* nspr_win16_defs_h___ */
diff --git a/pr/include/md/_win32_errors.h b/pr/include/md/_win32_errors.h
new file mode 100644
index 00000000..005caee6
--- /dev/null
+++ b/pr/include/md/_win32_errors.h
@@ -0,0 +1,132 @@
+/* -*- 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_win32_errors_h___
+#define nspr_win32_errors_h___
+
+#include <windows.h>
+#include <winsock.h>
+#include <errno.h>
+
+
+extern void _MD_win32_map_opendir_error(PRInt32 err);
+#define _PR_MD_MAP_OPENDIR_ERROR _MD_win32_map_opendir_error
+
+extern void _MD_win32_map_closedir_error(PRInt32 err);
+#define _PR_MD_MAP_CLOSEDIR_ERROR _MD_win32_map_closedir_error
+
+extern void _MD_unix_readdir_error(PRInt32 err);
+#define _PR_MD_MAP_READDIR_ERROR _MD_unix_readdir_error
+
+extern void _MD_win32_map_delete_error(PRInt32 err);
+#define _PR_MD_MAP_DELETE_ERROR _MD_win32_map_delete_error
+
+extern void _MD_win32_map_stat_error(PRInt32 err);
+#define _PR_MD_MAP_STAT_ERROR _MD_win32_map_stat_error
+
+extern void _MD_win32_map_fstat_error(PRInt32 err);
+#define _PR_MD_MAP_FSTAT_ERROR _MD_win32_map_fstat_error
+
+extern void _MD_win32_map_rename_error(PRInt32 err);
+#define _PR_MD_MAP_RENAME_ERROR _MD_win32_map_rename_error
+
+extern void _MD_win32_map_access_error(PRInt32 err);
+#define _PR_MD_MAP_ACCESS_ERROR _MD_win32_map_access_error
+
+extern void _MD_win32_map_mkdir_error(PRInt32 err);
+#define _PR_MD_MAP_MKDIR_ERROR _MD_win32_map_mkdir_error
+
+extern void _MD_win32_map_rmdir_error(PRInt32 err);
+#define _PR_MD_MAP_RMDIR_ERROR _MD_win32_map_rmdir_error
+
+extern void _MD_win32_map_read_error(PRInt32 err);
+#define _PR_MD_MAP_READ_ERROR _MD_win32_map_read_error
+
+extern void _MD_win32_map_transmitfile_error(PRInt32 err);
+#define _PR_MD_MAP_TRANSMITFILE_ERROR _MD_win32_map_transmitfile_error
+
+extern void _MD_win32_map_write_error(PRInt32 err);
+#define _PR_MD_MAP_WRITE_ERROR _MD_win32_map_write_error
+
+extern void _MD_win32_map_lseek_error(PRInt32 err);
+#define _PR_MD_MAP_LSEEK_ERROR _MD_win32_map_lseek_error
+
+extern void _MD_win32_map_fsync_error(PRInt32 err);
+#define _PR_MD_MAP_FSYNC_ERROR _MD_win32_map_fsync_error
+
+extern void _MD_win32_map_close_error(PRInt32 err);
+#define _PR_MD_MAP_CLOSE_ERROR _MD_win32_map_close_error
+
+extern void _MD_win32_map_socket_error(PRInt32 err);
+#define _PR_MD_MAP_SOCKET_ERROR _MD_win32_map_socket_error
+
+extern void _MD_win32_map_recv_error(PRInt32 err);
+#define _PR_MD_MAP_RECV_ERROR _MD_win32_map_recv_error
+
+extern void _MD_win32_map_recvfrom_error(PRInt32 err);
+#define _PR_MD_MAP_RECVFROM_ERROR _MD_win32_map_recvfrom_error
+
+extern void _MD_win32_map_send_error(PRInt32 err);
+#define _PR_MD_MAP_SEND_ERROR _MD_win32_map_send_error
+
+extern void _MD_win32_map_sendto_error(PRInt32 err);
+#define _PR_MD_MAP_SENDTO_ERROR _MD_win32_map_sendto_error
+
+extern void _MD_win32_map_accept_error(PRInt32 err);
+#define _PR_MD_MAP_ACCEPT_ERROR _MD_win32_map_accept_error
+
+extern void _MD_win32_map_acceptex_error(PRInt32 err);
+#define _PR_MD_MAP_ACCEPTEX_ERROR _MD_win32_map_acceptex_error
+
+extern PRInt32 _MD_win32_map_connect_error(PRInt32 err);
+#define _PR_MD_MAP_CONNECT_ERROR _MD_win32_map_connect_error
+
+extern void _MD_win32_map_bind_error(PRInt32 err);
+#define _PR_MD_MAP_BIND_ERROR _MD_win32_map_bind_error
+
+extern void _MD_win32_map_listen_error(PRInt32 err);
+#define _PR_MD_MAP_LISTEN_ERROR _MD_win32_map_listen_error
+
+extern void _MD_win32_map_shutdown_error(PRInt32 err);
+#define _PR_MD_MAP_SHUTDOWN_ERROR _MD_win32_map_shutdown_error
+
+extern void _MD_win32_map_getsockname_error(PRInt32 err);
+#define _PR_MD_MAP_GETSOCKNAME_ERROR _MD_win32_map_getsockname_error
+
+extern void _MD_win32_map_getpeername_error(PRInt32 err);
+#define _PR_MD_MAP_GETPEERNAME_ERROR _MD_win32_map_getpeername_error
+
+extern void _MD_win32_map_getsockopt_error(PRInt32 err);
+#define _PR_MD_MAP_GETSOCKOPT_ERROR _MD_win32_map_getsockopt_error
+
+extern void _MD_win32_map_setsockopt_error(PRInt32 err);
+#define _PR_MD_MAP_SETSOCKOPT_ERROR _MD_win32_map_setsockopt_error
+
+extern void _MD_win32_map_open_error(PRInt32 err);
+#define _PR_MD_MAP_OPEN_ERROR _MD_win32_map_open_error
+
+extern void _MD_win32_map_gethostname_error(PRInt32 err);
+#define _PR_MD_MAP_GETHOSTNAME_ERROR _MD_win32_map_gethostname_error
+
+extern void _MD_win32_map_select_error(PRInt32 err);
+#define _PR_MD_MAP_SELECT_ERROR _MD_win32_map_select_error
+
+extern void _MD_win32_map_lockf_error(int err);
+#define _PR_MD_MAP_LOCKF_ERROR _MD_win32_map_lockf_error
+
+#endif /* nspr_win32_errors_h___ */
diff --git a/pr/include/md/_win95.cfg b/pr/include/md/_win95.cfg
new file mode 100644
index 00000000..dc4696ad
--- /dev/null
+++ b/pr/include/md/_win95.cfg
@@ -0,0 +1,179 @@
+/* -*- 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_PC
+#define XP_PC
+#endif
+
+#ifndef WIN32
+#define WIN32
+#endif
+
+#ifndef WIN95
+#define WIN95
+#endif
+
+#if defined(_M_IX86) || defined(_X86_)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_DOUBLE 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_WORD 32
+#define PR_BITS_PER_DWORD 64
+#define PR_BITS_PER_DOUBLE 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_WORD_LOG2 5
+#define PR_BITS_PER_DWORD_LOG2 6
+#define PR_BITS_PER_DOUBLE_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_WORD 4
+#define PR_ALIGN_OF_DWORD 8
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 2
+
+#elif defined(_ALPHA_)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+
+#else /* defined(_M_IX86) || defined(_X86_) */
+
+#error unknown processor architecture
+
+#endif /* defined(_M_IX86) || defined(_X86_) */
+
+#define HAVE_LONG_LONG
+
+#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/include/md/_win95.h b/pr/include/md/_win95.h
new file mode 100644
index 00000000..a4710b7f
--- /dev/null
+++ b/pr/include/md/_win95.h
@@ -0,0 +1,437 @@
+/* -*- 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_win95_defs_h___
+#define nspr_win95_defs_h___
+
+#include "prio.h"
+
+#include <windows.h>
+#include <winsock.h>
+#include <errno.h>
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "win32"
+#define _PR_SI_SYSNAME "WIN95"
+#define _PR_SI_ARCHITECTURE "x86" /* XXXMB hardcode for now */
+
+#define HAVE_DLL
+#undef HAVE_THREAD_AFFINITY
+#define HAVE_SOCKET_REUSEADDR
+#define HAVE_SOCKET_KEEPALIVE
+#define _PR_HAVE_ATOMIC_OPS
+
+/* --- Common User-Thread/Native-Thread Definitions --------------------- */
+
+/* --- Globals --- */
+extern struct PRLock *_pr_schedLock;
+
+/* --- Typedefs --- */
+typedef void (*FiberFunc)(void *);
+
+#define PR_NUM_GCREGS 8
+typedef PRInt32 PR_CONTEXT_TYPE[PR_NUM_GCREGS];
+#define GC_VMBASE 0x40000000
+#define GC_VMLIMIT 0x00FFFFFF
+
+#define _MD_MAGIC_THREAD 0x22222222
+#define _MD_MAGIC_THREADSTACK 0x33333333
+#define _MD_MAGIC_SEGMENT 0x44444444
+#define _MD_MAGIC_DIR 0x55555555
+#define _MD_MAGIC_CV 0x66666666
+
+struct _MDCPU {
+ int unused;
+};
+
+struct _MDThread {
+ HANDLE blocked_sema; /* Threads block on this when waiting
+ * for IO or CondVar.
+ */
+ PRBool inCVWaitQueue; /* PR_TRUE if the thread is in the
+ * wait queue of some cond var.
+ * PR_FALSE otherwise. */
+ HANDLE handle; /* Win32 thread handle */
+ PRUint32 id;
+ void *sp; /* only valid when suspended */
+ PRUint32 magic; /* for debugging */
+ PR_CONTEXT_TYPE gcContext; /* Thread context for GC */
+ struct PRThread *prev, *next; /* used by the cvar wait queue to
+ * chain the PRThread structures
+ * together */
+};
+
+struct _MDThreadStack {
+ PRUint32 magic; /* for debugging */
+};
+
+struct _MDSegment {
+ PRUint32 magic; /* for debugging */
+};
+
+#undef PROFILE_LOCKS
+
+struct _MDDir {
+ HANDLE d_hdl;
+ WIN32_FIND_DATA d_entry;
+ PRBool firstEntry; /* Is this the entry returned
+ * by FindFirstFile()? */
+ PRUint32 magic; /* for debugging */
+};
+
+struct _MDCVar {
+ PRUint32 magic;
+ struct PRThread *waitHead, *waitTail; /* the wait queue: a doubly-
+ * linked list of threads
+ * waiting on this condition
+ * variable */
+ PRIntn nwait; /* number of threads in the
+ * wait queue */
+};
+
+#define _MD_CV_NOTIFIED_LENGTH 6
+typedef struct _MDNotified _MDNotified;
+struct _MDNotified {
+ PRIntn length; /* # of used entries in this
+ * structure */
+ struct {
+ struct _MDCVar *cv; /* the condition variable notified */
+ PRIntn times; /* and the number of times notified */
+ struct PRThread *notifyHead; /* list of threads to wake up */
+ } cv[_MD_CV_NOTIFIED_LENGTH];
+ _MDNotified *link; /* link to another of these, or NULL */
+};
+
+struct _MDLock {
+ CRITICAL_SECTION mutex; /* this is recursive on NT */
+
+ /*
+ * When notifying cvars, there is no point in actually
+ * waking up the threads waiting on the cvars until we've
+ * released the lock. So, we temporarily record the cvars.
+ * When doing an unlock, we'll then wake up the waiting threads.
+ */
+ struct _MDNotified notified; /* array of conditions notified */
+#ifdef PROFILE_LOCKS
+ PRInt32 hitcount;
+ PRInt32 misscount;
+#endif
+};
+
+struct _MDSemaphore {
+ HANDLE sem;
+};
+
+struct _MDFileDesc {
+ PRInt32 osfd; /* The osfd can come from one of three spaces:
+ * - For stdin, stdout, and stderr, we are using
+ * the libc file handle (0, 1, 2), which is an int.
+ * - For files and pipes, we are using Win32 HANDLE,
+ * which is a void*.
+ * - For sockets, we are using Winsock SOCKET, which
+ * is a u_int.
+ */
+};
+
+struct _MDProcess {
+ HANDLE handle;
+ DWORD id;
+};
+
+/* --- Misc stuff --- */
+#define _MD_GET_SP(thread) (thread)->md.gcContext[6]
+
+/* --- IO stuff --- */
+
+#define _MD_OPEN _PR_MD_OPEN
+#define _MD_READ _PR_MD_READ
+#define _MD_WRITE _PR_MD_WRITE
+#define _MD_WRITEV _PR_MD_WRITEV
+#define _MD_LSEEK _PR_MD_LSEEK
+#define _MD_LSEEK64 _PR_MD_LSEEK64
+extern PRInt32 _MD_CloseFile(PRInt32 osfd);
+#define _MD_CLOSE_FILE _MD_CloseFile
+#define _MD_GETFILEINFO _PR_MD_GETFILEINFO
+#define _MD_GETFILEINFO64 _PR_MD_GETFILEINFO64
+#define _MD_GETOPENFILEINFO _PR_MD_GETOPENFILEINFO
+#define _MD_GETOPENFILEINFO64 _PR_MD_GETOPENFILEINFO64
+#define _MD_STAT _PR_MD_STAT
+#define _MD_RENAME _PR_MD_RENAME
+#define _MD_ACCESS _PR_MD_ACCESS
+#define _MD_DELETE _PR_MD_DELETE
+#define _MD_MKDIR _PR_MD_MKDIR
+#define _MD_RMDIR _PR_MD_RMDIR
+#define _MD_LOCKFILE _PR_MD_LOCKFILE
+#define _MD_TLOCKFILE _PR_MD_TLOCKFILE
+#define _MD_UNLOCKFILE _PR_MD_UNLOCKFILE
+
+/* --- Socket IO stuff --- */
+#define _MD_EACCES WSAEACCES
+#define _MD_EADDRINUSE WSAEADDRINUSE
+#define _MD_EADDRNOTAVAIL WSAEADDRNOTAVAIL
+#define _MD_EAFNOSUPPORT WSAEAFNOSUPPORT
+#define _MD_EAGAIN WSAEWOULDBLOCK
+#define _MD_EALREADY WSAEALREADY
+#define _MD_EBADF WSAEBADF
+#define _MD_ECONNREFUSED WSAECONNREFUSED
+#define _MD_ECONNRESET WSAECONNRESET
+#define _MD_EFAULT WSAEFAULT
+#define _MD_EINPROGRESS WSAEINPROGRESS
+#define _MD_EINTR WSAEINTR
+#define _MD_EINVAL EINVAL
+#define _MD_EISCONN WSAEISCONN
+#define _MD_ENETUNREACH WSAENETUNREACH
+#define _MD_ENOENT ENOENT
+#define _MD_ENOTCONN WSAENOTCONN
+#define _MD_ENOTSOCK WSAENOTSOCK
+#define _MD_EOPNOTSUPP WSAEOPNOTSUPP
+#define _MD_EWOULDBLOCK WSAEWOULDBLOCK
+#define _MD_GET_SOCKET_ERROR() WSAGetLastError()
+#define _MD_SET_SOCKET_ERROR(_err) WSASetLastError(_err)
+
+extern void _MD_MakeNonblock(PRFileDesc *f);
+#define _MD_MAKE_NONBLOCK _MD_MakeNonblock
+#define _MD_SHUTDOWN _PR_MD_SHUTDOWN
+#define _MD_LISTEN(s, backlog) listen(s->secret->md.osfd,backlog)
+extern PRInt32 _MD_CloseSocket(PRInt32 osfd);
+#define _MD_CLOSE_SOCKET _MD_CloseSocket
+#define _MD_SENDTO _PR_MD_SENDTO
+#define _MD_RECVFROM _PR_MD_RECVFROM
+#define _MD_SOCKETPAIR(s, type, proto, sv) -1
+#define _MD_GETSOCKNAME _PR_MD_GETSOCKNAME
+#define _MD_GETPEERNAME _PR_MD_GETPEERNAME
+#define _MD_GETSOCKOPT _PR_MD_GETSOCKOPT
+#define _MD_SETSOCKOPT _PR_MD_SETSOCKOPT
+#define _MD_SELECT select
+#define _MD_FSYNC _PR_MD_FSYNC
+#define READ_FD 1
+#define WRITE_FD 2
+
+#define _MD_INIT_ATOMIC()
+#if defined(_M_IX86) || defined(_X86_)
+#define _MD_ATOMIC_INCREMENT _PR_MD_ATOMIC_INCREMENT
+#define _MD_ATOMIC_ADD _PR_MD_ATOMIC_ADD
+#define _MD_ATOMIC_DECREMENT _PR_MD_ATOMIC_DECREMENT
+#else /* non-x86 processors */
+#define _MD_ATOMIC_INCREMENT(x) InterlockedIncrement((PLONG)x)
+#define _MD_ATOMIC_ADD(ptr,val) (InterlockedExchangeAdd((PLONG)ptr, (LONG)val) + val)
+#define _MD_ATOMIC_DECREMENT(x) InterlockedDecrement((PLONG)x)
+#endif /* x86 */
+#define _MD_ATOMIC_SET(x,y) InterlockedExchange((PLONG)x, (LONG)y)
+
+#define _MD_INIT_IO _PR_MD_INIT_IO
+#define _MD_TRANSMITFILE _PR_MD_TRANSMITFILE
+
+
+/* win95 doesn't have async IO */
+#define _MD_SOCKET _PR_MD_SOCKET
+extern PRInt32 _MD_SocketAvailable(PRFileDesc *fd);
+#define _MD_SOCKETAVAILABLE _MD_SocketAvailable
+#define _MD_CONNECT _PR_MD_CONNECT
+extern PRInt32 _MD_Accept(PRFileDesc *fd, PRNetAddr *raddr, PRUint32 *rlen,
+ PRIntervalTime timeout);
+#define _MD_ACCEPT _MD_Accept
+#define _MD_BIND _PR_MD_BIND
+#define _MD_RECV _PR_MD_RECV
+#define _MD_SEND _PR_MD_SEND
+#define _MD_PR_POLL _PR_MD_PR_POLL
+
+/* --- Scheduler stuff --- */
+// #define _MD_PAUSE_CPU _PR_MD_PAUSE_CPU
+#define _MD_PAUSE_CPU
+
+/* --- DIR stuff --- */
+#define PR_DIRECTORY_SEPARATOR '\\'
+#define PR_DIRECTORY_SEPARATOR_STR "\\"
+#define PR_PATH_SEPARATOR ';'
+#define PR_PATH_SEPARATOR_STR ";"
+#define _MD_ERRNO() GetLastError()
+#define _MD_OPEN_DIR _PR_MD_OPEN_DIR
+#define _MD_CLOSE_DIR _PR_MD_CLOSE_DIR
+#define _MD_READ_DIR _PR_MD_READ_DIR
+
+/* --- Segment stuff --- */
+#define _MD_INIT_SEGS()
+#define _MD_ALLOC_SEGMENT(seg, size, vaddr) 0
+#define _MD_FREE_SEGMENT(seg)
+
+/* --- Environment Stuff --- */
+#define _MD_GET_ENV _PR_MD_GET_ENV
+#define _MD_PUT_ENV _PR_MD_PUT_ENV
+
+/* --- Threading Stuff --- */
+#define _MD_DEFAULT_STACK_SIZE 0
+#define _MD_INIT_THREAD _PR_MD_INIT_THREAD
+#define _MD_INIT_ATTACHED_THREAD _PR_MD_INIT_THREAD
+#define _MD_INIT_PRIMORDIAL_THREAD _PR_MD_INIT_PRIMORDIAL_THREAD
+#define _MD_CREATE_THREAD _PR_MD_CREATE_THREAD
+#define _MD_YIELD _PR_MD_YIELD
+#define _MD_SET_PRIORITY _PR_MD_SET_PRIORITY
+#define _MD_CLEAN_THREAD _PR_MD_CLEAN_THREAD
+#define _MD_SETTHREADAFFINITYMASK _PR_MD_SETTHREADAFFINITYMASK
+#define _MD_GETTHREADAFFINITYMASK _PR_MD_GETTHREADAFFINITYMASK
+#define _MD_EXIT_THREAD _PR_MD_EXIT_THREAD
+#define _MD_SUSPEND_THREAD _PR_MD_SUSPEND_THREAD
+#define _MD_RESUME_THREAD _PR_MD_RESUME_THREAD
+#define _MD_SUSPEND_CPU _PR_MD_SUSPEND_CPU
+#define _MD_RESUME_CPU _PR_MD_RESUME_CPU
+#define _MD_BEGIN_SUSPEND_ALL()
+#define _MD_BEGIN_RESUME_ALL()
+#define _MD_END_SUSPEND_ALL()
+#define _MD_END_RESUME_ALL()
+
+/* --- Lock stuff --- */
+#define _PR_LOCK _MD_LOCK
+#define _PR_UNLOCK _MD_UNLOCK
+
+#define _MD_NEW_LOCK(lock) (InitializeCriticalSection(&((lock)->mutex)),(lock)->notified.length=0,(lock)->notified.link=NULL,PR_SUCCESS)
+#define _MD_FREE_LOCK(lock) DeleteCriticalSection(&((lock)->mutex))
+#define _MD_LOCK(lock) EnterCriticalSection(&((lock)->mutex))
+#define _MD_TEST_AND_LOCK(lock) (EnterCriticalSection(&((lock)->mutex)),PR_SUCCESS)
+#define _MD_UNLOCK _PR_MD_UNLOCK
+
+/* --- lock and cv waiting --- */
+#define _MD_WAIT _PR_MD_WAIT
+#define _MD_WAKEUP_WAITER _PR_MD_WAKEUP_WAITER
+
+/* --- CVar ------------------- */
+#define _MD_WAIT_CV _PR_MD_WAIT_CV
+#define _MD_NEW_CV _PR_MD_NEW_CV
+#define _MD_FREE_CV _PR_MD_FREE_CV
+#define _MD_NOTIFY_CV _PR_MD_NOTIFY_CV
+#define _MD_NOTIFYALL_CV _PR_MD_NOTIFYALL_CV
+
+ /* XXXMB- the IOQ stuff is certainly not working correctly yet. */
+// extern struct _MDLock _pr_ioq_lock;
+#define _MD_IOQ_LOCK()
+#define _MD_IOQ_UNLOCK()
+
+
+/* --- Initialization stuff --- */
+#define _MD_START_INTERRUPTS()
+#define _MD_STOP_INTERRUPTS()
+#define _MD_DISABLE_CLOCK_INTERRUPTS()
+#define _MD_ENABLE_CLOCK_INTERRUPTS()
+#define _MD_BLOCK_CLOCK_INTERRUPTS()
+#define _MD_UNBLOCK_CLOCK_INTERRUPTS()
+#define _MD_EARLY_INIT _PR_MD_EARLY_INIT
+#define _MD_FINAL_INIT()
+#define _MD_INIT_CPUS()
+#define _MD_INIT_RUNNING_CPU(cpu)
+
+struct PRProcess;
+struct PRProcessAttr;
+
+#define _MD_CREATE_PROCESS _PR_CreateWindowsProcess
+extern struct PRProcess * _PR_CreateWindowsProcess(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const struct PRProcessAttr *attr
+);
+
+#define _MD_DETACH_PROCESS _PR_DetachWindowsProcess
+extern PRStatus _PR_DetachWindowsProcess(struct PRProcess *process);
+
+/* --- Wait for a child process to terminate --- */
+#define _MD_WAIT_PROCESS _PR_WaitWindowsProcess
+extern PRStatus _PR_WaitWindowsProcess(struct PRProcess *process,
+ PRInt32 *exitCode);
+
+#define _MD_KILL_PROCESS _PR_KillWindowsProcess
+extern PRStatus _PR_KillWindowsProcess(struct PRProcess *process);
+
+#define _MD_CLEANUP_BEFORE_EXIT _PR_MD_CLEANUP_BEFORE_EXIT
+#define _MD_INIT_CONTEXT
+#define _MD_SWITCH_CONTEXT
+#define _MD_RESTORE_CONTEXT
+
+/* --- Intervals --- */
+#define _MD_INTERVAL_INIT _PR_MD_INTERVAL_INIT
+#define _MD_GET_INTERVAL _PR_MD_GET_INTERVAL
+#define _MD_INTERVAL_PER_SEC _PR_MD_INTERVAL_PER_SEC
+#define _MD_INTERVAL_PER_MILLISEC() (_PR_MD_INTERVAL_PER_SEC() / 1000)
+#define _MD_INTERVAL_PER_MICROSEC() (_PR_MD_INTERVAL_PER_SEC() / 1000000)
+
+/* --- Native-Thread Specific Definitions ------------------------------- */
+
+extern struct PRThread * _MD_CURRENT_THREAD(void);
+
+#ifdef _PR_USE_STATIC_TLS
+extern __declspec(thread) struct PRThread *_pr_currentThread;
+#define _MD_GET_ATTACHED_THREAD() _pr_currentThread
+#define _MD_SET_CURRENT_THREAD(_thread) (_pr_currentThread = (_thread))
+
+extern __declspec(thread) struct PRThread *_pr_thread_last_run;
+#define _MD_LAST_THREAD() _pr_thread_last_run
+#define _MD_SET_LAST_THREAD(_thread) (_pr_thread_last_run = 0)
+
+extern __declspec(thread) struct _PRCPU *_pr_currentCPU;
+#define _MD_CURRENT_CPU() _pr_currentCPU
+#define _MD_SET_CURRENT_CPU(_cpu) (_pr_currentCPU = 0)
+#else /* _PR_USE_STATIC_TLS */
+extern DWORD _pr_currentThreadIndex;
+#define _MD_GET_ATTACHED_THREAD() ((PRThread *) TlsGetValue(_pr_currentThreadIndex))
+#define _MD_SET_CURRENT_THREAD(_thread) TlsSetValue(_pr_currentThreadIndex, (_thread))
+
+extern DWORD _pr_lastThreadIndex;
+#define _MD_LAST_THREAD() ((PRThread *) TlsGetValue(_pr_lastThreadIndex))
+#define _MD_SET_LAST_THREAD(_thread) TlsSetValue(_pr_lastThreadIndex, 0)
+
+extern DWORD _pr_currentCPUIndex;
+#define _MD_CURRENT_CPU() ((struct _PRCPU *) TlsGetValue(_pr_currentCPUIndex))
+#define _MD_SET_CURRENT_CPU(_cpu) TlsSetValue(_pr_currentCPUIndex, 0)
+#endif /* _PR_USE_STATIC_TLS */
+
+/* --- Scheduler stuff --- */
+#define LOCK_SCHEDULER() 0
+#define UNLOCK_SCHEDULER() 0
+#define _PR_LockSched() 0
+#define _PR_UnlockSched() 0
+
+/* --- Initialization stuff --- */
+#define _MD_INIT_LOCKS()
+
+/* --- Stack stuff --- */
+#define _MD_INIT_STACK(stack, redzone)
+#define _MD_CLEAR_STACK(stack)
+
+/* --- Memory-mapped files stuff --- */
+
+struct _MDFileMap {
+ HANDLE hFileMap;
+ DWORD dwAccess;
+};
+
+extern PRStatus _MD_CreateFileMap(struct PRFileMap *fmap, PRInt64 size);
+#define _MD_CREATE_FILE_MAP _MD_CreateFileMap
+
+extern void * _MD_MemMap(struct PRFileMap *fmap, PRInt64 offset,
+ PRUint32 len);
+#define _MD_MEM_MAP _MD_MemMap
+
+extern PRStatus _MD_MemUnmap(void *addr, PRUint32 size);
+#define _MD_MEM_UNMAP _MD_MemUnmap
+
+extern PRStatus _MD_CloseFileMap(struct PRFileMap *fmap);
+#define _MD_CLOSE_FILE_MAP _MD_CloseFileMap
+
+#endif /* nspr_win32_defs_h___ */
diff --git a/pr/include/md/_winnt.cfg b/pr/include/md/_winnt.cfg
new file mode 100644
index 00000000..e8cbe8a8
--- /dev/null
+++ b/pr/include/md/_winnt.cfg
@@ -0,0 +1,179 @@
+/* -*- 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_PC
+#define XP_PC
+#endif
+
+#ifndef WIN32
+#define WIN32
+#endif
+
+#ifndef WINNT
+#define WINNT
+#endif
+
+#if defined(_M_IX86) || defined(_X86_)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_DOUBLE 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_WORD 32
+#define PR_BITS_PER_DWORD 64
+#define PR_BITS_PER_DOUBLE 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_WORD_LOG2 5
+#define PR_BITS_PER_DWORD_LOG2 6
+#define PR_BITS_PER_DOUBLE_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_WORD 4
+#define PR_ALIGN_OF_DWORD 8
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 2
+
+#elif defined(_ALPHA_)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+
+#else /* defined(_M_IX86) || defined(_X86_) */
+
+#error unknown processor architecture
+
+#endif /* defined(_M_IX86) || defined(_X86_) */
+
+#define HAVE_LONG_LONG
+
+#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/include/md/_winnt.h b/pr/include/md/_winnt.h
new file mode 100644
index 00000000..9de2cf05
--- /dev/null
+++ b/pr/include/md/_winnt.h
@@ -0,0 +1,504 @@
+/* -*- 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_win32_defs_h___
+#define nspr_win32_defs_h___
+
+/* Need to force service-pack 3 extensions to be defined by
+** setting _WIN32_WINNT to NT 4.0 for winsock.h, winbase.h, winnt.h.
+*/
+#ifndef _WIN32_WINNT
+ #define _WIN32_WINNT 0x0400
+#elif (_WIN32_WINNT < 0x0400)
+ #undef _WIN32_WINNT
+ #define _WIN32_WINNT 0x0400
+#endif /* _WIN32_WINNT */
+
+#include <windows.h>
+#include <winsock.h>
+#include <errno.h>
+
+#include "prio.h"
+#include "prclist.h"
+
+/*
+ * Internal configuration macros
+ */
+
+#define PR_LINKER_ARCH "win32"
+#define _PR_SI_SYSNAME "WINNT"
+#define _PR_SI_ARCHITECTURE "x86" /* XXXMB hardcode for now */
+
+#define HAVE_DLL
+#define HAVE_CUSTOM_USER_THREADS
+#define HAVE_THREAD_AFFINITY
+#define HAVE_SOCKET_REUSEADDR
+#define HAVE_SOCKET_KEEPALIVE
+#define _PR_HAVE_ATOMIC_OPS
+#define _PR_HAVE_ATOMIC_CAS
+
+/* --- Common User-Thread/Native-Thread Definitions --------------------- */
+
+/* --- Globals --- */
+extern struct PRLock *_pr_schedLock;
+
+/* --- Typedefs --- */
+typedef void (*FiberFunc)(void *);
+
+#define PR_NUM_GCREGS 8
+typedef PRInt32 PR_CONTEXT_TYPE[PR_NUM_GCREGS];
+#define GC_VMBASE 0x40000000
+#define GC_VMLIMIT 0x00FFFFFF
+
+#define _MD_MAGIC_THREAD 0x22222222
+#define _MD_MAGIC_THREADSTACK 0x33333333
+#define _MD_MAGIC_SEGMENT 0x44444444
+#define _MD_MAGIC_DIR 0x55555555
+
+struct _MDCPU {
+ int unused;
+};
+
+enum _MDIOModel {
+ _MD_BlockingIO = 0x38,
+ _MD_MultiWaitIO = 0x49
+};
+
+typedef struct _MDOverlapped {
+ OVERLAPPED overlapped; /* Used for async I/O */
+
+ enum _MDIOModel ioModel; /* The I/O model to implement
+ * using overlapped I/O.
+ */
+
+ union {
+ struct _MDThread *mdThread; /* For blocking I/O, this structure
+ * is embedded in the _MDThread
+ * structure.
+ */
+ struct {
+ PRCList links; /* for group->io_ready list */
+ struct PRRecvWait *desc; /* For multiwait I/O, this structure
+ * is associated with a PRRecvWait
+ * structure.
+ */
+ struct PRWaitGroup *group;
+ struct TimerEvent *timer;
+ DWORD error;
+ } mw;
+ } data;
+} _MDOverlapped;
+
+struct _MDThread {
+ /* The overlapped structure must be first! */
+ struct _MDOverlapped overlapped; /* Used for async IO for this thread */
+ void *acceptex_buf; /* Used for AcceptEx() */
+ TRANSMIT_FILE_BUFFERS *xmit_bufs; /* Used for TransmitFile() */
+ HANDLE blocked_sema; /* Threads block on this when waiting
+ * for IO or CondVar.
+ */
+ PRInt32 blocked_io_status; /* Status of the completed IO */
+ PRInt32 blocked_io_bytes; /* Bytes transferred for completed IO */
+ PRInt32 blocked_io_error; /* Save error if status is FALSE */
+ HANDLE handle;
+ PRUint32 id;
+ void *sp; /* only valid when suspended */
+ PRUint32 magic; /* for debugging */
+ PR_CONTEXT_TYPE gcContext; /* Thread context for GC */
+
+ /* The following are used only if this is a fiber */
+ void *fiber_id; /* flag whether or not this is a fiber*/
+ FiberFunc fiber_fn; /* main fiber routine */
+ void *fiber_arg; /* arg to main fiber routine */
+ PRUint32 fiber_stacksize; /* stacksize for fiber */
+ PRInt32 fiber_last_error; /* last error for the fiber */
+};
+
+struct _MDThreadStack {
+ PRUint32 magic; /* for debugging */
+};
+
+struct _MDSegment {
+ PRUint32 magic; /* for debugging */
+};
+
+#undef PROFILE_LOCKS
+
+struct _MDLock {
+ CRITICAL_SECTION mutex; /* this is recursive on NT */
+#ifdef PROFILE_LOCKS
+ PRInt32 hitcount;
+ PRInt32 misscount;
+#endif
+};
+
+struct _MDDir {
+ HANDLE d_hdl;
+ WIN32_FIND_DATA d_entry;
+ PRBool firstEntry; /* Is this the entry returned
+ * by FindFirstFile()? */
+ PRUint32 magic; /* for debugging */
+};
+
+struct _MDCVar {
+ PRUint32 unused;
+};
+
+struct _MDSemaphore {
+ HANDLE sem;
+};
+
+struct _MDFileDesc {
+ PRInt32 osfd; /* The osfd can come from one of three spaces:
+ * - For stdin, stdout, and stderr, we are using
+ * the libc file handle (0, 1, 2), which is an int.
+ * - For files and pipes, we are using Win32 HANDLE,
+ * which is a void*.
+ * - For sockets, we are using Winsock SOCKET, which
+ * is a u_int.
+ */
+ PRBool io_model_committed; /* The io model (blocking or nonblocking)
+ * for this osfd has been committed and
+ * cannot be changed. The osfd has been
+ * either associated with the io
+ * completion port or made nonblocking.
+ * This is only used for sockets, not
+ * for files. */
+ PRBool nonoverlapped;
+ PRBool accepted_socket; /* Is this an accepted socket (on the
+ * server side)? */
+ PRNetAddr peer_addr; /* If this is an accepted socket, cache
+ * the peer's address returned by
+ * AcceptEx(). This is to work around
+ * the bug that getpeername() on an
+ * socket accepted by AcceptEx() returns
+ * an all-zero net address. */
+};
+
+struct _MDProcess {
+ HANDLE handle;
+ DWORD id;
+};
+
+/* --- Misc stuff --- */
+#define _MD_GET_SP(thread) (thread)->md.gcContext[6]
+
+/* --- IO stuff --- */
+
+extern PRInt32 _md_Associate(HANDLE);
+extern PRInt32 _PR_MD_CLOSE(PRInt32 osfd, PRBool socket);
+
+#define _MD_OPEN _PR_MD_OPEN
+#define _MD_READ _PR_MD_READ
+#define _MD_WRITE _PR_MD_WRITE
+#define _MD_WRITEV _PR_MD_WRITEV
+#define _MD_LSEEK _PR_MD_LSEEK
+#define _MD_LSEEK64 _PR_MD_LSEEK64
+#define _MD_CLOSE_FILE(f) _PR_MD_CLOSE(f, PR_FALSE)
+#define _MD_GETFILEINFO _PR_MD_GETFILEINFO
+#define _MD_GETFILEINFO64 _PR_MD_GETFILEINFO64
+#define _MD_GETOPENFILEINFO _PR_MD_GETOPENFILEINFO
+#define _MD_GETOPENFILEINFO64 _PR_MD_GETOPENFILEINFO64
+#define _MD_STAT _PR_MD_STAT
+#define _MD_RENAME _PR_MD_RENAME
+#define _MD_ACCESS _PR_MD_ACCESS
+#define _MD_DELETE _PR_MD_DELETE
+#define _MD_MKDIR _PR_MD_MKDIR
+#define _MD_RMDIR _PR_MD_RMDIR
+#define _MD_LOCKFILE _PR_MD_LOCKFILE
+#define _MD_TLOCKFILE _PR_MD_TLOCKFILE
+#define _MD_UNLOCKFILE _PR_MD_UNLOCKFILE
+
+/* --- Socket IO stuff --- */
+#define _MD_GET_SOCKET_ERROR() WSAGetLastError()
+#define _MD_SET_SOCKET_ERROR(_err) WSASetLastError(_err)
+
+#define _MD_MAKE_NONBLOCK _PR_MD_MAKE_NONBLOCK
+#define _MD_SHUTDOWN _PR_MD_SHUTDOWN
+#define _MD_LISTEN _PR_MD_LISTEN
+#define _MD_CLOSE_SOCKET(s) _PR_MD_CLOSE(s, PR_TRUE)
+#define _MD_SENDTO _PR_MD_SENDTO
+#define _MD_RECVFROM _PR_MD_RECVFROM
+#define _MD_SOCKETPAIR(s, type, proto, sv) -1
+#define _MD_GETSOCKNAME _PR_MD_GETSOCKNAME
+#define _MD_GETPEERNAME _PR_MD_GETPEERNAME
+#define _MD_GETSOCKOPT _PR_MD_GETSOCKOPT
+#define _MD_SETSOCKOPT _PR_MD_SETSOCKOPT
+#define _MD_SELECT select
+#define _MD_FSYNC _PR_MD_FSYNC
+#define _MD_SOCKETAVAILABLE _PR_MD_SOCKETAVAILABLE
+
+#define _MD_INIT_ATOMIC()
+#if defined(_M_IX86) || defined(_X86_)
+#define _MD_ATOMIC_INCREMENT _PR_MD_ATOMIC_INCREMENT
+#define _MD_ATOMIC_ADD _PR_MD_ATOMIC_ADD
+#define _MD_ATOMIC_DECREMENT _PR_MD_ATOMIC_DECREMENT
+#else /* non-x86 processors */
+#define _MD_ATOMIC_INCREMENT(x) InterlockedIncrement((PLONG)x)
+#define _MD_ATOMIC_ADD(ptr,val) (InterlockedExchangeAdd((PLONG)ptr, (LONG)val) + val)
+#define _MD_ATOMIC_DECREMENT(x) InterlockedDecrement((PLONG)x)
+#endif /* x86 */
+#define _MD_ATOMIC_SET(x,y) InterlockedExchange((PLONG)x, (LONG)y)
+
+#define _MD_INIT_IO _PR_MD_INIT_IO
+#define _MD_SOCKET _PR_MD_SOCKET
+#define _MD_CONNECT _PR_MD_CONNECT
+
+#define _MD_ACCEPT(s, a, l, to) \
+ _MD_FAST_ACCEPT(s, a, l, to, PR_FALSE, NULL, NULL)
+#define _MD_FAST_ACCEPT(s, a, l, to, fast, cb, cba) \
+ _PR_MD_FAST_ACCEPT(s, a, l, to, fast, cb, cba)
+#define _MD_ACCEPT_READ(s, ns, ra, buf, l, t) \
+ _MD_FAST_ACCEPT_READ(s, ns, ra, buf, l, t, PR_FALSE, NULL, NULL)
+#define _MD_FAST_ACCEPT_READ(s, ns, ra, buf, l, t, fast, cb, cba) \
+ _PR_MD_FAST_ACCEPT_READ(s, ns, ra, buf, l, t, fast, cb, cba)
+#define _MD_UPDATE_ACCEPT_CONTEXT _PR_MD_UPDATE_ACCEPT_CONTEXT
+
+#define _MD_BIND _PR_MD_BIND
+#define _MD_RECV _PR_MD_RECV
+#define _MD_SEND _PR_MD_SEND
+#define _MD_TRANSMITFILE _PR_MD_TRANSMITFILE
+#define _MD_PR_POLL _PR_MD_PR_POLL
+
+/* --- Scheduler stuff --- */
+#define _MD_PAUSE_CPU _PR_MD_PAUSE_CPU
+
+/* --- DIR stuff --- */
+#define PR_DIRECTORY_SEPARATOR '\\'
+#define PR_DIRECTORY_SEPARATOR_STR "\\"
+#define PR_PATH_SEPARATOR ';'
+#define PR_PATH_SEPARATOR_STR ";"
+#define _MD_ERRNO() GetLastError()
+#define _MD_OPEN_DIR _PR_MD_OPEN_DIR
+#define _MD_CLOSE_DIR _PR_MD_CLOSE_DIR
+#define _MD_READ_DIR _PR_MD_READ_DIR
+
+/* --- Segment stuff --- */
+#define _MD_INIT_SEGS()
+#define _MD_ALLOC_SEGMENT(seg, size, vaddr) 0
+#define _MD_FREE_SEGMENT(seg)
+
+/* --- Environment Stuff --- */
+#define _MD_GET_ENV _PR_MD_GET_ENV
+#define _MD_PUT_ENV _PR_MD_PUT_ENV
+
+/* --- Threading Stuff --- */
+#define _MD_DEFAULT_STACK_SIZE 0
+#define _MD_INIT_THREAD _PR_MD_INIT_THREAD
+#define _MD_INIT_ATTACHED_THREAD _PR_MD_INIT_THREAD
+#define _MD_INIT_PRIMORDIAL_THREAD _PR_MD_INIT_PRIMORDIAL_THREAD
+#define _MD_CREATE_THREAD _PR_MD_CREATE_THREAD
+#define _MD_YIELD _PR_MD_YIELD
+#define _MD_SET_PRIORITY _PR_MD_SET_PRIORITY
+#define _MD_CLEAN_THREAD _PR_MD_CLEAN_THREAD
+#define _MD_SETTHREADAFFINITYMASK _PR_MD_SETTHREADAFFINITYMASK
+#define _MD_GETTHREADAFFINITYMASK _PR_MD_GETTHREADAFFINITYMASK
+#define _MD_EXIT_THREAD _PR_MD_EXIT_THREAD
+#define _MD_SUSPEND_THREAD _PR_MD_SUSPEND_THREAD
+#define _MD_RESUME_THREAD _PR_MD_RESUME_THREAD
+#define _MD_SUSPEND_CPU _PR_MD_SUSPEND_CPU
+#define _MD_RESUME_CPU _PR_MD_RESUME_CPU
+#define _MD_BEGIN_SUSPEND_ALL()
+#define _MD_BEGIN_RESUME_ALL()
+#define _MD_END_SUSPEND_ALL()
+#define _MD_END_RESUME_ALL()
+
+/* --- Lock stuff --- */
+#define _MD_NEW_LOCK(lock) (InitializeCriticalSection(&((lock)->mutex)),PR_SUCCESS)
+#define _MD_FREE_LOCK(lock) DeleteCriticalSection(&((lock)->mutex))
+#ifndef PROFILE_LOCKS
+#define _MD_LOCK(lock) EnterCriticalSection(&((lock)->mutex))
+#define _MD_TEST_AND_LOCK(lock) 0 /* XXXMB */
+#define _MD_UNLOCK(lock) LeaveCriticalSection(&((lock)->mutex))
+#else
+#define _MD_LOCK(lock) \
+ PR_BEGIN_MACRO \
+ BOOL rv = TryEnterCriticalSection(&((lock)->mutex)); \
+ if (rv == TRUE) { \
+ InterlockedIncrement(&((lock)->hitcount)); \
+ } else { \
+ InterlockedIncrement(&((lock)->misscount)); \
+ EnterCriticalSection(&((lock)->mutex)); \
+ } \
+ PR_END_MACRO
+#define _MD_TEST_AND_LOCK(lock) 0 /* XXXMB */
+#define _MD_UNLOCK(lock) LeaveCriticalSection(&((lock)->mutex))
+#endif
+#define _PR_LOCK _MD_LOCK
+#define _PR_UNLOCK _MD_UNLOCK
+
+/* --- lock and cv waiting --- */
+#define _MD_WAIT _PR_MD_WAIT
+#define _MD_WAKEUP_WAITER _PR_MD_WAKEUP_WAITER
+
+ /* XXXMB- the IOQ stuff is certainly not working correctly yet. */
+extern struct _MDLock _pr_ioq_lock;
+#define _MD_IOQ_LOCK() _MD_LOCK(&_pr_ioq_lock)
+#define _MD_IOQ_UNLOCK() _MD_UNLOCK(&_pr_ioq_lock)
+
+
+/* --- Initialization stuff --- */
+#define _MD_START_INTERRUPTS()
+#define _MD_STOP_INTERRUPTS()
+#define _MD_DISABLE_CLOCK_INTERRUPTS()
+#define _MD_ENABLE_CLOCK_INTERRUPTS()
+#define _MD_BLOCK_CLOCK_INTERRUPTS()
+#define _MD_UNBLOCK_CLOCK_INTERRUPTS()
+#define _MD_EARLY_INIT _PR_MD_EARLY_INIT
+#define _MD_FINAL_INIT()
+#define _MD_INIT_CPUS()
+#define _MD_INIT_RUNNING_CPU(cpu)
+
+struct PRProcess;
+struct PRProcessAttr;
+
+/* --- Create a new process --- */
+#define _MD_CREATE_PROCESS _PR_CreateWindowsProcess
+extern struct PRProcess * _PR_CreateWindowsProcess(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const struct PRProcessAttr *attr
+);
+
+#define _MD_DETACH_PROCESS _PR_DetachWindowsProcess
+extern PRStatus _PR_DetachWindowsProcess(struct PRProcess *process);
+
+/* --- Wait for a child process to terminate --- */
+#define _MD_WAIT_PROCESS _PR_WaitWindowsProcess
+extern PRStatus _PR_WaitWindowsProcess(struct PRProcess *process,
+ PRInt32 *exitCode);
+
+#define _MD_KILL_PROCESS _PR_KillWindowsProcess
+extern PRStatus _PR_KillWindowsProcess(struct PRProcess *process);
+
+/* --- User Threading stuff --- */
+#define HAVE_FIBERS
+#define _MD_CREATE_USER_THREAD _PR_MD_CREATE_USER_THREAD
+#define _MD_CREATE_PRIMORDIAL_USER_THREAD _PR_MD_CREATE_PRIMORDIAL_USER_THREAD
+#define _MD_CLEANUP_BEFORE_EXIT _PR_MD_CLEANUP_BEFORE_EXIT
+#define _MD_EXIT _PR_MD_EXIT
+#define _MD_INIT_CONTEXT _PR_MD_INIT_CONTEXT
+#define _MD_SWITCH_CONTEXT _PR_MD_SWITCH_CONTEXT
+#define _MD_RESTORE_CONTEXT _PR_MD_RESTORE_CONTEXT
+
+/* --- Intervals --- */
+#define _MD_INTERVAL_INIT _PR_MD_INTERVAL_INIT
+#define _MD_GET_INTERVAL _PR_MD_GET_INTERVAL
+#define _MD_INTERVAL_PER_SEC _PR_MD_INTERVAL_PER_SEC
+#define _MD_INTERVAL_PER_MILLISEC() (_PR_MD_INTERVAL_PER_SEC() / 1000)
+#define _MD_INTERVAL_PER_MICROSEC() (_PR_MD_INTERVAL_PER_SEC() / 1000000)
+
+/* --- Native-Thread Specific Definitions ------------------------------- */
+
+extern BOOL _pr_use_static_tls;
+
+extern __declspec(thread) struct PRThread *_pr_current_fiber;
+extern DWORD _pr_currentFiberIndex;
+
+#define _MD_GET_ATTACHED_THREAD() \
+ (_pr_use_static_tls ? _pr_current_fiber \
+ : (PRThread *) TlsGetValue(_pr_currentFiberIndex))
+
+extern struct PRThread * _MD_CURRENT_THREAD(void);
+
+#define _MD_SET_CURRENT_THREAD(_thread) \
+ PR_BEGIN_MACRO \
+ if (_pr_use_static_tls) { \
+ _pr_current_fiber = (_thread); \
+ } else { \
+ TlsSetValue(_pr_currentFiberIndex, (_thread)); \
+ } \
+ PR_END_MACRO
+
+extern __declspec(thread) struct PRThread *_pr_fiber_last_run;
+extern DWORD _pr_lastFiberIndex;
+
+#define _MD_LAST_THREAD() \
+ (_pr_use_static_tls ? _pr_fiber_last_run \
+ : (PRThread *) TlsGetValue(_pr_lastFiberIndex))
+
+#define _MD_SET_LAST_THREAD(_thread) \
+ PR_BEGIN_MACRO \
+ if (_pr_use_static_tls) { \
+ _pr_fiber_last_run = (_thread); \
+ } else { \
+ TlsSetValue(_pr_lastFiberIndex, (_thread)); \
+ } \
+ PR_END_MACRO
+
+extern __declspec(thread) struct _PRCPU *_pr_current_cpu;
+extern DWORD _pr_currentCPUIndex;
+
+#define _MD_CURRENT_CPU() \
+ (_pr_use_static_tls ? _pr_current_cpu \
+ : (struct _PRCPU *) TlsGetValue(_pr_currentCPUIndex))
+
+#define _MD_SET_CURRENT_CPU(_cpu) \
+ PR_BEGIN_MACRO \
+ if (_pr_use_static_tls) { \
+ _pr_current_cpu = (_cpu); \
+ } else { \
+ TlsSetValue(_pr_currentCPUIndex, (_cpu)); \
+ } \
+ PR_END_MACRO
+
+extern __declspec(thread) PRUintn _pr_ints_off;
+extern DWORD _pr_intsOffIndex;
+
+#define _MD_GET_INTSOFF() \
+ (_pr_use_static_tls ? _pr_ints_off \
+ : (PRUintn) TlsGetValue(_pr_intsOffIndex))
+
+#define _MD_SET_INTSOFF(_val) \
+ PR_BEGIN_MACRO \
+ if (_pr_use_static_tls) { \
+ _pr_ints_off = (_val); \
+ } else { \
+ TlsSetValue(_pr_intsOffIndex, (LPVOID) (_val)); \
+ } \
+ PR_END_MACRO
+
+/* --- Initialization stuff --- */
+#define _MD_INIT_LOCKS()
+
+/* --- Stack stuff --- */
+#define _MD_INIT_STACK(stack, redzone)
+#define _MD_CLEAR_STACK(stack)
+
+/* --- Memory-mapped files stuff --- */
+
+struct _MDFileMap {
+ HANDLE hFileMap;
+ DWORD dwAccess;
+};
+
+extern PRStatus _MD_CreateFileMap(struct PRFileMap *fmap, PRInt64 size);
+#define _MD_CREATE_FILE_MAP _MD_CreateFileMap
+
+extern void * _MD_MemMap(struct PRFileMap *fmap, PRInt64 offset,
+ PRUint32 len);
+#define _MD_MEM_MAP _MD_MemMap
+
+extern PRStatus _MD_MemUnmap(void *addr, PRUint32 size);
+#define _MD_MEM_UNMAP _MD_MemUnmap
+
+extern PRStatus _MD_CloseFileMap(struct PRFileMap *fmap);
+#define _MD_CLOSE_FILE_MAP _MD_CloseFileMap
+
+#endif /* nspr_win32_defs_h___ */
diff --git a/pr/include/md/prosdep.h b/pr/include/md/prosdep.h
new file mode 100644
index 00000000..98002558
--- /dev/null
+++ b/pr/include/md/prosdep.h
@@ -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.
+ */
+
+#ifndef prosdep_h___
+#define prosdep_h___
+
+/*
+** Get OS specific header information
+*/
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+#ifdef XP_PC
+
+#include "md/_pcos.h"
+#ifdef WINNT
+#include "md/_winnt.h"
+#include "md/_win32_errors.h"
+#elif defined(WIN95)
+#include "md/_win95.h"
+#include "md/_win32_errors.h"
+#elif defined(WIN16)
+#include "md/_win16.h"
+#elif defined(OS2)
+#include "md/_os2.h"
+#include "md/_os2_errors.h"
+#else
+#error unknown Windows platform
+#endif
+
+#elif defined XP_MAC
+
+#include "_macos.h"
+
+#elif defined(XP_UNIX)
+
+#if defined(AIX)
+#include "md/_aix.h"
+
+#elif defined(FREEBSD)
+#include "md/_freebsd.h"
+
+#elif defined(NETBSD)
+#include "md/_netbsd.h"
+
+#elif defined(OPENBSD)
+#include "md/_openbsd.h"
+
+#elif defined(BSDI)
+#include "md/_bsdi.h"
+
+#elif defined(HPUX)
+#include "md/_hpux.h"
+
+#elif defined(IRIX)
+#include "md/_irix.h"
+
+#elif defined(LINUX)
+#include "md/_linux.h"
+
+#elif defined(OSF1)
+#include "md/_osf1.h"
+
+#elif defined(RHAPSODY)
+#include "md/_rhapsody.h"
+
+#elif defined(SOLARIS)
+#include "md/_solaris.h"
+
+#elif defined(SUNOS4)
+#include "md/_sunos4.h"
+
+#elif defined(SNI)
+#include "md/_reliantunix.h"
+
+#elif defined(SONY)
+#include "md/_sony.h"
+
+#elif defined(NEC)
+#include "md/_nec.h"
+
+#elif defined(SCO)
+#include "md/_scoos.h"
+
+#elif defined(UNIXWARE)
+#include "md/_unixware.h"
+
+#elif defined(NCR)
+#include "md/_ncr.h"
+
+#elif defined(DGUX)
+#include "md/_dgux.h"
+
+#else
+#error unknown Unix flavor
+
+#endif
+
+#include "md/_unixos.h"
+#include "md/_unix_errors.h"
+
+#else
+
+#error "The platform is not Unix, Windows, or Mac"
+
+#endif
+
+#ifdef _PR_PTHREADS
+#include "md/_pth.h"
+#endif
+
+PR_END_EXTERN_C
+
+#endif /* prosdep_h___ */
diff --git a/pr/include/md/sunos4.h b/pr/include/md/sunos4.h
new file mode 100644
index 00000000..da0bc9e6
--- /dev/null
+++ b/pr/include/md/sunos4.h
@@ -0,0 +1,146 @@
+/* -*- 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 pr_sunos4_h___
+#define pr_sunos4_h___
+
+#ifndef SVR4
+
+/*
+** Hodge podge of random missing prototypes for the Sunos4 system
+*/
+#include <stdio.h>
+#include <stdarg.h>
+#include <time.h>
+#include <limits.h>
+#include <sys/types.h>
+
+#define PATH_MAX _POSIX_PATH_MAX
+
+struct timeval;
+struct timezone;
+struct itimerval;
+struct sockaddr;
+struct stat;
+struct tm;
+
+/* ctype.h */
+extern int tolower(int);
+extern int toupper(int);
+
+/* errno.h */
+extern char *sys_errlist[];
+extern int sys_nerr;
+
+#define strerror(e) sys_errlist[((unsigned)(e) < sys_nerr) ? e : 0]
+
+extern void perror(const char *);
+
+/* getopt */
+extern char *optarg;
+extern int optind;
+extern int getopt(int argc, char **argv, char *spec);
+
+/* math.h */
+extern int srandom(long val);
+extern long random(void);
+
+/* memory.h */
+#define memmove(to,from,len) bcopy((char*)(from),(char*)(to),len)
+
+extern void bcopy(const char *, char *, int);
+
+/* signal.h */
+/*
+** SunOS4 sigaction hides interrupts by default, so we can safely define
+** SA_RESTART to 0.
+*/
+#define SA_RESTART 0
+#define SA_SIGINFO 0
+
+/* stdio.h */
+extern int printf(const char *, ...);
+extern int fprintf(FILE *, const char *, ...);
+extern int vprintf(const char *, va_list);
+extern int vfprintf(FILE *, const char *, va_list);
+extern char *vsprintf(char *, const char *, va_list);
+extern int scanf(const char *, ...);
+extern int sscanf(const char *, const char *, ...);
+extern int fscanf(FILE *, const char *, ...);
+extern int fgetc(FILE *);
+extern int fputc(int, FILE *);
+extern int fputs(const char *, FILE *);
+extern int puts(const char *);
+extern int fread(void *, size_t, size_t, FILE *);
+extern int fwrite(const char *, int, int, FILE *);
+extern int fseek(FILE *, long, int);
+extern long ftell(FILE *);
+extern int rewind(FILE *);
+extern int fflush(FILE *);
+extern int _flsbuf(unsigned char, FILE *);
+extern int fclose(FILE *);
+extern int remove(const char *);
+extern int setvbuf(FILE *, char *, int, size_t);
+extern int system(const char *);
+extern FILE *popen(const char *, const char *);
+extern int pclose(FILE *);
+
+/* stdlib.h */
+#define strtoul strtol
+
+extern int isatty(int fildes);
+extern long strtol(const char *, char **, int);
+extern int putenv(const char *);
+extern void srand48(long);
+extern long lrand48(void);
+extern double drand48(void);
+
+/* string.h */
+extern int strcasecmp(const char *, const char *);
+extern int strncasecmp(const char *, const char *, size_t);
+extern int strcoll(const char *, const char *);
+
+/* time.h */
+extern time_t mktime(struct tm *);
+extern size_t strftime(char *, size_t, const char *, const struct tm *);
+extern int gettimeofday(struct timeval *, struct timezone *);
+extern int setitimer(int, struct itimerval *, struct itimerval *);
+extern time_t time(time_t *);
+extern time_t timegm(struct tm *);
+extern struct tm *localtime(const time_t *);
+extern struct tm *gmtime(const time_t *);
+
+/* unistd.h */
+extern int rename(const char *, const char *);
+extern int ioctl(int, int, int *arg);
+extern int connect(int, struct sockaddr *, int);
+extern int readlink(const char *, char *, int);
+extern int symlink(const char *, const char *);
+extern int ftruncate(int, off_t);
+extern int fchmod(int, mode_t);
+extern int fchown(int, uid_t, gid_t);
+extern int lstat(const char *, struct stat *);
+extern int fstat(int, struct stat *);
+extern int select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
+extern int gethostname(char *, int);
+extern char *getwd(char *);
+extern int getpagesize(void);
+
+#endif /* SVR4 */
+
+#endif /* pr_sunos4_h___ */
diff --git a/pr/include/nspr.h b/pr/include/nspr.h
new file mode 100644
index 00000000..a2800df2
--- /dev/null
+++ b/pr/include/nspr.h
@@ -0,0 +1,49 @@
+/* -*- 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_h___
+#define nspr_h___
+
+#include "pratom.h"
+#include "prbit.h"
+#include "prclist.h"
+#include "prcmon.h"
+#include "prcvar.h"
+#include "prdtoa.h"
+#include "prenv.h"
+#include "prerror.h"
+#include "prinet.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prio.h"
+#include "prlink.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prlong.h"
+#include "prmem.h"
+#include "prmon.h"
+#include "prmwait.h"
+#include "prnetdb.h"
+#include "prprf.h"
+#include "prproces.h"
+#include "prsystem.h"
+#include "prthread.h"
+#include "prtime.h"
+#include "prtypes.h"
+
+#endif /* nspr_h___ */
diff --git a/pr/include/obsolete/Makefile b/pr/include/obsolete/Makefile
new file mode 100644
index 00000000..aa05cd0d
--- /dev/null
+++ b/pr/include/obsolete/Makefile
@@ -0,0 +1,35 @@
+#
+# 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
+
+HEADERS = *.h
+
+RELEASE_HEADERS = $(HEADERS)
+RELEASE_HEADERS_DEST = $(RELEASE_INCLUDE_DIR)/obsolete
+
+include $(MOD_DEPTH)/config/rules.mk
+
+export:: $(HEADERS)
+ $(INSTALL) -m 444 $(HEADERS) $(DIST)/include/obsolete
+ifeq ($(MOZ_BITS),16)
+ $(INSTALL) -m 444 $(HEADERS) $(MOZ_INCL)/obsolete
+endif
diff --git a/pr/include/obsolete/pralarm.h b/pr/include/obsolete/pralarm.h
new file mode 100644
index 00000000..422a7b6a
--- /dev/null
+++ b/pr/include/obsolete/pralarm.h
@@ -0,0 +1,175 @@
+/* -*- 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: pralarm.h
+** Description: API to periodic alarms.
+**
+**
+** Alarms are defined to invoke some client specified function at
+** a time in the future. The notification may be a one time event
+** or repeated at a fixed interval. The interval at which the next
+** notification takes place may be modified by the client code only
+** during the respective notification.
+**
+** The notification is delivered on a thread that is part of the
+** alarm context (PRAlarm). The thread will inherit the priority
+** of the Alarm creator.
+**
+** Any number of periodic alarms (PRAlarmID) may be created within
+** the context of a single alarm (PRAlarm). The notifications will be
+** scheduled as close to the desired time as possible.
+**
+** Repeating periodic notifies are expected to run at a fixed rate.
+** That rate is expressed as some number of notifies per period where
+** the period is much larger than a PRIntervalTime (see prinrval.h).
+*/
+
+#if !defined(pralarm_h)
+#define pralarm_h
+
+#include "prtypes.h"
+#include "prinrval.h"
+
+
+PR_BEGIN_EXTERN_C
+
+/**********************************************************************/
+/************************* TYPES AND CONSTANTS ************************/
+/**********************************************************************/
+
+typedef struct PRAlarm PRAlarm;
+typedef struct PRAlarmID PRAlarmID;
+
+typedef PRBool (PR_CALLBACK *PRPeriodicAlarmFn)(
+ PRAlarmID *id, void *clientData, PRUint32 late);
+
+/**********************************************************************/
+/****************************** FUNCTIONS *****************************/
+/**********************************************************************/
+
+/***********************************************************************
+** FUNCTION: PR_CreateAlarm
+** DESCRIPTION:
+** Create an alarm context.
+** INPUTS: void
+** OUTPUTS: None
+** RETURN: PRAlarm*
+**
+** SIDE EFFECTS:
+** This creates an alarm context, which is an object used for subsequent
+** notification creations. It also creates a thread that will be used to
+** deliver the notifications that are expected to be defined. The client
+** is resposible for destroying the context when appropriate.
+** RESTRICTIONS:
+** None.
+** MEMORY: The object (PRAlarm) and a thread to support notifications.
+** ALGORITHM: N/A
+***********************************************************************/
+PR_EXTERN(PRAlarm*) PR_CreateAlarm(void);
+
+/***********************************************************************
+** FUNCTION: PR_DestroyAlarm
+** DESCRIPTION:
+** Destroys the context created by PR_CreateAlarm().
+** INPUTS: PRAlarm*
+** OUTPUTS: None
+** RETURN: PRStatus
+**
+** SIDE EFFECTS:
+** This destroys the context that was created by PR_CreateAlarm().
+** If there are any active alarms (PRAlarmID), they will be cancelled.
+** Once that is done, the thread that was used to deliver the alarms
+** will be joined.
+** RESTRICTIONS:
+** None.
+** MEMORY: N/A
+** ALGORITHM: N/A
+***********************************************************************/
+PR_EXTERN(PRStatus) PR_DestroyAlarm(PRAlarm *alarm);
+
+/***********************************************************************
+** FUNCTION: PR_SetAlarm
+** DESCRIPTION:
+** Creates a periodic notifier that is to be delivered to a specified
+** function at some fixed interval.
+** INPUTS: PRAlarm *alarm Parent alarm context
+** PRIntervalTime period Interval over which the notifies
+** are delivered.
+** PRUint32 rate The rate within the interval that
+** the notifies will be delivered.
+** PRPeriodicAlarmFn function Entry point where the notifies
+** will be delivered.
+** OUTPUTS: None
+** RETURN: PRAlarmID* Handle to the notifier just created
+** or NULL if the request failed.
+**
+** SIDE EFFECTS:
+** A periodic notifier is created. The notifications will be delivered
+** by the alarm's internal thread at a fixed interval whose rate is the
+** number of interrupts per interval specified. The first notification
+** will be delivered as soon as possible, and they will continue until
+** the notifier routine indicates that they should cease of the alarm
+** context is destroyed (PR_DestroyAlarm).
+** RESTRICTIONS:
+** None.
+** MEMORY: Memory for the notifier object.
+** ALGORITHM: The rate at which notifications are delivered are stated
+** to be "'rate' notifies per 'interval'". The exact time of
+** the notification is computed based on a epoch established
+** when the notifier was set. Each notification is delivered
+** not ealier than the epoch plus the fixed rate times the
+** notification sequence number. Such notifications have the
+** potential to be late by not more than 'interval'/'rate'.
+** The amount of lateness of one notification is taken into
+** account on the next in an attempt to avoid long term slew.
+***********************************************************************/
+PR_EXTERN(PRAlarmID*) PR_SetAlarm(
+ PRAlarm *alarm, PRIntervalTime period, PRUint32 rate,
+ PRPeriodicAlarmFn function, void *clientData);
+
+/***********************************************************************
+** FUNCTION: PR_ResetAlarm
+** DESCRIPTION:
+** Resets an existing alarm.
+** INPUTS: PRAlarmID *id Identify of the notifier.
+** PRIntervalTime period Interval over which the notifies
+** are delivered.
+** PRUint32 rate The rate within the interval that
+** the notifies will be delivered.
+** OUTPUTS: None
+** RETURN: PRStatus Indication of completion.
+**
+** SIDE EFFECTS:
+** An existing alarm may have its period and rate redefined. The
+** additional side effect is that the notifier's epoch is recomputed.
+** The first notification delivered by the newly refreshed alarm is
+** defined to be 'interval'/'rate' from the time of the reset.
+** RESTRICTIONS:
+** This function may only be called in the notifier for that alarm.
+** MEMORY: N/A.
+** ALGORITHM: See PR_SetAlarm().
+***********************************************************************/
+PR_EXTERN(PRStatus) PR_ResetAlarm(
+ PRAlarmID *id, PRIntervalTime period, PRUint32 rate);
+
+PR_END_EXTERN_C
+
+#endif /* !defined(pralarm_h) */
+
+/* prinrval.h */
diff --git a/pr/include/obsolete/probslet.h b/pr/include/obsolete/probslet.h
new file mode 100644
index 00000000..5b53d3bc
--- /dev/null
+++ b/pr/include/obsolete/probslet.h
@@ -0,0 +1,225 @@
+/* -*- 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.
+ */
+
+/*
+** A collection of things thought to be obsolete
+*/
+
+#if defined(PROBSLET_H)
+#else
+#define PROBSLET_H
+
+#include "prio.h"
+
+PR_BEGIN_EXTERN_C
+
+/*
+** Yield the current thread. The proper function to use in place of
+** PR_Yield() is PR_Sleep() with an argument of PR_INTERVAL_NO_WAIT.
+*/
+PR_EXTERN(PRStatus) PR_Yield(void);
+
+/*
+** These are obsolete and are replaced by PR_GetSocketOption() and
+** PR_SetSocketOption().
+*/
+
+PR_EXTERN(PRStatus) PR_GetSockOpt(
+ PRFileDesc *fd, PRSockOption optname, void* optval, PRInt32* optlen);
+
+PR_EXTERN(PRStatus) PR_SetSockOpt(
+ PRFileDesc *fd, PRSockOption optname, const void* optval, PRInt32 optlen);
+
+/************************************************************************/
+/************* The following definitions are for select *****************/
+/************************************************************************/
+
+/*
+** The following is obsolete and will be deleted in the next release!
+** These are provided for compatibility, but are GUARANTEED to be slow.
+**
+** Override PR_MAX_SELECT_DESC if you need more space in the select set.
+*/
+#ifndef PR_MAX_SELECT_DESC
+#define PR_MAX_SELECT_DESC 1024
+#endif
+typedef struct PR_fd_set {
+ PRUint32 hsize;
+ PRFileDesc *harray[PR_MAX_SELECT_DESC];
+ PRUint32 nsize;
+ PRInt32 narray[PR_MAX_SELECT_DESC];
+} PR_fd_set;
+
+/*
+*************************************************************************
+** FUNCTION: PR_Select
+** DESCRIPTION:
+**
+** The call returns as soon as I/O is ready on one or more of the underlying
+** file/socket descriptors or an exceptional condition is pending. A count of the
+** number of ready descriptors is returned unless a timeout occurs in which case
+** zero is returned. On return, PR_Select replaces the given descriptor sets with
+** subsets consisting of those descriptors that are ready for the requested condition.
+** The total number of ready descriptors in all the sets is the return value.
+**
+** INPUTS:
+** PRInt32 num
+** This argument is unused but is provided for select(unix) interface
+** compatability. All input PR_fd_set arguments are self-describing
+** with its own maximum number of elements in the set.
+**
+** PR_fd_set *readfds
+** A set describing the io descriptors for which ready for reading
+** condition is of interest.
+**
+** PR_fd_set *writefds
+** A set describing the io descriptors for which ready for writing
+** condition is of interest.
+**
+** PR_fd_set *exceptfds
+** A set describing the io descriptors for which exception pending
+** condition is of interest.
+**
+** Any of the above readfds, writefds or exceptfds may be given as NULL
+** pointers if no descriptors are of interest for that particular condition.
+**
+** PRIntervalTime timeout
+** Amount of time the call will block waiting for I/O to become ready.
+** If this time expires without any I/O becoming ready, the result will
+** be zero.
+**
+** OUTPUTS:
+** PR_fd_set *readfds
+** A set describing the io descriptors which are ready for reading.
+**
+** PR_fd_set *writefds
+** A set describing the io descriptors which are ready for writing.
+**
+** PR_fd_set *exceptfds
+** A set describing the io descriptors which have pending exception.
+**
+** RETURN:PRInt32
+** Number of io descriptors with asked for conditions or zero if the function
+** timed out or -1 on failure. The reason for the failure is obtained by
+** calling PR_GetError().
+** XXX can we implement this on windoze and mac?
+**************************************************************************
+*/
+PR_EXTERN(PRInt32) PR_Select(
+ PRInt32 num, PR_fd_set *readfds, PR_fd_set *writefds,
+ PR_fd_set *exceptfds, PRIntervalTime timeout);
+
+/*
+** The following are not thread safe for two threads operating on them at the
+** same time.
+**
+** The following routines are provided for manipulating io descriptor sets.
+** PR_FD_ZERO(&fdset) initializes a descriptor set fdset to the null set.
+** PR_FD_SET(fd, &fdset) includes a particular file descriptor fd in fdset.
+** PR_FD_CLR(fd, &fdset) removes a file descriptor fd from fdset.
+** PR_FD_ISSET(fd, &fdset) is nonzero if file descriptor fd is a member of
+** fdset, zero otherwise.
+**
+** PR_FD_NSET(osfd, &fdset) includes a particular native file descriptor osfd
+** in fdset.
+** PR_FD_NCLR(osfd, &fdset) removes a native file descriptor osfd from fdset.
+** PR_FD_NISSET(osfd, &fdset) is nonzero if native file descriptor osfd is a member of
+** fdset, zero otherwise.
+*/
+
+PR_EXTERN(void) PR_FD_ZERO(PR_fd_set *set);
+PR_EXTERN(void) PR_FD_SET(PRFileDesc *fd, PR_fd_set *set);
+PR_EXTERN(void) PR_FD_CLR(PRFileDesc *fd, PR_fd_set *set);
+PR_EXTERN(PRInt32) PR_FD_ISSET(PRFileDesc *fd, PR_fd_set *set);
+PR_EXTERN(void) PR_FD_NSET(PRInt32 osfd, PR_fd_set *set);
+PR_EXTERN(void) PR_FD_NCLR(PRInt32 osfd, PR_fd_set *set);
+PR_EXTERN(PRInt32) PR_FD_NISSET(PRInt32 osfd, PR_fd_set *set);
+
+#ifndef NO_NSPR_10_SUPPORT
+#ifdef XP_MAC
+#include <stat.h>
+#else
+#include <sys/stat.h>
+#endif
+
+PR_EXTERN(PRInt32) PR_Stat(const char *path, struct stat *buf);
+#endif /* NO_NSPR_10_SUPPORT */
+
+/***********************************************************************
+** FUNCTION: PR_CreateNetAddr(), PR_DestroyNetAddr()
+** DESCRIPTION:
+** Create an instance of a PRNetAddr, assigning well known values as
+** appropriate.
+**
+** INPUTS
+** PRNetAddrValue val The value to be assigned to the IP Address portion
+** of the network address. This can only specify the
+** special well known values that are equivalent to
+** INADDR_ANY and INADDR_LOOPBACK.
+**
+** PRUInt16 port The port number to be assigned in the structure.
+**
+** OUTPUTS:
+** None
+**
+** RETURN:
+** PRNetAddr *addr The initialized address that has been allocated
+** from the heap. It must be freed by the caller.
+** A returned value of zero indicates an error. The
+** cause of the error (most likely low memory) may
+** be retrieved by calling PR_GetError().
+**
+***********************************************************************/
+PR_EXTERN(PRNetAddr*) PR_CreateNetAddr(PRNetAddrValue val, PRUint16 port);
+PR_EXTERN(PRStatus) PR_DestroyNetAddr(PRNetAddr *addr);
+
+
+
+/***********************************************************************
+** FUNCTION: PR_GetHostName() **OBSOLETE**
+** Use PR_GetSystemInfo() (prsystem.h) with an argument of
+** PR_SI_HOSTNAME instead.
+** DESCRIPTION:
+** Get the DNS name of the hosting computer.
+**
+** INPUTS:
+** char *name The location where the host's name is stored.
+** PRIntn bufsize Number of bytes in 'name'.
+** OUTPUTS:
+** PRHostEnt *name
+** This string is filled in by the runtime if the
+** function returns PR_SUCCESS. The string must be
+** allocated by the caller
+** RETURN:
+** PRStatus PR_SUCCESS if the succeeds. If it fails the
+** result will be PR_FAILURE and the reason for
+** the failure can be retrieved by PR_GetError().
+***********************************************************************/
+PR_EXTERN(PRStatus) PR_GetHostName(char *name, PRUint32 namelen);
+
+/*
+** Return the current thread's last error string.
+** obsoleted by PR_GetErrorText().
+*/
+PR_EXTERN(const char *) PR_GetErrorString(void);
+
+PR_END_EXTERN_C
+
+#endif /* defined(PROBSLET_H) */
+
+/* probslet.h */
diff --git a/pr/include/obsolete/protypes.h b/pr/include/obsolete/protypes.h
new file mode 100644
index 00000000..e70b03b4
--- /dev/null
+++ b/pr/include/obsolete/protypes.h
@@ -0,0 +1,176 @@
+/* -*- 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 section typedefs the old 'native' types to the new PR<type>s.
+ * These definitions are scheduled to be eliminated at the earliest
+ * possible time. The NSPR API is implemented and documented using
+ * the new definitions.
+ */
+
+#if !defined(PROTYPES_H)
+#define PROTYPES_H
+
+
+/* SVR4 typedef of uint is commonly found on UNIX machines. */
+#ifdef XP_UNIX
+#include <sys/types.h>
+#else
+typedef PRUintn uint;
+#endif
+
+typedef PRUintn uintn;
+typedef PRUint64 uint64;
+#if !defined(XP_MAC) && !defined(_WIN32) && !defined(XP_OS2)
+typedef PRUint32 uint32;
+#else
+typedef unsigned long uint32;
+#endif
+typedef PRUint16 uint16;
+typedef PRUint8 uint8;
+
+#ifndef _XP_Core_
+typedef PRIntn intn;
+#endif
+
+/*
+ * On AIX 4.3, sys/inttypes.h (which is included by sys/types.h, a very
+ * common header file) defines the types int8, int16, int32, and int64.
+ * So we don't define these four types here to avoid conflicts in case
+ * the code also includes sys/types.h.
+ */
+#if defined(AIX4_3)
+#include <sys/inttypes.h>
+#else
+typedef PRInt64 int64;
+
+/* /usr/include/model.h on HP-UX defines int8, int16, and int32 */
+#if defined(HPUX)
+#include <model.h>
+#else
+#if !defined(WIN32) || !defined(_WINSOCK2API_) /* defines its own "int32" */
+#if !defined(XP_MAC) && !defined(_WIN32) && !defined(XP_OS2)
+typedef PRInt32 int32;
+#else
+typedef long int32;
+#endif
+#endif
+typedef PRInt16 int16;
+typedef PRInt8 int8;
+#endif /* HPUX */
+#endif /* AIX4_3 */
+
+typedef PRFloat64 float64;
+typedef PRUptrdiff uptrdiff_t;
+typedef PRUword uprword_t;
+typedef PRWord prword_t;
+
+
+/* Re: prbit.h */
+#define TEST_BIT PR_TEST_BIT
+#define SET_BIT PR_SET_BIT
+#define CLEAR_BIT PR_CLEAR_BIT
+
+/* Re: prarena.h->plarena.h */
+#define PRArena PLArena
+#define PRArenaPool PLArenaPool
+#define PRArenaStats PLArenaStats
+#define PR_ARENA_ALIGN PL_ARENA_ALIGN
+#define PR_INIT_ARENA_POOL PL_INIT_ARENA_POOL
+#define PR_ARENA_ALLOCATE PL_ARENA_ALLOCATE
+#define PR_ARENA_GROW PL_ARENA_GROW
+#define PR_ARENA_MARK PL_ARENA_MARK
+#define PR_CLEAR_UNUSED PL_CLEAR_UNUSED
+#define PR_CLEAR_ARENA PL_CLEAR_ARENA
+#define PR_ARENA_RELEASE PL_ARENA_RELEASE
+#define PR_COUNT_ARENA PL_COUNT_ARENA
+#define PR_ARENA_DESTROY PL_ARENA_DESTROY
+#define PR_InitArenaPool PL_InitArenaPool
+#define PR_FreeArenaPool PL_FreeArenaPool
+#define PR_FinishArenaPool PL_FinishArenaPool
+#define PR_CompactArenaPool PL_CompactArenaPool
+#define PR_ArenaFinish PL_ArenaFinish
+#define PR_ArenaAllocate PL_ArenaAllocate
+#define PR_ArenaGrow PL_ArenaGrow
+#define PR_ArenaRelease PL_ArenaRelease
+#define PR_ArenaCountAllocation PL_ArenaCountAllocation
+#define PR_ArenaCountInplaceGrowth PL_ArenaCountInplaceGrowth
+#define PR_ArenaCountGrowth PL_ArenaCountGrowth
+#define PR_ArenaCountRelease PL_ArenaCountRelease
+#define PR_ArenaCountRetract PL_ArenaCountRetract
+
+/* Re: prevent.h->plevent.h */
+#define PREvent PLEvent
+#define PREventQueue PLEventQueue
+#define PR_CreateEventQueue PL_CreateEventQueue
+#define PR_DestroyEventQueue PL_DestroyEventQueue
+#define PR_GetEventQueueMonitor PL_GetEventQueueMonitor
+#define PR_ENTER_EVENT_QUEUE_MONITOR PL_ENTER_EVENT_QUEUE_MONITOR
+#define PR_EXIT_EVENT_QUEUE_MONITOR PL_EXIT_EVENT_QUEUE_MONITOR
+#define PR_PostEvent PL_PostEvent
+#define PR_PostSynchronousEvent PL_PostSynchronousEvent
+#define PR_GetEvent PL_GetEvent
+#define PR_EventAvailable PL_EventAvailable
+#define PREventFunProc PLEventFunProc
+#define PR_MapEvents PL_MapEvents
+#define PR_RevokeEvents PL_RevokeEvents
+#define PR_ProcessPendingEvents PL_ProcessPendingEvents
+#define PR_WaitForEvent PL_WaitForEvent
+#define PR_EventLoop PL_EventLoop
+#define PR_GetEventQueueSelectFD PL_GetEventQueueSelectFD
+#define PRHandleEventProc PLHandleEventProc
+#define PRDestroyEventProc PLDestroyEventProc
+#define PR_InitEvent PL_InitEvent
+#define PR_GetEventOwner PL_GetEventOwner
+#define PR_HandleEvent PL_HandleEvent
+#define PR_DestroyEvent PL_DestroyEvent
+#define PR_DequeueEvent PL_DequeueEvent
+#define PR_GetMainEventQueue PL_GetMainEventQueue
+
+/* Re: prhash.h->plhash.h */
+#define PRHashEntry PLHashEntry
+#define PRHashTable PLHashTable
+#define PRHashNumber PLHashNumber
+#define PRHashFunction PLHashFunction
+#define PRHashComparator PLHashComparator
+#define PRHashEnumerator PLHashEnumerator
+#define PRHashAllocOps PLHashAllocOps
+#define PR_NewHashTable PL_NewHashTable
+#define PR_HashTableDestroy PL_HashTableDestroy
+#define PR_HashTableRawLookup PL_HashTableRawLookup
+#define PR_HashTableRawAdd PL_HashTableRawAdd
+#define PR_HashTableRawRemove PL_HashTableRawRemove
+#define PR_HashTableAdd PL_HashTableAdd
+#define PR_HashTableRemove PL_HashTableRemove
+#define PR_HashTableEnumerateEntries PL_HashTableEnumerateEntries
+#define PR_HashTableLookup PL_HashTableLookup
+#define PR_HashTableDump PL_HashTableDump
+#define PR_HashString PL_HashString
+#define PR_CompareStrings PL_CompareStrings
+#define PR_CompareValues PL_CompareValues
+
+#if defined(XP_MAC)
+#ifndef TRUE /* Mac standard is lower case true */
+ #define TRUE 1
+#endif
+#ifndef FALSE /* Mac standard is lower case false */
+ #define FALSE 0
+#endif
+#endif
+
+#endif /* !defined(PROTYPES_H) */
diff --git a/pr/include/obsolete/prsem.h b/pr/include/obsolete/prsem.h
new file mode 100644
index 00000000..e88dca96
--- /dev/null
+++ b/pr/include/obsolete/prsem.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.
+ */
+
+#ifndef prsem_h___
+#define prsem_h___
+
+/*
+** API for counting semaphores. Semaphores are counting synchronizing
+** variables based on a lock and a condition variable. They are lightweight
+** contention control for a given count of resources.
+*/
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+typedef struct PRSemaphore PRSemaphore;
+
+/*
+** Create a new semaphore object.
+*/
+PR_EXTERN(PRSemaphore*) PR_NewSem(PRUintn value);
+
+/*
+** Destroy the given semaphore object.
+**
+*/
+PR_EXTERN(void) PR_DestroySem(PRSemaphore *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_EXTERN(PRStatus) PR_WaitSem(PRSemaphore *sem);
+
+/*
+** 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_EXTERN(void) PR_PostSem(PRSemaphore *sem);
+
+/*
+** Returns the value of the semaphore referenced by sem without affecting
+** the state of the semaphore. The value represents the semaphore vaule
+F** at the time of the call, but may not be the actual value when the
+** caller inspects it.
+*/
+PR_EXTERN(PRUintn) PR_GetValueSem(PRSemaphore *sem);
+
+PR_END_EXTERN_C
+
+#endif /* prsem_h___ */
diff --git a/pr/include/pratom.h b/pr/include/pratom.h
new file mode 100644
index 00000000..d8ad0a8e
--- /dev/null
+++ b/pr/include/pratom.h
@@ -0,0 +1,151 @@
+/* -*- 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.
+ */
+
+/* GLOBAL FUNCTIONS:
+** DESCRIPTION:
+** PR Atomic operations
+*/
+
+#ifndef pratom_h___
+#define pratom_h___
+
+#include "prtypes.h"
+#include "prlock.h"
+
+PR_BEGIN_EXTERN_C
+
+/*
+** FUNCTION: PR_AtomicIncrement
+** DESCRIPTION:
+** Atomically increment a 32 bit value.
+** INPUTS:
+** val: a pointer to the value to increment
+** RETURN:
+** the returned value is the result of the increment
+*/
+PR_EXTERN(PRInt32) PR_AtomicIncrement(PRInt32 *val);
+
+/*
+** FUNCTION: PR_AtomicDecrement
+** DESCRIPTION:
+** Atomically decrement a 32 bit value.
+** INPUTS:
+** val: a pointer to the value to decrement
+** RETURN:
+** the returned value is the result of the decrement
+*/
+PR_EXTERN(PRInt32) PR_AtomicDecrement(PRInt32 *val);
+
+/*
+** FUNCTION: PR_AtomicSet
+** DESCRIPTION:
+** Atomically set a 32 bit value.
+** INPUTS:
+** val: A pointer to a 32 bit value to be set
+** newval: The newvalue to assign to val
+** RETURN:
+** Returns the prior value
+*/
+PR_EXTERN(PRInt32) PR_AtomicSet(PRInt32 *val, PRInt32 newval);
+
+/*
+** FUNCTION: PR_AtomicAdd
+** DESCRIPTION:
+** Atomically add a 32 bit value.
+** INPUTS:
+** ptr: a pointer to the value to increment
+** val: value to be added
+** RETURN:
+** the returned value is the result of the addition
+*/
+PR_EXTERN(PRInt32) PR_AtomicAdd(PRInt32 *ptr, PRInt32 val);
+
+/*
+** LIFO linked-list (stack)
+*/
+typedef struct PRStackElemStr PRStackElem;
+
+struct PRStackElemStr {
+ PRStackElem *prstk_elem_next; /* next pointer MUST be at offset 0;
+ assembly language code relies on this */
+};
+
+typedef struct PRStackStr PRStack;
+
+struct PRStackStr {
+ PRStackElem prstk_head; /* head MUST be at offset 0; assembly
+ language code relies on this
+ */
+ PRLock *prstk_lock;
+ char *prstk_name;
+};
+
+
+/*
+** FUNCTION: PR_CreateStack
+** DESCRIPTION:
+** Create a stack, a LIFO linked list
+** INPUTS:
+** stack_name: a pointer to string containing the name of the stack
+** RETURN:
+** A pointer to the created stack, if successful, else NULL.
+*/
+PR_EXTERN(PRStack *) PR_CreateStack(const char *stack_name);
+
+/*
+** FUNCTION: PR_StackPush
+** DESCRIPTION:
+** Push an element on the top of the stack
+** INPUTS:
+** stack: pointer to the stack
+** stack_elem: pointer to the stack element
+** RETURN:
+** None
+*/
+PR_EXTERN(void) PR_StackPush(PRStack *stack, PRStackElem *stack_elem);
+
+/*
+** FUNCTION: PR_StackPop
+** DESCRIPTION:
+** Remove the element on the top of the stack
+** INPUTS:
+** stack: pointer to the stack
+** RETURN:
+** A pointer to the stack element removed from the top of the stack,
+** if non-empty,
+** else NULL
+*/
+PR_EXTERN(PRStackElem *) PR_StackPop(PRStack *stack);
+
+/*
+** FUNCTION: PR_DestroyStack
+** DESCRIPTION:
+** Destroy the stack
+** INPUTS:
+** stack: pointer to the stack
+** RETURN:
+** PR_SUCCESS - if successfully deleted
+** PR_FAILURE - if the stack is not empty
+** PR_GetError will return
+** PR_INVALID_STATE_ERROR - stack is not empty
+*/
+PR_EXTERN(PRStatus) PR_DestroyStack(PRStack *stack);
+
+PR_END_EXTERN_C
+
+#endif /* pratom_h___ */
diff --git a/pr/include/prbit.h b/pr/include/prbit.h
new file mode 100644
index 00000000..72beb243
--- /dev/null
+++ b/pr/include/prbit.h
@@ -0,0 +1,92 @@
+/* -*- 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 prbit_h___
+#define prbit_h___
+
+#include "prtypes.h"
+PR_BEGIN_EXTERN_C
+
+/*
+** A prbitmap_t is a long integer that can be used for bitmaps
+*/
+typedef unsigned long prbitmap_t;
+
+#define PR_TEST_BIT(_map,_bit) \
+ ((_map)[(_bit)>>PR_BITS_PER_LONG_LOG2] & (1L << ((_bit) & (PR_BITS_PER_LONG-1))))
+#define PR_SET_BIT(_map,_bit) \
+ ((_map)[(_bit)>>PR_BITS_PER_LONG_LOG2] |= (1L << ((_bit) & (PR_BITS_PER_LONG-1))))
+#define PR_CLEAR_BIT(_map,_bit) \
+ ((_map)[(_bit)>>PR_BITS_PER_LONG_LOG2] &= ~(1L << ((_bit) & (PR_BITS_PER_LONG-1))))
+
+/*
+** Compute the log of the least power of 2 greater than or equal to n
+*/
+PR_EXTERN(PRIntn) PR_CeilingLog2(PRUint32 i);
+
+/*
+** Compute the log of the greatest power of 2 less than or equal to n
+*/
+PR_EXTERN(PRIntn) PR_FloorLog2(PRUint32 i);
+
+/*
+** Macro version of PR_CeilingLog2: Compute the log of the least power of
+** 2 greater than or equal to _n. The result is returned in _log2.
+*/
+#define PR_CEILING_LOG2(_log2,_n) \
+ PR_BEGIN_MACRO \
+ PRUint32 j_ = (PRUint32)(_n); \
+ (_log2) = 0; \
+ if ((j_) & ((j_)-1)) \
+ (_log2) += 1; \
+ if ((j_) >> 16) \
+ (_log2) += 16, (j_) >>= 16; \
+ if ((j_) >> 8) \
+ (_log2) += 8, (j_) >>= 8; \
+ if ((j_) >> 4) \
+ (_log2) += 4, (j_) >>= 4; \
+ if ((j_) >> 2) \
+ (_log2) += 2, (j_) >>= 2; \
+ if ((j_) >> 1) \
+ (_log2) += 1; \
+ PR_END_MACRO
+
+/*
+** Macro version of PR_FloorLog2: Compute the log of the greatest power of
+** 2 less than or equal to _n. The result is returned in _log2.
+**
+** This is equivalent to finding the highest set bit in the word.
+*/
+#define PR_FLOOR_LOG2(_log2,_n) \
+ PR_BEGIN_MACRO \
+ PRUint32 j_ = (PRUint32)(_n); \
+ (_log2) = 0; \
+ if ((j_) >> 16) \
+ (_log2) += 16, (j_) >>= 16; \
+ if ((j_) >> 8) \
+ (_log2) += 8, (j_) >>= 8; \
+ if ((j_) >> 4) \
+ (_log2) += 4, (j_) >>= 4; \
+ if ((j_) >> 2) \
+ (_log2) += 2, (j_) >>= 2; \
+ if ((j_) >> 1) \
+ (_log2) += 1; \
+ PR_END_MACRO
+
+PR_END_EXTERN_C
+#endif /* prbit_h___ */
diff --git a/pr/include/prclist.h b/pr/include/prclist.h
new file mode 100644
index 00000000..f1215d48
--- /dev/null
+++ b/pr/include/prclist.h
@@ -0,0 +1,121 @@
+/* -*- 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 prclist_h___
+#define prclist_h___
+
+#include "prtypes.h"
+
+typedef struct PRCListStr PRCList;
+
+/*
+** Circular linked list
+*/
+struct PRCListStr {
+ PRCList *next;
+ PRCList *prev;
+};
+
+/*
+** Insert element "_e" into the list, before "_l".
+*/
+#define PR_INSERT_BEFORE(_e,_l) \
+ PR_BEGIN_MACRO \
+ (_e)->next = (_l); \
+ (_e)->prev = (_l)->prev; \
+ (_l)->prev->next = (_e); \
+ (_l)->prev = (_e); \
+ PR_END_MACRO
+
+/*
+** Insert element "_e" into the list, after "_l".
+*/
+#define PR_INSERT_AFTER(_e,_l) \
+ PR_BEGIN_MACRO \
+ (_e)->next = (_l)->next; \
+ (_e)->prev = (_l); \
+ (_l)->next->prev = (_e); \
+ (_l)->next = (_e); \
+ PR_END_MACRO
+
+/*
+** Return the element following element "_e"
+*/
+#define PR_NEXT_LINK(_e) \
+ ((_e)->next)
+/*
+** Return the element preceding element "_e"
+*/
+#define PR_PREV_LINK(_e) \
+ ((_e)->prev)
+
+/*
+** Append an element "_e" to the end of the list "_l"
+*/
+#define PR_APPEND_LINK(_e,_l) PR_INSERT_BEFORE(_e,_l)
+
+/*
+** Insert an element "_e" at the head of the list "_l"
+*/
+#define PR_INSERT_LINK(_e,_l) PR_INSERT_AFTER(_e,_l)
+
+/* Return the head/tail of the list */
+#define PR_LIST_HEAD(_l) (_l)->next
+#define PR_LIST_TAIL(_l) (_l)->prev
+
+/*
+** Remove the element "_e" from it's circular list.
+*/
+#define PR_REMOVE_LINK(_e) \
+ PR_BEGIN_MACRO \
+ (_e)->prev->next = (_e)->next; \
+ (_e)->next->prev = (_e)->prev; \
+ PR_END_MACRO
+
+/*
+** Remove the element "_e" from it's circular list. Also initializes the
+** linkage.
+*/
+#define PR_REMOVE_AND_INIT_LINK(_e) \
+ PR_BEGIN_MACRO \
+ (_e)->prev->next = (_e)->next; \
+ (_e)->next->prev = (_e)->prev; \
+ (_e)->next = (_e); \
+ (_e)->prev = (_e); \
+ PR_END_MACRO
+
+/*
+** Return non-zero if the given circular list "_l" is empty, zero if the
+** circular list is not empty
+*/
+#define PR_CLIST_IS_EMPTY(_l) \
+ ((_l)->next == (_l))
+
+/*
+** Initialize a circular list
+*/
+#define PR_INIT_CLIST(_l) \
+ PR_BEGIN_MACRO \
+ (_l)->next = (_l); \
+ (_l)->prev = (_l); \
+ PR_END_MACRO
+
+#define PR_INIT_STATIC_CLIST(_l) \
+ {(_l), (_l)}
+
+#endif /* prclist_h___ */
diff --git a/pr/include/prcmon.h b/pr/include/prcmon.h
new file mode 100644
index 00000000..08567c0f
--- /dev/null
+++ b/pr/include/prcmon.h
@@ -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.
+ */
+
+#ifndef prcmon_h___
+#define prcmon_h___
+
+/*
+** Interface to cached monitors. Cached monitors use an address to find a
+** given PR monitor. In this way a monitor can be associated with another
+** object without preallocating a monitor for all objects.
+**
+** A hash table is used to quickly map addresses to individual monitors
+** and the system automatically grows the hash table as needed.
+**
+** Cache monitors are about 5 times slower to use than uncached monitors.
+*/
+#include "prmon.h"
+#include "prinrval.h"
+
+PR_BEGIN_EXTERN_C
+
+/**
+** Like PR_EnterMonitor except use the "address" to find a monitor in the
+** monitor cache. If successful, returns the PRMonitor now associated
+** with "address". Note that you must PR_CExitMonitor the address to
+** release the monitor cache entry (otherwise the monitor cache will fill
+** up). This call will return NULL if the monitor cache needs to be
+** expanded and the system is out of memory.
+*/
+PR_EXTERN(PRMonitor*) PR_CEnterMonitor(void *address);
+
+/*
+** Like PR_ExitMonitor except use the "address" to find a monitor in the
+** monitor cache.
+*/
+PR_EXTERN(PRStatus) PR_CExitMonitor(void *address);
+
+/*
+** Like PR_Wait except use the "address" to find a monitor in the
+** monitor cache.
+*/
+PR_EXTERN(PRStatus) PR_CWait(void *address, PRIntervalTime timeout);
+
+/*
+** Like PR_Notify except use the "address" to find a monitor in the
+** monitor cache.
+*/
+PR_EXTERN(PRStatus) PR_CNotify(void *address);
+
+/*
+** Like PR_NotifyAll except use the "address" to find a monitor in the
+** monitor cache.
+*/
+PR_EXTERN(PRStatus) PR_CNotifyAll(void *address);
+
+PR_END_EXTERN_C
+
+#endif /* prcmon_h___ */
diff --git a/pr/include/prcountr.h b/pr/include/prcountr.h
new file mode 100644
index 00000000..44d5fb43
--- /dev/null
+++ b/pr/include/prcountr.h
@@ -0,0 +1,512 @@
+/* -*- 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 prcountr_h___
+#define prcountr_h___
+
+/*----------------------------------------------------------------------------
+** prcountr.h -- NSPR Instrumentation counters
+**
+** The NSPR Counter Feature provides a means to "count
+** something." Counters can be dynamically defined, incremented,
+** decremented, set, and deleted under application program
+** control.
+**
+** The Counter Feature is intended to be used as instrumentation,
+** not as operational data. If you need a counter for operational
+** data, use native integral types.
+**
+** Counters are 32bit unsigned intergers. On overflow, a counter
+** will wrap. No exception is recognized or reported.
+**
+** A counter can be dynamically created using a two level naming
+** convention. A "handle" is returned when the counter is
+** created. The counter can subsequently be addressed by its
+** handle. An API is provided to get an existing counter's handle
+** given the names with which it was originally created.
+** Similarly, a counter's name can be retrieved given its handle.
+**
+** The counter naming convention is a two-level hierarchy. The
+** QName is the higher level of the hierarchy; RName is the
+** lower level. RNames can be thought of as existing within a
+** QName. The same RName can exist within multiple QNames. QNames
+** are unique. The NSPR Counter is not a near-zero overhead
+** feature. Application designers should be aware of
+** serialization issues when using the Counter API. Creating a
+** counter locks a large asset, potentially causing a stall. This
+** suggest that applications should create counters at component
+** initialization, for example, and not create and destroy them
+** willy-nilly. ... You have been warned.
+**
+** Incrementing and Adding to counters uses atomic operations.
+** The performance of these operations will vary from platform
+** to platform. On platforms where atomic operations are not
+** supported the overhead may be substantial.
+**
+** When traversing the counter database with FindNext functions,
+** the instantaneous values of any given counter is that at the
+** moment of extraction. The state of the entire counter database
+** may not be viewed as atomic.
+**
+** The counter interface may be disabled (No-Op'd) at compile
+** time. When DEBUG is defined at compile time, the Counter
+** Feature is compiled into NSPR and applications invoking it.
+** When DEBUG is not defined, the counter macros compile to
+** nothing. To force the Counter Feature to be compiled into an
+** optimized build, define FORCE_NSPR_COUNTERS at compile time
+** for both NSPR and the application intending to use it.
+**
+** Application designers should use the macro form of the Counter
+** Feature methods to minimize performance impact in optimized
+** builds. The macros normally compile to nothing on optimized
+** builds.
+**
+** Application designers should be aware of the effects of
+** debug and optimized build differences when using result of the
+** Counter Feature macros in expressions.
+**
+** The Counter Feature is thread-safe and SMP safe.
+**
+** /lth. 09-Jun-1998.
+*/
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+/*
+** Opaque counter handle type.
+** ... don't even think of looking in here.
+**
+*/
+typedef void * PRCounterHandle;
+
+#define PRCOUNTER_NAME_MAX 31
+#define PRCOUNTER_DESC_MAX 255
+
+
+#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS)
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_DEFINE_COUNTER() -- Define a PRCounterHandle
+**
+** DESCRIPTION: PR_DEFINE_COUNTER() is used to define a counter
+** handle.
+**
+*/
+#define PR_DEFINE_COUNTER(name) PRCounterHandle name
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_INIT_COUNTER_HANDLE() -- Set the value of a PRCounterHandle
+**
+** DESCRIPTION:
+** PR_INIT_COUNTER_HANDLE() sets the value of a PRCounterHandle
+** to value.
+**
+*/
+#define PR_INIT_COUNTER_HANDLE(handle,value)\
+ (handle) = (PRCounterHandle)(value)
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_CreateCounter() -- Create a counter
+**
+** DESCRIPTION: PR_CreateCounter() creates a counter object and
+** initializes it to zero.
+**
+** The macro form takes as its first argument the name of the
+** PRCounterHandle to receive the handle returned from
+** PR_CreateCounter().
+**
+** INPUTS:
+** qName: The QName for the counter object. The maximum length
+** of qName is defined by PRCOUNTER_NAME_MAX
+**
+** rName: The RName for the counter object. The maximum length
+** of qName is defined by PRCOUNTER_NAME_MAX
+**
+** descrioption: The description of the counter object. The
+** maximum length of description is defined by
+** PRCOUNTER_DESC_MAX.
+**
+** OUTPUTS:
+**
+** RETURNS:
+** PRCounterHandle.
+**
+** RESTRICTIONS:
+**
+*/
+#define PR_CREATE_COUNTER(handle,qName,rName,description)\
+ (handle) = PR_CreateCounter((qName),(rName),(description))
+
+PR_EXTERN(PRCounterHandle)
+ PR_CreateCounter(
+ const char *qName,
+ const char *rName,
+ const char *description
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_DestroyCounter() -- Destroy a counter object.
+**
+** DESCRIPTION: PR_DestroyCounter() removes a counter and
+** unregisters its handle from the counter database.
+**
+** INPUTS:
+** handle: the PRCounterHandle of the counter to be destroyed.
+**
+** OUTPUTS:
+** The counter is destroyed.
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#define PR_DESTROY_COUNTER(handle) PR_DestroyCounter((handle))
+
+PR_EXTERN(void)
+ PR_DestroyCounter(
+ PRCounterHandle handle
+);
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_GetCounterHandleFromName() -- Retreive a
+** counter's handle give its name.
+**
+** DESCRIPTION: PR_GetCounterHandleFromName() retreives a
+** counter's handle from the counter database, given the name
+** the counter was originally created with.
+**
+** INPUTS:
+** qName: Counter's original QName.
+** rName: Counter's original RName.
+**
+** OUTPUTS:
+**
+** RETURNS:
+** PRCounterHandle or PRCounterError.
+**
+** RESTRICTIONS:
+**
+*/
+#define PR_GET_COUNTER_HANDLE_FROM_NAME(handle,qName,rName)\
+ (handle) = PR_GetCounterHandleFromName((qName),(rName))
+
+PR_EXTERN(PRCounterHandle)
+ PR_GetCounterHandleFromName(
+ const char *qName,
+ const char *rName
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_GetCounterNameFromHandle() -- Retreive a
+** counter's name, given its handle.
+**
+** DESCRIPTION: PR_GetCounterNameFromHandle() retreives a
+** counter's name given its handle.
+**
+** INPUTS:
+** qName: Where to store a pointer to qName.
+** rName: Where to store a pointer to rName.
+** description: Where to store a pointer to description.
+**
+** OUTPUTS: Pointers to the Counter Feature's copies of the names
+** used when the counters were created.
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#define PR_GET_COUNTER_NAME_FROM_HANDLE(handle,qName,rName,description)\
+ PR_GetCounterNameFromHandle((handle),(qName),(rName),(description))
+
+PR_EXTERN(void)
+ PR_GetCounterNameFromHandle(
+ PRCounterHandle handle,
+ const char **qName,
+ const char **rName,
+ const char **description
+);
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_IncrementCounter() -- Add one to the referenced
+** counter.
+**
+** DESCRIPTION: Add one to the referenced counter.
+**
+** INPUTS:
+** handle: The PRCounterHandle of the counter to be incremented
+**
+** OUTPUTS: The counter is incrementd.
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#define PR_INCREMENT_COUNTER(handle) PR_IncrementCounter(handle)
+
+PR_EXTERN(void)
+ PR_IncrementCounter(
+ PRCounterHandle handle
+);
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_DecrementCounter() -- Subtract one from the
+** referenced counter
+**
+** DESCRIPTION: Subtract one from the referenced counter.
+**
+** INPUTS:
+** handle: The PRCounterHandle of the coutner to be
+** decremented.
+**
+** OUTPUTS: the counter is decremented.
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#define PR_DECREMENT_COUNTER(handle) PR_DecrementCounter(handle)
+
+PR_EXTERN(void)
+ PR_DecrementCounter(
+ PRCounterHandle handle
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_AddToCounter() -- Add a value to a counter.
+**
+** DESCRIPTION: Add value to the counter referenced by handle.
+**
+** INPUTS:
+** handle: the PRCounterHandle of the counter to be added to.
+**
+** value: the value to be added to the counter.
+**
+** OUTPUTS: new value for counter.
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#define PR_ADD_TO_COUNTER(handle,value)\
+ PR_AddToCounter((handle),(value))
+
+PR_EXTERN(void)
+ PR_AddToCounter(
+ PRCounterHandle handle,
+ PRUint32 value
+);
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_SubtractFromCounter() -- A value is subtracted
+** from a counter.
+**
+** DESCRIPTION:
+** Subtract a value from a counter.
+**
+** INPUTS:
+** handle: the PRCounterHandle of the counter to be subtracted
+** from.
+**
+** value: the value to be subtracted from the counter.
+**
+** OUTPUTS: new value for counter
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#define PR_SUBTRACT_FROM_COUNTER(handle,value)\
+ PR_SubtractFromCounter((handle),(value))
+
+PR_EXTERN(void)
+ PR_SubtractFromCounter(
+ PRCounterHandle handle,
+ PRUint32 value
+);
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_GetCounter() -- Retreive the value of a counter
+**
+** DESCRIPTION:
+** Retreive the value of a counter.
+**
+** INPUTS:
+** handle: the PR_CounterHandle of the counter to be retreived
+**
+** OUTPUTS:
+**
+** RETURNS: The value of the referenced counter
+**
+** RESTRICTIONS:
+**
+*/
+#define PR_GET_COUNTER(counter,handle)\
+ (counter) = PR_GetCounter((handle))
+
+PR_EXTERN(PRUint32)
+ PR_GetCounter(
+ PRCounterHandle handle
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_SetCounter() -- Replace the content of counter
+** with value.
+**
+** DESCRIPTION: The contents of the referenced counter are
+** replaced by value.
+**
+** INPUTS:
+** handle: the PRCounterHandle of the counter whose contents
+** are to be replaced.
+**
+** value: the new value of the counter.
+**
+** OUTPUTS:
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#define PR_SET_COUNTER(handle,value) PR_SetCounter((handle),(value))
+
+PR_EXTERN(void)
+ PR_SetCounter(
+ PRCounterHandle handle,
+ PRUint32 value
+);
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_FindNextCounterQname() -- Retreive the next QName counter
+** handle iterator
+**
+** DESCRIPTION:
+** PR_FindNextCounterQname() retreives the first or next Qname
+** the counter data base, depending on the value of handle. When
+** handle is NULL, the function attempts to retreive the first
+** QName handle in the database. When handle is a handle previosly
+** retreived QName handle, then the function attempts to retreive
+** the next QName handle.
+**
+** INPUTS:
+** handle: PRCounterHandle or NULL.
+**
+** OUTPUTS: returned
+**
+** RETURNS: PRCounterHandle or NULL when no more QName counter
+** handles are present.
+**
+** RESTRICTIONS:
+** A concurrent PR_CreateCounter() or PR_DestroyCounter() may
+** cause unpredictable results.
+**
+** A PRCounterHandle returned from this function may only be used
+** in another PR_FindNextCounterQname() function call; other
+** operations may cause unpredictable results.
+**
+*/
+#define PR_FIND_NEXT_COUNTER_QNAME(next,handle)\
+ (next) = PR_FindNextCounterQname((handle))
+
+PR_EXTERN(PRCounterHandle)
+ PR_FindNextCounterQname(
+ PRCounterHandle handle
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_FindNextCounterRname() -- Retreive the next RName counter
+** handle iterator
+**
+** DESCRIPTION:
+** PR_FindNextCounterRname() retreives the first or next RNname
+** handle from the counter data base, depending on the
+** value of handle. When handle is NULL, the function attempts to
+** retreive the first RName handle in the database. When handle is
+** a handle previosly retreived RName handle, then the function
+** attempts to retreive the next RName handle.
+**
+** INPUTS:
+** handle: PRCounterHandle or NULL.
+** qhandle: PRCounterHandle of a previously aquired via
+** PR_FIND_NEXT_QNAME_HANDLE()
+**
+** OUTPUTS: returned
+**
+** RETURNS: PRCounterHandle or NULL when no more RName counter
+** handles are present.
+**
+** RESTRICTIONS:
+** A concurrent PR_CreateCounter() or PR_DestroyCounter() may
+** cause unpredictable results.
+**
+** A PRCounterHandle returned from this function may only be used
+** in another PR_FindNextCounterRname() function call; other
+** operations may cause unpredictable results.
+**
+*/
+#define PR_FIND_NEXT_COUNTER_RNAME(next,rhandle,qhandle)\
+ (next) = PR_FindNextCounterRname((rhandle),(qhandle))
+
+PR_EXTERN(PRCounterHandle)
+ PR_FindNextCounterRname(
+ PRCounterHandle rhandle,
+ PRCounterHandle qhandle
+);
+
+
+#else /* ( !(defined(DEBUG) || defined(FORCE_NSPR_COUNTERS)) */
+/*
+** When counters are not compiled in, provide macros that
+** evaluate to No-Ops.
+**
+*/
+
+#define PR_DEFINE_COUNTER(name) PRCounterHandle name
+#define PR_INIT_COUNTER_HANDLE(handle,value)
+#define PR_CREATE_COUNTER(handle,qName,rName,description)
+#define PR_DESTROY_COUNTER(handle)
+#define PR_GET_COUNTER_HANDLE_FROM_NAME(handle,qName,rName)
+#define PR_GET_COUNTER_NAME_FROM_HANDLE(handle,qName,rName,description )
+#define PR_INCREMENT_COUNTER(handle)
+#define PR_DECREMENT_COUNTER(handle)
+#define PR_ADD_TO_COUNTER(handle,value)
+#define PR_SUBTRACT_FROM_COUNTER(handle,value)
+#define PR_GET_COUNTER(counter,handle) 0
+#define PR_SET_COUNTER(handle,value)
+#define PR_FIND_NEXT_COUNTER_QNAME(next,handle) NULL
+#define PR_FIND_NEXT_COUNTER_RNAME(next,rhandle,qhandle)
+
+#endif /* ( !(defined(DEBUG) || defined(FORCE_NSPR_COUNTERS)) */
+
+PR_END_EXTERN_C
+
+#endif /* prcountr_h___ */
diff --git a/pr/include/prcvar.h b/pr/include/prcvar.h
new file mode 100644
index 00000000..a8eee446
--- /dev/null
+++ b/pr/include/prcvar.h
@@ -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.
+ */
+
+#ifndef prcvar_h___
+#define prcvar_h___
+
+#include "prlock.h"
+#include "prinrval.h"
+
+PR_BEGIN_EXTERN_C
+
+typedef struct PRCondVar PRCondVar;
+
+/*
+** Create a new condition variable.
+**
+** "lock" is the lock used to protect 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. In such cases, a NULL will be returned.
+*/
+PR_EXTERN(PRCondVar*) PR_NewCondVar(PRLock *lock);
+
+/*
+** 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_EXTERN(void) PR_DestroyCondVar(PRCondVar *cvar);
+
+/*
+** The thread that waits on a condition is blocked in a "waiting on
+** condition" state until another thread notifies the condition or a
+** caller specified amount of time expires. The lock associated with
+** the condition variable will be released, which must have be held
+** prior to the call to wait.
+**
+** Logically a notified thread is moved from the "waiting on condition"
+** state and made "ready." When scheduled, it will attempt to reacquire
+** the lock that it held when wait was called.
+**
+** The timeout has two well known values, PR_INTERVAL_NO_TIMEOUT and
+** PR_INTERVAL_NO_WAIT. The former value requires that a condition be
+** notified (or the thread interrupted) before it will resume from the
+** wait. If the timeout has a value of PR_INTERVAL_NO_WAIT, the effect
+** is to release the lock, possibly causing a rescheduling within the
+** runtime, then immediately attempting to reacquire the lock and resume.
+**
+** Any other value for timeout will cause the thread to be rescheduled
+** either due to explicit notification or an expired interval. The latter
+** must be determined by treating time as one part of the monitored data
+** being protected by the lock and tested explicitly for an expired
+** interval.
+**
+** Returns PR_FAILURE if the caller has not locked the lock associated
+** with the condition variable or the thread was interrupted (PR_Interrupt()).
+** The particular reason can be extracted with PR_GetError().
+*/
+PR_EXTERN(PRStatus) PR_WaitCondVar(PRCondVar *cvar, PRIntervalTime timeout);
+
+/*
+** Notify ONE thread that is currently waiting on 'cvar'. Which thread is
+** dependent on the implementation of the runtime. Common sense would dictate
+** that all threads waiting on a single condition have identical semantics,
+** therefore which one gets notified is not significant.
+**
+** The calling thead must hold the lock that protects the condition, as
+** well as the invariants that are tightly bound to the condition, when
+** notify is called.
+**
+** Returns PR_FAILURE if the caller has not locked the lock associated
+** with the condition variable.
+*/
+PR_EXTERN(PRStatus) PR_NotifyCondVar(PRCondVar *cvar);
+
+/*
+** Notify all of the threads waiting on the condition variable. The order
+** that the threads are notified is indeterminant. The lock that protects
+** the condition must be held.
+**
+** Returns PR_FAILURE if the caller has not locked the lock associated
+** with the condition variable.
+*/
+PR_EXTERN(PRStatus) PR_NotifyAllCondVar(PRCondVar *cvar);
+
+PR_END_EXTERN_C
+
+#endif /* prcvar_h___ */
diff --git a/pr/include/prdtoa.h b/pr/include/prdtoa.h
new file mode 100644
index 00000000..22cfc58c
--- /dev/null
+++ b/pr/include/prdtoa.h
@@ -0,0 +1,71 @@
+/* -*- 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 prdtoa_h___
+#define prdtoa_h___
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+/*
+** PR_strtod() returns as a double-precision floating-point number
+** the value represented by the character string pointed to by
+** s00. The string is scanned up to the first unrecognized
+** character.
+**a
+** If the value of se is not (char **)NULL, a pointer to
+** the character terminating the scan is returned in the location pointed
+** to by se. If no number can be formed, se is set to s00, and
+** zero is returned.
+*/
+#if defined(HAVE_WATCOM_BUG_1)
+/* this is a hack to circumvent a bug in the Watcom C/C++ 11.0 compiler
+** When Watcom fixes the bug, remove the special case for Win16
+*/
+PRFloat64 __pascal __loadds __export
+#else
+PR_EXTERN(PRFloat64)
+#endif
+PR_strtod(const char *s00, char **se);
+
+/*
+** PR_cnvtf()
+** conversion routines for floating point
+** prcsn - number of digits of precision to generate floating
+** point value.
+*/
+PR_EXTERN(void) PR_cnvtf(char *buf, PRIntn bufsz, PRIntn prcsn, PRFloat64 fval);
+
+/*
+** PR_dtoa() converts double to a string.
+**
+** ARGUMENTS:
+** If rve is 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.
+*/
+PR_EXTERN(PRStatus) PR_dtoa(PRFloat64 d, PRIntn mode, PRIntn ndigits,
+ PRIntn *decpt, PRIntn *sign, char **rve, char *buf, PRSize bufsize);
+
+PR_END_EXTERN_C
+
+#endif /* prdtoa_h___ */
diff --git a/pr/include/prenv.h b/pr/include/prenv.h
new file mode 100644
index 00000000..1126e5bc
--- /dev/null
+++ b/pr/include/prenv.h
@@ -0,0 +1,40 @@
+/* -*- 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 prenv_h___
+#define prenv_h___
+
+#include "prtypes.h"
+
+/*******************************************************************************/
+/*******************************************************************************/
+/****************** THESE FUNCTIONS MAY NOT BE THREAD SAFE *********************/
+/*******************************************************************************/
+/*******************************************************************************/
+
+PR_BEGIN_EXTERN_C
+
+/*
+** Lookup a variable in the environment. Return NULL if it's not found,
+** otherwise return it's value.
+*/
+PR_EXTERN(char*) PR_GetEnv(const char *var);
+
+PR_END_EXTERN_C
+
+#endif /* prenv_h___ */
diff --git a/pr/include/prerror.h b/pr/include/prerror.h
new file mode 100644
index 00000000..dbb7256b
--- /dev/null
+++ b/pr/include/prerror.h
@@ -0,0 +1,205 @@
+/* -*- 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 prerror_h___
+#define prerror_h___
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+typedef PRInt32 PRErrorCode;
+
+#define PR_NSPR_ERROR_BASE -6000
+
+#define PR_OUT_OF_MEMORY_ERROR PR_NSPR_ERROR_BASE + 0
+ /* Insufficient memory to perform request */
+#define PR_BAD_DESCRIPTOR_ERROR PR_NSPR_ERROR_BASE + 1
+ /* the file descriptor used as an argument
+ in the function is invalid; either it has
+ been deleted or otherwise corrupted. */
+#define PR_WOULD_BLOCK_ERROR PR_NSPR_ERROR_BASE + 2
+ /* The operation would have blocked and that
+ is in conflict with the semantics that
+ have been established. */
+#define PR_ACCESS_FAULT_ERROR PR_NSPR_ERROR_BASE + 3
+#define PR_INVALID_METHOD_ERROR PR_NSPR_ERROR_BASE + 4
+ /* The method being called is invalid for
+ the type of file descriptor used. */
+#define PR_ILLEGAL_ACCESS_ERROR PR_NSPR_ERROR_BASE + 5
+#define PR_UNKNOWN_ERROR PR_NSPR_ERROR_BASE + 6
+ /* Some unknown error has occured */
+#define PR_PENDING_INTERRUPT_ERROR PR_NSPR_ERROR_BASE + 7
+ /* The operation terminated because another
+ thread has interrupted it (PR_Interrupt) */
+#define PR_NOT_IMPLEMENTED_ERROR PR_NSPR_ERROR_BASE + 8
+ /* The function called has not been
+ implemented. */
+#define PR_IO_ERROR PR_NSPR_ERROR_BASE + 9
+#define PR_IO_TIMEOUT_ERROR PR_NSPR_ERROR_BASE + 10
+ /* The I/O operation has not completed in
+ the time specified for the function. */
+#define PR_IO_PENDING_ERROR PR_NSPR_ERROR_BASE + 11
+ /* An I/O operation has been attempted on
+ a file descriptor that is currently
+ busy with another operation. */
+#define PR_DIRECTORY_OPEN_ERROR PR_NSPR_ERROR_BASE + 12
+ /* The directory could not be opened. */
+#define PR_INVALID_ARGUMENT_ERROR PR_NSPR_ERROR_BASE + 13
+ /* One or more of the arguments to the
+ function is invalid. */
+#define PR_ADDRESS_NOT_AVAILABLE_ERROR PR_NSPR_ERROR_BASE + 14
+ /* The network address (PRNetAddr) is not
+ available (probably in use). */
+#define PR_ADDRESS_NOT_SUPPORTED_ERROR PR_NSPR_ERROR_BASE + 15
+#define PR_IS_CONNECTED_ERROR PR_NSPR_ERROR_BASE + 16
+ /* An attempt to connect on an already
+ connected network file descriptor. */
+#define PR_BAD_ADDRESS_ERROR PR_NSPR_ERROR_BASE + 17
+ /* The network address specified is invalid
+ (as reported by the network). */
+#define PR_ADDRESS_IN_USE_ERROR PR_NSPR_ERROR_BASE + 18
+#define PR_CONNECT_REFUSED_ERROR PR_NSPR_ERROR_BASE + 19
+ /* The peer has refused to allow the connec-
+ tion to be established. */
+#define PR_NETWORK_UNREACHABLE_ERROR PR_NSPR_ERROR_BASE + 20
+ /* The network address specifies a host
+ that is unreachable (perhaps temporary). */
+#define PR_CONNECT_TIMEOUT_ERROR PR_NSPR_ERROR_BASE + 21
+ /* The connection attempt did not complete
+ in a reasonable period of time. */
+#define PR_NOT_CONNECTED_ERROR PR_NSPR_ERROR_BASE + 22
+ /* The call attempted to use connected
+ sematics on a network file descriptor
+ that was not connected. */
+#define PR_LOAD_LIBRARY_ERROR PR_NSPR_ERROR_BASE + 23
+ /* Some sort of failure attempting to load
+ a dynamic library. */
+#define PR_UNLOAD_LIBRARY_ERROR PR_NSPR_ERROR_BASE + 24
+ /* Some sort of failure attempting to unload
+ a dynamic library. */
+#define PR_FIND_SYMBOL_ERROR PR_NSPR_ERROR_BASE + 25
+ /* Dynamic library symbol could not be found
+ in any of the available libraries. */
+#define PR_INSUFFICIENT_RESOURCES_ERROR PR_NSPR_ERROR_BASE + 26
+ /* There are insufficient system resources
+ to process the request. */
+#define PR_DIRECTORY_LOOKUP_ERROR PR_NSPR_ERROR_BASE + 27
+#define PR_TPD_RANGE_ERROR PR_NSPR_ERROR_BASE + 28
+ /* Attempt to access a TPD key that is beyond
+ any key that has been allocated to the
+ process. */
+
+#define PR_PROC_DESC_TABLE_FULL_ERROR PR_NSPR_ERROR_BASE + 29
+#define PR_SYS_DESC_TABLE_FULL_ERROR PR_NSPR_ERROR_BASE + 30
+#define PR_NOT_SOCKET_ERROR PR_NSPR_ERROR_BASE + 31
+#define PR_NOT_TCP_SOCKET_ERROR PR_NSPR_ERROR_BASE + 32
+#define PR_SOCKET_ADDRESS_IS_BOUND_ERROR PR_NSPR_ERROR_BASE + 33
+#define PR_NO_ACCESS_RIGHTS_ERROR PR_NSPR_ERROR_BASE + 34
+#define PR_OPERATION_NOT_SUPPORTED_ERROR PR_NSPR_ERROR_BASE + 35
+#define PR_PROTOCOL_NOT_SUPPORTED_ERROR PR_NSPR_ERROR_BASE + 36
+#define PR_REMOTE_FILE_ERROR PR_NSPR_ERROR_BASE + 37
+#define PR_BUFFER_OVERFLOW_ERROR PR_NSPR_ERROR_BASE + 38
+#define PR_CONNECT_RESET_ERROR PR_NSPR_ERROR_BASE + 39
+#define PR_RANGE_ERROR PR_NSPR_ERROR_BASE + 40
+
+#define PR_DEADLOCK_ERROR PR_NSPR_ERROR_BASE + 41
+#define PR_FILE_IS_LOCKED_ERROR PR_NSPR_ERROR_BASE + 42
+#define PR_FILE_TOO_BIG_ERROR PR_NSPR_ERROR_BASE + 43
+#define PR_NO_DEVICE_SPACE_ERROR PR_NSPR_ERROR_BASE + 44
+#define PR_PIPE_ERROR PR_NSPR_ERROR_BASE + 45
+#define PR_NO_SEEK_DEVICE_ERROR PR_NSPR_ERROR_BASE + 46
+#define PR_IS_DIRECTORY_ERROR PR_NSPR_ERROR_BASE + 47
+#define PR_LOOP_ERROR PR_NSPR_ERROR_BASE + 48
+#define PR_NAME_TOO_LONG_ERROR PR_NSPR_ERROR_BASE + 49
+#define PR_FILE_NOT_FOUND_ERROR PR_NSPR_ERROR_BASE + 50
+#define PR_NOT_DIRECTORY_ERROR PR_NSPR_ERROR_BASE + 51
+#define PR_READ_ONLY_FILESYSTEM_ERROR PR_NSPR_ERROR_BASE + 52
+#define PR_DIRECTORY_NOT_EMPTY_ERROR PR_NSPR_ERROR_BASE + 53
+#define PR_FILESYSTEM_MOUNTED_ERROR PR_NSPR_ERROR_BASE + 54
+#define PR_NOT_SAME_DEVICE_ERROR PR_NSPR_ERROR_BASE + 55
+#define PR_DIRECTORY_CORRUPTED_ERROR PR_NSPR_ERROR_BASE + 56
+#define PR_FILE_EXISTS_ERROR PR_NSPR_ERROR_BASE + 57
+#define PR_MAX_DIRECTORY_ENTRIES_ERROR PR_NSPR_ERROR_BASE + 58
+#define PR_INVALID_DEVICE_STATE_ERROR PR_NSPR_ERROR_BASE + 59
+#define PR_DEVICE_IS_LOCKED_ERROR PR_NSPR_ERROR_BASE + 60
+#define PR_NO_MORE_FILES_ERROR PR_NSPR_ERROR_BASE + 61
+#define PR_END_OF_FILE_ERROR PR_NSPR_ERROR_BASE + 62
+#define PR_FILE_SEEK_ERROR PR_NSPR_ERROR_BASE + 63
+#define PR_FILE_IS_BUSY_ERROR PR_NSPR_ERROR_BASE + 64
+
+#define PR_IN_PROGRESS_ERROR PR_NSPR_ERROR_BASE + 66
+#define PR_ALREADY_INITIATED_ERROR PR_NSPR_ERROR_BASE + 67
+#define PR_GROUP_EMPTY_ERROR PR_NSPR_ERROR_BASE + 68
+#define PR_INVALID_STATE_ERROR PR_NSPR_ERROR_BASE + 69
+#define PR_NETWORK_DOWN_ERROR PR_NSPR_ERROR_BASE + 70
+#define PR_SOCKET_SHUTDOWN_ERROR PR_NSPR_ERROR_BASE + 71
+#define PR_CONNECT_ABORTED_ERROR PR_NSPR_ERROR_BASE + 72
+#define PR_HOST_UNREACHABLE_ERROR PR_NSPR_ERROR_BASE + 73
+#define PR_MAX_ERROR PR_NSPR_ERROR_BASE + 74
+ /* Place holder for the end of the list */
+
+/*
+** Set error will preserve an error condition within a thread context.
+** The values stored are the NSPR (platform independent) translation of
+** the error. Also, if available, the platform specific oserror is stored.
+** If there is no appropriate OS error number, a zero my be supplied.
+*/
+PR_EXTERN(void) PR_SetError(PRErrorCode errorCode, PRInt32 oserr);
+
+/*
+** The text value specified may be NULL. If it is not NULL and the text length
+** is zero, the string is assumed to be a null terminated C string. Otherwise
+** the text is assumed to be the length specified and possibly include NULL
+** characters (e.g., a multi-national string).
+**
+** The text will be copied into to thread structure and remain there
+** until the next call to PR_SetError.
+*/
+PR_EXTERN(void) PR_SetErrorText(
+ PRIntn textLength, const char *text);
+
+/*
+** Return the current threads last set error code.
+*/
+PR_EXTERN(PRErrorCode) PR_GetError(void);
+
+/*
+** Return the current threads last set os error code. This is used for
+** machine specific code that desires the underlying os error.
+*/
+PR_EXTERN(PRInt32) PR_GetOSError(void);
+
+/*
+** Get the length of the error text. If a zero is returned, then there
+** is no text. Otherwise, the value returned is sufficient to contain
+** the error text currently available.
+*/
+PR_EXTERN(PRInt32) PR_GetErrorTextLength(void);
+
+/*
+** Copy the current threads current error text. Then actual number of bytes
+** copied is returned as the result. If the result is zero, the 'text' area
+** is unaffected.
+*/
+PR_EXTERN(PRInt32) PR_GetErrorText(char *text);
+
+
+PR_END_EXTERN_C
+
+#endif /* prerror_h___ */
diff --git a/pr/include/prinet.h b/pr/include/prinet.h
new file mode 100644
index 00000000..149b943e
--- /dev/null
+++ b/pr/include/prinet.h
@@ -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.
+ */
+
+/*
+ * File: prinet.h
+ * Description:
+ * Header file used to find the system header files for socket support.
+ * This file serves the following purposes:
+ * - A cross-platform, "get-everything" socket header file. On
+ * Unix, socket support is scattered in several header files,
+ * while Windows and Mac have a "get-everything" socket header
+ * file.
+ * - NSPR needs the following macro definitions and function
+ * prototype declarations from these header files:
+ * AF_INET
+ * INADDR_ANY, INADDR_LOOPBACK, INADDR_BROADCAST
+ * ntohl(), ntohs(), htonl(), ntons().
+ * NSPR does not define its own versions of these macros and
+ * functions. It simply uses the native versions, which have
+ * the same names on all supported platforms.
+ * This file is intended to be included by nspr20 public header
+ * files, such as prio.h. One should not include this file directly.
+ */
+
+#ifndef prinet_h__
+#define prinet_h__
+
+#if defined(XP_UNIX) || defined(XP_OS2)
+
+#include <sys/types.h>
+#include <sys/socket.h> /* AF_INET */
+#include <netinet/in.h> /* INADDR_ANY, ..., ntohl(), ... */
+#ifdef XP_OS2
+#include <sys/ioctl.h>
+#else
+#include <arpa/inet.h>
+#endif
+#include <netdb.h>
+
+#if defined(FREEBSD) || defined(BSDI)
+#include <rpc/types.h> /* the only place that defines INADDR_LOOPBACK */
+#endif
+
+/*
+ * OS/2 hack. For some reason INADDR_LOOPBACK is not defined in the
+ * socket headers.
+ */
+#if defined(OS2) && !defined(INADDR_LOOPBACK)
+#define INADDR_LOOPBACK gethostid()
+#endif
+
+/*
+ * Prototypes of ntohl() etc. are declared in <machine/endian.h>
+ * on these platforms.
+ */
+#if defined(BSDI) || defined(OSF1)
+#include <machine/endian.h>
+#endif
+
+#elif defined(WIN32)
+
+/* Do not include any system header files. */
+
+#elif defined(WIN16)
+
+#include <winsock.h>
+
+#elif defined(XP_MAC)
+
+#include "macsocket.h"
+
+#else
+
+#error Unknown platform
+
+#endif
+
+#endif /* prinet_h__ */
diff --git a/pr/include/prinit.h b/pr/include/prinit.h
new file mode 100644
index 00000000..b531b85d
--- /dev/null
+++ b/pr/include/prinit.h
@@ -0,0 +1,223 @@
+/* -*- 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 prinit_h___
+#define prinit_h___
+
+#include "prthread.h"
+#include "prtypes.h"
+#include "prwin16.h"
+#include <stdio.h>
+
+PR_BEGIN_EXTERN_C
+
+/************************************************************************/
+/**************************IDENTITY AND VERSIONING***********************/
+/************************************************************************/
+
+/*
+** NSPR's name, this should persist until at least the turn of the
+** century.
+*/
+#define PR_NAME "NSPR"
+
+/*
+** NSPR's version is used to determine the likelihood that the version you
+** used to build your component is anywhere close to being compatible with
+** what is in the underlying library.
+**
+** The format of the version string is
+** "<major version>.<minor version> <build date>"
+*/
+#define PR_VERSION "3.0 19981002"
+#define PR_VMAJOR 3
+#define PR_VMINOR 0
+#define PR_VPATCH 0
+#define PR_BETA PR_FALSE
+
+/*
+** PRVersionCheck
+**
+** The basic signature of the function that is called to provide version
+** checking. The result will be a boolean that indicates the likelihood
+** that the underling library will perform as the caller expects.
+**
+** The only argument is a string, which should be the verson identifier
+** of the library in question. That string will be compared against an
+** equivalent string that represents the actual build version of the
+** exporting library.
+**
+** The result will be the logical union of the directly called library
+** and all dependent libraries.
+*/
+
+typedef PRBool (*PRVersionCheck)(const char*);
+
+/*
+** PR_VersionCheck
+**
+** NSPR's existance proof of the version check function.
+**
+** Note that NSPR has no cooperating dependencies.
+*/
+
+PR_EXTERN(PRBool) PR_VersionCheck(const char *importedVersion);
+
+
+/************************************************************************/
+/*******************************INITIALIZATION***************************/
+/************************************************************************/
+
+/*
+** Initialize the runtime. Attach a thread object to the currently
+** executing native thread of type "type".
+**
+** The specificaiton of 'maxPTDs' is ignored.
+*/
+PR_EXTERN(void) PR_Init(
+ PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs);
+
+/*
+** And alternate form of initialization, one that may become the default if
+** not the only mechanism, provides a method to get the NSPR runtime init-
+** ialized and place NSPR between the caller and the runtime library. This
+** allows main() to be treated as any other thread root function, signalling
+** its compeletion by returning and allowing the runtime to coordinate the
+** completion of the other threads of the runtime.
+**
+** The priority of the main (or primordial) thread will be PR_PRIORITY_NORMAL.
+** The thread may adjust its own priority by using PR_SetPriority(), though
+** at this time the support for priorities is somewhat weak.
+**
+** The specificaiton of 'maxPTDs' is ignored.
+**
+** The value returned by PR_Initialize is the value returned from the root
+** function, 'prmain'.
+*/
+
+typedef PRIntn (PR_CALLBACK *PRPrimordialFn)(PRIntn argc, char **argv);
+
+PR_EXTERN(PRIntn) PR_Initialize(
+ PRPrimordialFn prmain, PRIntn argc, char **argv, PRUintn maxPTDs);
+
+/*
+** Return PR_TRUE if PR_Init has already been called.
+*/
+PR_EXTERN(PRBool) PR_Initialized(void);
+
+/*
+ * Perform a graceful shutdown of NSPR. PR_Cleanup() may be called by
+ * the primordial thread near the end of the main() function.
+ *
+ * PR_Cleanup() attempts to synchronize the natural termination of
+ * process. It does that by blocking the caller, if and only if it is
+ * the primordial thread, until the number of user threads has dropped
+ * to zero. When the primordial thread returns from main(), the process
+ * will immediately and silently exit. That is, it will (if necessary)
+ * forcibly terminate any existing threads and exit without significant
+ * blocking and there will be no error messages or core files.
+ *
+ * PR_Cleanup() returns PR_SUCCESS if NSPR is successfully shutdown,
+ * or PR_FAILURE if the calling thread of this function is not the
+ * primordial thread.
+ */
+PR_EXTERN(PRStatus) PR_Cleanup(void);
+
+/*
+** Disable Interrupts
+** Disables timer signals used for pre-emptive scheduling.
+*/
+PR_EXTERN(void) PR_DisableClockInterrupts(void);
+
+/*
+** Enables Interrupts
+** Enables timer signals used for pre-emptive scheduling.
+*/
+PR_EXTERN(void) PR_EnableClockInterrupts(void);
+
+/*
+** Block Interrupts
+** Blocks the timer signal used for pre-emptive scheduling
+*/
+PR_EXTERN(void) PR_BlockClockInterrupts(void);
+
+/*
+** Unblock Interrupts
+** Unblocks the timer signal used for pre-emptive scheduling
+*/
+PR_EXTERN(void) PR_UnblockClockInterrupts(void);
+
+/*
+** Create extra virtual processor threads. Generally used with MP systems.
+*/
+PR_EXTERN(void) PR_SetConcurrency(PRUintn numCPUs);
+
+/*
+** Control the method and size of the file descriptor (PRFileDesc*)
+** cache used by the runtime. Setting 'high' to zero is for performance,
+** any other value probably for debugging (see memo on FD caching).
+*/
+PR_EXTERN(PRStatus) PR_SetFDCacheSize(PRIntn low, PRIntn high);
+
+/*
+ * Cause an immediate, nongraceful, forced termination of the process.
+ * It takes a PRIntn argument, which is the exit status code of the
+ * process.
+ */
+PR_EXTERN(void) PR_ProcessExit(PRIntn status);
+
+/*
+** Abort the process in a non-graceful manner. This will cause a core file,
+** call to the debugger or other moral equivalent as well as causing the
+** entire process to stop.
+*/
+PR_EXTERN(void) PR_Abort(void);
+
+/*
+ ****************************************************************
+ *
+ * Module initialization:
+ *
+ ****************************************************************
+ */
+
+typedef struct PRCallOnceType {
+ PRIntn initialized;
+ PRInt32 inProgress;
+ PRStatus status;
+} PRCallOnceType;
+
+typedef PRStatus (PR_CALLBACK *PRCallOnceFN)(void);
+
+PR_EXTERN(PRStatus) PR_CallOnce(
+ PRCallOnceType *once,
+ PRCallOnceFN func
+);
+
+
+PR_END_EXTERN_C
+
+#endif /* prinit_h___ */
+
+
+
+
+
+
+
+
diff --git a/pr/include/prinrval.h b/pr/include/prinrval.h
new file mode 100644
index 00000000..f2d5c956
--- /dev/null
+++ b/pr/include/prinrval.h
@@ -0,0 +1,156 @@
+/* -*- 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.h
+** Description: API to interval timing functions of NSPR.
+**
+**
+** NSPR provides interval times that are independent of network time
+** of day values. Interval times are (in theory) accurate regardless
+** of host processing requirements and also very cheap to acquire. It
+** is expected that getting an interval time while in a synchronized
+** function (holding one's lock).
+**/
+
+#if !defined(prinrval_h)
+#define prinrval_h
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+/**********************************************************************/
+/************************* TYPES AND CONSTANTS ************************/
+/**********************************************************************/
+
+typedef PRUint32 PRIntervalTime;
+
+/***********************************************************************
+** DEFINES: PR_INTERVAL_MIN
+** PR_INTERVAL_MAX
+** DESCRIPTION:
+** These two constants define the range (in ticks / second) of the
+** platform dependent type, PRIntervalTime. These constants bound both
+** the period and the resolution of a PRIntervalTime.
+***********************************************************************/
+#define PR_INTERVAL_MIN 1000UL
+#define PR_INTERVAL_MAX 100000UL
+
+/***********************************************************************
+** DEFINES: PR_INTERVAL_NO_WAIT
+** PR_INTERVAL_NO_TIMEOUT
+** DESCRIPTION:
+** Two reserved constants are defined in the PRIntervalTime namespace.
+** They are used to indicate that the process should wait no time (return
+** immediately) or wait forever (never time out), respectively.
+***********************************************************************/
+#define PR_INTERVAL_NO_WAIT 0UL
+#define PR_INTERVAL_NO_TIMEOUT 0xffffffffUL
+
+/**********************************************************************/
+/****************************** FUNCTIONS *****************************/
+/**********************************************************************/
+
+/***********************************************************************
+** FUNCTION: PR_IntervalNow
+** DESCRIPTION:
+** Return the value of NSPR's free running interval timer. That timer
+** can be used to establish epochs and determine intervals (be computing
+** the difference between two times).
+** INPUTS: void
+** OUTPUTS: void
+** RETURN: PRIntervalTime
+**
+** SIDE EFFECTS:
+** None
+** RESTRICTIONS:
+** The units of PRIntervalTime are platform dependent. They are chosen
+** such that they are appropriate for the host OS, yet provide sufficient
+** resolution and period to be useful to clients.
+** MEMORY: N/A
+** ALGORITHM: Platform dependent
+***********************************************************************/
+PR_EXTERN(PRIntervalTime) PR_IntervalNow(void);
+
+/***********************************************************************
+** FUNCTION: PR_TicksPerSecond
+** DESCRIPTION:
+** Return the number of ticks per second for PR_IntervalNow's clock.
+** The value will be in the range [PR_INTERVAL_MIN..PR_INTERVAL_MAX].
+** INPUTS: void
+** OUTPUTS: void
+** RETURN: PRUint32
+**
+** SIDE EFFECTS:
+** None
+** RESTRICTIONS:
+** None
+** MEMORY: N/A
+** ALGORITHM: N/A
+***********************************************************************/
+PR_EXTERN(PRUint32) PR_TicksPerSecond(void);
+
+/***********************************************************************
+** FUNCTION: PR_SecondsToInterval
+** PR_MillisecondsToInterval
+** PR_MicrosecondsToInterval
+** DESCRIPTION:
+** Convert standard clock units to platform dependent intervals.
+** INPUTS: PRUint32
+** OUTPUTS: void
+** RETURN: PRIntervalTime
+**
+** SIDE EFFECTS:
+** None
+** RESTRICTIONS:
+** Conversion may cause overflow, which is not reported.
+** MEMORY: N/A
+** ALGORITHM: N/A
+***********************************************************************/
+PR_EXTERN(PRIntervalTime) PR_SecondsToInterval(PRUint32 seconds);
+PR_EXTERN(PRIntervalTime) PR_MillisecondsToInterval(PRUint32 milli);
+PR_EXTERN(PRIntervalTime) PR_MicrosecondsToInterval(PRUint32 micro);
+
+/***********************************************************************
+** FUNCTION: PR_IntervalToSeconds
+** PR_IntervalToMilliseconds
+** PR_IntervalToMicroseconds
+** DESCRIPTION:
+** Convert platform dependent intervals to standard clock units.
+** INPUTS: PRIntervalTime
+** OUTPUTS: void
+** RETURN: PRUint32
+**
+** SIDE EFFECTS:
+** None
+** RESTRICTIONS:
+** Conversion may cause overflow, which is not reported.
+** MEMORY: N/A
+** ALGORITHM: N/A
+***********************************************************************/
+PR_EXTERN(PRUint32) PR_IntervalToSeconds(PRIntervalTime ticks);
+PR_EXTERN(PRUint32) PR_IntervalToMilliseconds(PRIntervalTime ticks);
+PR_EXTERN(PRUint32) PR_IntervalToMicroseconds(PRIntervalTime ticks);
+
+PR_END_EXTERN_C
+
+
+#endif /* !defined(prinrval_h) */
+
+/* prinrval.h */
diff --git a/pr/include/prio.h b/pr/include/prio.h
new file mode 100644
index 00000000..89ffb871
--- /dev/null
+++ b/pr/include/prio.h
@@ -0,0 +1,1685 @@
+/* -*- 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: prio.h
+ *
+ * Description: PR i/o related stuff, such as file system access, file
+ * i/o, socket i/o, etc.
+ */
+
+#ifndef prio_h___
+#define prio_h___
+
+#include "prlong.h"
+#include "prtime.h"
+#include "prinrval.h"
+#include "prinet.h"
+
+PR_BEGIN_EXTERN_C
+
+/* Typedefs */
+typedef struct PRDir PRDir;
+typedef struct PRDirEntry PRDirEntry;
+typedef struct PRFileDesc PRFileDesc;
+typedef struct PRFileInfo PRFileInfo;
+typedef struct PRFileInfo64 PRFileInfo64;
+typedef union PRNetAddr PRNetAddr;
+typedef struct PRIOMethods PRIOMethods;
+typedef struct PRPollDesc PRPollDesc;
+typedef struct PRFilePrivate PRFilePrivate;
+
+/*
+***************************************************************************
+** The file descriptor.
+** This is the primary structure to represent any active open socket,
+** whether it be a normal file or a network connection. Such objects
+** are stackable (or layerable). Each layer may have its own set of
+** method pointers and context private to that layer. All each layer
+** knows about its neighbors is how to get to their method table.
+***************************************************************************
+*/
+
+typedef PRIntn PRDescIdentity; /* see: Layering file descriptors */
+
+struct PRFileDesc {
+ const PRIOMethods *methods; /* the I/O methods table */
+ PRFilePrivate *secret; /* layer dependent data */
+ PRFileDesc *lower, *higher; /* pointers to adjacent layers */
+ void (PR_CALLBACK *dtor)(PRFileDesc *fd);
+ /* A destructor function for layer */
+ PRDescIdentity identity; /* Identity of this particular layer */
+};
+
+/*
+***************************************************************************
+** PRTransmitFileFlags
+**
+** Flags for PR_TransmitFile. Pass PR_TRANSMITFILE_CLOSE_SOCKET to
+** PR_TransmitFile if the connection should be closed after the file
+** is transmitted.
+***************************************************************************
+*/
+typedef enum PRTransmitFileFlags {
+ PR_TRANSMITFILE_KEEP_OPEN = 0, /* socket is left open after file
+ * is transmitted. */
+ PR_TRANSMITFILE_CLOSE_SOCKET = 1 /* socket is closed after file
+ * is transmitted. */
+} PRTransmitFileFlags;
+
+/*
+**************************************************************************
+** Macros for PRNetAddr
+**
+** Address families: PR_AF_INET, PR_AF_INET6, PR_AF_LOCAL
+** IP addresses: PR_INADDR_ANY, PR_INADDR_LOOPBACK, PR_INADDR_BROADCAST
+**************************************************************************
+*/
+
+#ifdef WIN32
+
+#define PR_AF_INET 2
+#define PR_AF_LOCAL 1
+#define PR_INADDR_ANY (unsigned long)0x00000000
+#define PR_INADDR_LOOPBACK 0x7f000001
+#define PR_INADDR_BROADCAST (unsigned long)0xffffffff
+
+#else /* WIN32 */
+
+#define PR_AF_INET AF_INET
+#define PR_AF_LOCAL AF_UNIX
+#ifdef AF_INET6
+#define PR_AF_INET6 AF_INET6
+#endif
+#define PR_INADDR_ANY INADDR_ANY
+#define PR_INADDR_LOOPBACK INADDR_LOOPBACK
+#define PR_INADDR_BROADCAST INADDR_BROADCAST
+
+#endif /* WIN32 */
+
+/*
+**************************************************************************
+** A network address
+**
+** Only Internet Protocol (IPv4 and IPv6) addresses are supported.
+** The address family must always represent IPv4 (AF_INET, probably == 2)
+** or IPv6 (AF_INET6).
+**************************************************************************
+*************************************************************************/
+#if defined(_PR_INET6)
+
+#if !defined(AF_INET6)
+#error "AF_INET6 is not defined"
+#endif
+
+typedef struct in6_addr PRIPv6Addr;
+
+#endif /* defined(_PR_INET6) */
+
+union PRNetAddr {
+ struct {
+ PRUint16 family; /* address family (0x00ff maskable) */
+ char data[14]; /* raw address data */
+ } raw;
+ struct {
+ PRUint16 family; /* address family (AF_INET) */
+ PRUint16 port; /* port number */
+ PRUint32 ip; /* The actual 32 bits of address */
+ char pad[8];
+ } inet;
+#if defined(_PR_INET6)
+ struct {
+ PRUint16 family; /* address family (AF_INET6) */
+ PRUint16 port; /* port number */
+ PRUint32 flowinfo; /* routing information */
+ PRIPv6Addr ip; /* the actual 128 bits of address */
+ } ipv6;
+#endif /* defined(_PR_INET6) */
+#if defined(XP_UNIX)
+ struct { /* Unix domain socket address */
+ PRUint16 family; /* address family (AF_UNIX) */
+ char path[104]; /* null-terminated pathname */
+ } local;
+#endif
+};
+
+/*
+** The PR_NETADDR_SIZE macro can only be called on a PRNetAddr union
+** whose 'family' field is set. It returns the size of the union
+** member corresponding to the specified address family.
+*/
+
+#if defined(_PR_INET6)
+
+#define PR_NETADDR_SIZE(_addr) PR_NetAddrSize(_addr)
+
+#else
+
+#if defined(XP_UNIX)
+#define PR_NETADDR_SIZE(_addr) \
+ ((_addr)->raw.family == AF_UNIX \
+ ? sizeof((_addr)->local) \
+ : sizeof((_addr)->inet))
+#else
+#define PR_NETADDR_SIZE(_addr) sizeof((_addr)->inet)
+#endif /* defined(XP_UNIX) */
+
+#endif /* defined(_PR_INET6) */
+
+/*
+***************************************************************************
+** PRSockOption
+**
+** The file descriptors can have predefined options set after they file
+** descriptor is created to change their behavior. Only the options in
+** the following enumeration are supported.
+***************************************************************************
+*/
+typedef enum PRSockOption
+{
+ PR_SockOpt_Nonblocking, /* nonblocking io */
+ PR_SockOpt_Linger, /* linger on close if data present */
+ PR_SockOpt_Reuseaddr, /* allow local address reuse */
+ PR_SockOpt_Keepalive, /* keep connections alive */
+ PR_SockOpt_RecvBufferSize, /* send buffer size */
+ PR_SockOpt_SendBufferSize, /* receive buffer size */
+
+ PR_SockOpt_IpTimeToLive, /* time to live */
+ PR_SockOpt_IpTypeOfService, /* type of service and precedence */
+
+ PR_SockOpt_AddMember, /* add an IP group membership */
+ PR_SockOpt_DropMember, /* drop an IP group membership */
+ PR_SockOpt_McastInterface, /* multicast interface address */
+ PR_SockOpt_McastTimeToLive, /* multicast timetolive */
+ PR_SockOpt_McastLoopback, /* multicast loopback */
+
+ PR_SockOpt_NoDelay, /* don't delay send to coalesce packets */
+ PR_SockOpt_MaxSegment, /* maximum segment size */
+ PR_SockOpt_Last
+} PRSockOption;
+
+typedef struct PRLinger {
+ PRBool polarity; /* Polarity of the option's setting */
+ PRIntervalTime linger; /* Time to linger before closing */
+} PRLinger;
+
+typedef struct PRMcastRequest {
+ PRNetAddr mcaddr; /* IP multicast address of group */
+ PRNetAddr ifaddr; /* local IP address of interface */
+} PRMcastRequest;
+
+typedef struct PRSocketOptionData
+{
+ PRSockOption option;
+ union
+ {
+ PRUintn ip_ttl; /* IP time to live */
+ PRUintn mcast_ttl; /* IP multicast time to live */
+ PRUintn tos; /* IP type of service and precedence */
+ PRBool non_blocking; /* Non-blocking (network) I/O */
+ PRBool reuse_addr; /* Allow local address reuse */
+ PRBool keep_alive; /* Keep connections alive */
+ PRBool mcast_loopback; /* IP multicast loopback */
+ PRBool no_delay; /* Don't delay send to coalesce packets */
+ PRSize max_segment; /* Maximum segment size */
+ PRSize recv_buffer_size; /* Receive buffer size */
+ PRSize send_buffer_size; /* Send buffer size */
+ PRLinger linger; /* Time to linger on close if data present */
+ PRMcastRequest add_member; /* add an IP group membership */
+ PRMcastRequest drop_member; /* Drop an IP group membership */
+ PRNetAddr mcast_if; /* multicast interface address */
+ } value;
+} PRSocketOptionData;
+
+/*
+***************************************************************************
+** PRIOVec
+**
+** The I/O vector is used by the write vector method to describe the areas
+** that are affected by the ouput operation.
+***************************************************************************
+*/
+typedef struct PRIOVec {
+ char *iov_base;
+ int iov_len;
+} PRIOVec;
+
+/*
+***************************************************************************
+** Discover what type of socket is being described by the file descriptor.
+***************************************************************************
+*/
+typedef enum PRDescType
+{
+ PR_DESC_FILE = 1,
+ PR_DESC_SOCKET_TCP = 2,
+ PR_DESC_SOCKET_UDP = 3,
+ PR_DESC_LAYERED = 4
+} PRDescType;
+
+typedef enum PRSeekWhence {
+ PR_SEEK_SET = 0,
+ PR_SEEK_CUR = 1,
+ PR_SEEK_END = 2
+} PRSeekWhence;
+
+PR_EXTERN(PRDescType) PR_GetDescType(PRFileDesc *file);
+
+/*
+***************************************************************************
+** PRIOMethods
+**
+** The I/O methods table provides procedural access to the functions of
+** the file descriptor. It is the responsibility of a layer implementor
+** to provide suitable functions at every entry point. If a layer provides
+** no functionality, it should call the next lower(higher) function of the
+** same name (e.g., return fd->lower->method->close(fd->lower));
+**
+** Not all functions are implemented for all types of files. In cases where
+** that is true, the function will return a error indication with an error
+** code of PR_INVALID_METHOD_ERROR.
+***************************************************************************
+*/
+
+typedef PRStatus (PR_CALLBACK *PRCloseFN)(PRFileDesc *fd);
+typedef PRInt32 (PR_CALLBACK *PRReadFN)(PRFileDesc *fd, void *buf, PRInt32 amount);
+typedef PRInt32 (PR_CALLBACK *PRWriteFN)(PRFileDesc *fd, const void *buf, PRInt32 amount);
+typedef PRInt32 (PR_CALLBACK *PRAvailableFN)(PRFileDesc *fd);
+typedef PRInt64 (PR_CALLBACK *PRAvailable64FN)(PRFileDesc *fd);
+typedef PRStatus (PR_CALLBACK *PRFsyncFN)(PRFileDesc *fd);
+typedef PRInt32 (PR_CALLBACK *PRSeekFN)(PRFileDesc *fd, PRInt32 offset, PRSeekWhence how);
+typedef PRInt64 (PR_CALLBACK *PRSeek64FN)(PRFileDesc *fd, PRInt64 offset, PRSeekWhence how);
+typedef PRStatus (PR_CALLBACK *PRFileInfoFN)(PRFileDesc *fd, PRFileInfo *info);
+typedef PRStatus (PR_CALLBACK *PRFileInfo64FN)(PRFileDesc *fd, PRFileInfo64 *info);
+typedef PRInt32 (PR_CALLBACK *PRWritevFN)(
+ PRFileDesc *fd, PRIOVec *iov, PRInt32 size, PRIntervalTime timeout);
+typedef PRStatus (PR_CALLBACK *PRConnectFN)(
+ PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout);
+typedef PRFileDesc* (PR_CALLBACK *PRAcceptFN) (
+ PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout);
+typedef PRStatus (PR_CALLBACK *PRBindFN)(PRFileDesc *fd, const PRNetAddr *addr);
+typedef PRStatus (PR_CALLBACK *PRListenFN)(PRFileDesc *fd, PRIntn backlog);
+typedef PRStatus (PR_CALLBACK *PRShutdownFN)(PRFileDesc *fd, PRIntn how);
+typedef PRInt32 (PR_CALLBACK *PRRecvFN)(
+ PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout);
+typedef PRInt32 (PR_CALLBACK *PRSendFN) (
+ PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout);
+typedef PRInt32 (PR_CALLBACK *PRRecvfromFN)(
+ PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout);
+typedef PRInt32 (PR_CALLBACK *PRSendtoFN)(
+ PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout);
+typedef PRInt16 (PR_CALLBACK *PRPollFN)(
+ PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags);
+typedef PRInt32 (PR_CALLBACK *PRAcceptreadFN)(
+ PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr,
+ void *buf, PRInt32 amount, PRIntervalTime t);
+typedef PRInt32 (PR_CALLBACK *PRTransmitfileFN)(
+ PRFileDesc *sd, PRFileDesc *fd, const void *headers,
+ PRInt32 hlen, PRTransmitFileFlags flags, PRIntervalTime t);
+typedef PRStatus (PR_CALLBACK *PRGetsocknameFN)(PRFileDesc *fd, PRNetAddr *addr);
+typedef PRStatus (PR_CALLBACK *PRGetpeernameFN)(PRFileDesc *fd, PRNetAddr *addr);
+typedef PRStatus (PR_CALLBACK *PRGetsockoptFN)( /* OBSOLETE */
+ PRFileDesc *fd, PRSockOption optname, void* optval, PRInt32 *optlen);
+typedef PRStatus (PR_CALLBACK *PRSetsockoptFN)( /* OBSOLETE */
+ PRFileDesc *fd, PRSockOption optname, const void* optval, PRInt32 optlen);
+typedef PRStatus (PR_CALLBACK *PRGetsocketoptionFN)(
+ PRFileDesc *fd, PRSocketOptionData *data);
+typedef PRStatus (PR_CALLBACK *PRSetsocketoptionFN)(
+ PRFileDesc *fd, const PRSocketOptionData *data);
+
+struct PRIOMethods {
+ PRDescType file_type; /* Type of file represented (tos) */
+ PRCloseFN close; /* close file and destroy descriptor */
+ PRReadFN read; /* read up to specified bytes into buffer */
+ PRWriteFN write; /* write specified bytes from buffer */
+ PRAvailableFN available; /* determine number of bytes available */
+ PRAvailable64FN available64; /* ditto, 64 bit */
+ PRFsyncFN fsync; /* flush all buffers to permanent store */
+ PRSeekFN seek; /* position the file to the desired place */
+ PRSeek64FN seek64; /* ditto, 64 bit */
+ PRFileInfoFN fileInfo; /* Get information about an open file */
+ PRFileInfo64FN fileInfo64; /* ditto, 64 bit */
+ PRWritevFN writev; /* Write segments as described by iovector */
+ PRConnectFN connect; /* Connect to the specified (net) address */
+ PRAcceptFN accept; /* Accept a connection for a (net) peer */
+ PRBindFN bind; /* Associate a (net) address with the fd */
+ PRListenFN listen; /* Prepare to listen for (net) connections */
+ PRShutdownFN shutdown; /* Shutdown a (net) connection */
+ PRRecvFN recv; /* Solicit up the the specified bytes */
+ PRSendFN send; /* Send all the bytes specified */
+ PRRecvfromFN recvfrom; /* Solicit (net) bytes and report source */
+ PRSendtoFN sendto; /* Send bytes to (net) address specified */
+ PRPollFN poll; /* Test the fd to see if it is ready */
+ PRAcceptreadFN acceptread; /* Accept and read on a new (net) fd */
+ PRTransmitfileFN transmitfile; /* Transmit at entire file */
+ PRGetsocknameFN getsockname; /* Get (net) address associated with fd */
+ PRGetpeernameFN getpeername; /* Get peer's (net) address */
+ PRGetsockoptFN getsockopt; /* OBSOLETE */
+ PRSetsockoptFN setsockopt; /* OBSOLETE */
+ PRGetsocketoptionFN getsocketoption;
+ /* Get current setting of specified option */
+ PRSetsocketoptionFN setsocketoption;
+ /* Set value of specified option */
+};
+
+/*
+ **************************************************************************
+ * FUNCTION: PR_GetSpecialFD
+ * DESCRIPTION: Get the file descriptor that represents the standard input,
+ * output, or error stream.
+ * INPUTS:
+ * PRSpecialFD id
+ * A value indicating the type of stream desired:
+ * PR_StandardInput: standard input
+ * PR_StandardOuput: standard output
+ * PR_StandardError: standard error
+ * OUTPUTS: none
+ * RETURNS: PRFileDesc *
+ * If the argument is valid, PR_GetSpecialFD returns a file descriptor
+ * that represents the corresponding standard I/O stream. Otherwise,
+ * PR_GetSpecialFD returns NULL and sets error PR_INVALID_ARGUMENT_ERROR.
+ **************************************************************************
+ */
+
+typedef enum PRSpecialFD
+{
+ PR_StandardInput, /* standard input */
+ PR_StandardOutput, /* standard output */
+ PR_StandardError /* standard error */
+} PRSpecialFD;
+
+PR_EXTERN(PRFileDesc*) PR_GetSpecialFD(PRSpecialFD id);
+
+#define PR_STDIN PR_GetSpecialFD(PR_StandardInput)
+#define PR_STDOUT PR_GetSpecialFD(PR_StandardOutput)
+#define PR_STDERR PR_GetSpecialFD(PR_StandardError)
+
+/*
+ **************************************************************************
+ * Layering file descriptors
+ *
+ * File descriptors may be layered. Each layer has it's own identity.
+ * Identities are allocated by the runtime and are to be associated
+ * (by the layer implementor) with all layers that are of that type.
+ * It is then possible to scan the chain of layers and find a layer
+ * that one recongizes and therefore predict that it will implement
+ * a desired protocol.
+ *
+ * There are three well-known identities:
+ * PR_INVALID_IO_LAYER => an invalid layer identity, for error return
+ * PR_TOP_IO_LAYER => the identity of the top of the stack
+ * PR_NSPR_IO_LAYER => the identity used by NSPR proper
+ * PR_TOP_IO_LAYER may be used as a shorthand for identifying the topmost
+ * layer of an existing stack. Ie., the following two constructs are
+ * equivalent.
+ *
+ * rv = PR_PushIOLayer(stack, PR_TOP_IO_LAYER, my_layer);
+ * rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), my_layer)
+ *
+ * A string may be associated with the creation of the identity. It
+ * will be copied by the runtime. If queried the runtime will return
+ * a reference to that copied string (not yet another copy). There
+ * is no facility for deleting an identity.
+ **************************************************************************
+ */
+
+#define PR_INVALID_IO_LAYER (PRDescIdentity)-1
+#define PR_TOP_IO_LAYER (PRDescIdentity)-2
+#define PR_NSPR_IO_LAYER (PRDescIdentity)0
+
+PR_EXTERN(PRDescIdentity) PR_GetUniqueIdentity(const char *layer_name);
+PR_EXTERN(const char*) PR_GetNameForIdentity(PRDescIdentity ident);
+PR_EXTERN(PRDescIdentity) PR_GetLayersIdentity(PRFileDesc* fd);
+PR_EXTERN(PRFileDesc*) PR_GetIdentitiesLayer(PRFileDesc* stack, PRDescIdentity id);
+
+/*
+ **************************************************************************
+ * PR_GetDefaultIOMethods: Accessing the default methods table.
+ * You may get a pointer to the default methods table by calling this function.
+ * You may then select any elements from that table with which to build your
+ * layer's methods table. You may NOT modify the table directly.
+ **************************************************************************
+ */
+PR_EXTERN(const PRIOMethods *) PR_GetDefaultIOMethods(void);
+
+/*
+ **************************************************************************
+ * Creating a layer
+ *
+ * A new layer may be allocated by calling PR_CreateIOLayerStub(). The
+ * file descriptor returned will contain the pointer to the methods table
+ * provided. The runtime will not modify the table nor test its correctness.
+ **************************************************************************
+ */
+PR_EXTERN(PRFileDesc*) PR_CreateIOLayerStub(
+ PRDescIdentity ident, const PRIOMethods *methods);
+
+/*
+ **************************************************************************
+ * Pushing a layer
+ *
+ * A file descriptor (perhaps allocated using PR_CreateIOLayerStub()) may
+ * be pushed into an existing stack of file descriptors at any point the
+ * caller deems appropriate. The new layer will be inserted into the stack
+ * just above the layer with the indicated identity.
+ *
+ * Note: Even if the identity parameter indicates the top-most layer of
+ * the stack, the value of the file descriptor describing the original
+ * stack will not change.
+ **************************************************************************
+ */
+PR_EXTERN(PRStatus) PR_PushIOLayer(
+ PRFileDesc *stack, PRDescIdentity id, PRFileDesc *layer);
+
+/*
+ **************************************************************************
+ * Popping a layer
+ *
+ * A layer may be popped from a stack by indicating the identity of the
+ * layer to be removed. If found, a pointer to the removed object will
+ * be returned to the caller. The object then becomes the responsibility
+ * of the caller.
+ *
+ * Note: Even if the identity indicates the top layer of the stack, the
+ * reference returned will not be the file descriptor for the stack and
+ * that file descriptor will remain valid.
+ **************************************************************************
+ */
+PR_EXTERN(PRFileDesc*) PR_PopIOLayer(PRFileDesc *stack, PRDescIdentity id);
+
+/*
+ **************************************************************************
+ * FUNCTION: PR_Open
+ * DESCRIPTION: Open a file for reading, writing, or both.
+ * INPUTS:
+ * const char *name
+ * The path name of the file to be opened
+ * PRIntn flags
+ * The file status flags.
+ * It is a bitwise OR of the following bit flags (only one of
+ * the first three flags below may be used):
+ * PR_RDONLY Open for reading only.
+ * PR_WRONLY Open for writing only.
+ * PR_RDWR Open for reading and writing.
+ * PR_CREATE_FILE If the file does not exist, the file is created
+ * If the file exists, this flag has no effect.
+ * PR_SYNC If set, each write will wait for both the file data
+ * and file status to be physically updated.
+ * PR_APPEND The file pointer is set to the end of
+ * the file prior to each write.
+ * PR_TRUNCATE If the file exists, its length is truncated to 0.
+ *
+ * PRIntn mode
+ * The access permission bits of the file mode, if the file is
+ * created when PR_CREATE_FILE is on.
+ * OUTPUTS: None
+ * RETURNS: PRFileDesc *
+ * If the file is successfully opened,
+ * returns a pointer to the PRFileDesc
+ * created for the newly opened file.
+ * Returns a NULL pointer if the open
+ * failed.
+ * SIDE EFFECTS:
+ * RESTRICTIONS:
+ * MEMORY:
+ * The return value, if not NULL, points to a dynamically allocated
+ * PRFileDesc object.
+ * ALGORITHM:
+ **************************************************************************
+ */
+
+PR_EXTERN(PRFileDesc*) PR_Open(const char *name, PRIntn flags, PRIntn mode);
+/* Open flags */
+#define PR_RDONLY 0x01
+#define PR_WRONLY 0x02
+#define PR_RDWR 0x04
+#define PR_CREATE_FILE 0x08
+#define PR_APPEND 0x10
+#define PR_TRUNCATE 0x20
+#define PR_SYNC 0x40
+
+/*
+** File modes ....
+**
+** CAVEAT: 'mode' is currently only applicable on UNIX platforms. We are
+** still in the process of seeing how these apply to other file systems.
+**
+** 00400 Read by owner.
+** 00200 Write by owner.
+** 00100 Execute (search if a directory) by owner.
+** 00040 Read by group.
+** 00020 Write by group.
+** 00010 Execute by group.
+** 00004 Read by others.
+** 00002 Write by others
+** 00001 Execute by others.
+**
+*/
+
+/*
+ **************************************************************************
+ * FUNCTION: PR_Close
+ * DESCRIPTION:
+ * Close a file or socket.
+ * INPUTS:
+ * PRFileDesc *fd
+ * a pointer to a PRFileDesc.
+ * OUTPUTS:
+ * None.
+ * RETURN:
+ * PRStatus
+ * SIDE EFFECTS:
+ * RESTRICTIONS:
+ * None.
+ * MEMORY:
+ * The dynamic memory pointed to by the argument fd is freed.
+ **************************************************************************
+ */
+
+PR_EXTERN(PRStatus) PR_Close(PRFileDesc *fd);
+
+/*
+ **************************************************************************
+ * FUNCTION: PR_Read
+ * DESCRIPTION:
+ * Read bytes from a file or socket.
+ * The operation will block until either an end of stream indication is
+ * encountered, some positive number of bytes are transferred, or there
+ * is an error. No more than 'amount' bytes will be transferred.
+ * INPUTS:
+ * PRFileDesc *fd
+ * pointer to the PRFileDesc object for the file or socket
+ * void *buf
+ * pointer to a buffer to hold the data read in.
+ * PRInt32 amount
+ * the size of 'buf' (in bytes)
+ * OUTPUTS:
+ * RETURN:
+ * PRInt32
+ * a positive number indicates the number of bytes actually read in.
+ * 0 means end of file is reached or the network connection is closed.
+ * -1 indicates a failure. The reason for the failure is obtained
+ * by calling PR_GetError().
+ * SIDE EFFECTS:
+ * data is written into the buffer pointed to by 'buf'.
+ * RESTRICTIONS:
+ * None.
+ * MEMORY:
+ * N/A
+ * ALGORITHM:
+ * N/A
+ **************************************************************************
+ */
+
+PR_EXTERN(PRInt32) PR_Read(PRFileDesc *fd, void *buf, PRInt32 amount);
+
+/*
+ ***************************************************************************
+ * FUNCTION: PR_Write
+ * DESCRIPTION:
+ * Write a specified number of bytes to a file or socket. The thread
+ * invoking this function blocks until all the data is written.
+ * INPUTS:
+ * PRFileDesc *fd
+ * pointer to a PRFileDesc object that refers to a file or socket
+ * const void *buf
+ * pointer to the buffer holding the data
+ * PRInt32 amount
+ * amount of data in bytes to be written from the buffer
+ * OUTPUTS:
+ * None.
+ * RETURN: PRInt32
+ * A positive number indicates the number of bytes successfully written.
+ * A -1 is an indication that the operation failed. The reason
+ * for the failure is obtained by calling PR_GetError().
+ ***************************************************************************
+ */
+
+PR_EXTERN(PRInt32) PR_Write(PRFileDesc *fd,const void *buf,PRInt32 amount);
+
+/*
+ ***************************************************************************
+ * FUNCTION: PR_Writev
+ * DESCRIPTION:
+ * Write data to a socket. The data is organized in a PRIOVec array. The
+ * operation will block until all the data is written or the operation
+ * fails.
+ * INPUTS:
+ * PRFileDesc *fd
+ * Pointer that points to a PRFileDesc object for a socket.
+ * PRIOVec *iov
+ * An array of PRIOVec. PRIOVec is a struct with the following
+ * two fields:
+ * char *iov_base;
+ * int iov_len;
+ * PRInt32 iov_size
+ * Number of elements in the iov array. The value of this
+ * argument must not be greater than PR_MAX_IOVECTOR_SIZE.
+ * If it is, the method will fail (PR_BUFFER_OVERFLOW_ERROR).
+ * PRIntervalTime timeout
+ * Time limit for completion of the entire write operation.
+ * OUTPUTS:
+ * None
+ * RETURN:
+ * A positive number indicates the number of bytes successfully written.
+ * A -1 is an indication that the operation failed. The reason
+ * for the failure is obtained by calling PR_GetError().
+ ***************************************************************************
+ */
+
+#define PR_MAX_IOVECTOR_SIZE 16 /* 'size' must be <= */
+
+PR_EXTERN(PRInt32) PR_Writev(
+ PRFileDesc *fd, PRIOVec *iov, PRInt32 size, PRIntervalTime timeout);
+
+/*
+ ***************************************************************************
+ * FUNCTION: PR_Delete
+ * DESCRIPTION:
+ * Delete a file from the filesystem. The operation may fail if the
+ * file is open.
+ * INPUTS:
+ * const char *name
+ * Path name of the file to be deleted.
+ * OUTPUTS:
+ * None.
+ * RETURN: PRStatus
+ * The function returns PR_SUCCESS if the file is successfully
+ * deleted, otherwise it returns PR_FAILURE.
+ ***************************************************************************
+ */
+
+PR_EXTERN(PRStatus) PR_Delete(const char *name);
+
+/**************************************************************************/
+
+typedef enum PRFileType
+{
+ PR_FILE_FILE = 1,
+ PR_FILE_DIRECTORY = 2,
+ PR_FILE_OTHER = 3
+} PRFileType;
+
+struct PRFileInfo {
+ PRFileType type; /* Type of file */
+ PRUint32 size; /* Size, in bytes, of file's contents */
+ PRTime creationTime; /* Creation time per definition of PRTime */
+ PRTime modifyTime; /* Last modification time per definition of PRTime */
+};
+
+struct PRFileInfo64 {
+ PRFileType type; /* Type of file */
+ PRUint64 size; /* Size, in bytes, of file's contents */
+ PRTime creationTime; /* Creation time per definition of PRTime */
+ PRTime modifyTime; /* Last modification time per definition of PRTime */
+};
+
+/****************************************************************************
+ * FUNCTION: PR_GetFileInfo, PR_GetFileInfo64
+ * DESCRIPTION:
+ * Get the information about the file with the given path name. This is
+ * applicable only to NSFileDesc describing 'file' types (see
+ * INPUTS:
+ * const char *fn
+ * path name of the file
+ * OUTPUTS:
+ * PRFileInfo *info
+ * Information about the given file is written into the file
+ * information object pointer to by 'info'.
+ * RETURN: PRStatus
+ * PR_GetFileInfo returns PR_SUCCESS if file information is successfully
+ * obtained, otherwise it returns PR_FAILURE.
+ ***************************************************************************
+ */
+
+PR_EXTERN(PRStatus) PR_GetFileInfo(const char *fn, PRFileInfo *info);
+PR_EXTERN(PRStatus) PR_GetFileInfo64(const char *fn, PRFileInfo64 *info);
+
+/*
+ **************************************************************************
+ * FUNCTION: PR_GetOpenFileInfo, PR_GetOpenFileInfo64
+ * DESCRIPTION:
+ * Get information about an open file referred to by the
+ * given PRFileDesc object.
+ * INPUTS:
+ * const PRFileDesc *fd
+ * A reference to a valid, open file.
+ * OUTPUTS:
+ * Same as PR_GetFileInfo, PR_GetFileInfo64
+ * RETURN: PRStatus
+ * PR_GetFileInfo returns PR_SUCCESS if file information is successfully
+ * obtained, otherwise it returns PR_FAILURE.
+ ***************************************************************************
+ */
+
+PR_EXTERN(PRStatus) PR_GetOpenFileInfo(PRFileDesc *fd, PRFileInfo *info);
+PR_EXTERN(PRStatus) PR_GetOpenFileInfo64(PRFileDesc *fd, PRFileInfo64 *info);
+
+/*
+ **************************************************************************
+ * FUNCTION: PR_Rename
+ * DESCRIPTION:
+ * Rename a file from the old name 'from' to the new name 'to'.
+ * INPUTS:
+ * const char *from
+ * The old name of the file to be renamed.
+ * const char *to
+ * The new name of the file.
+ * OUTPUTS:
+ * None.
+ * RETURN: PRStatus
+ **************************************************************************
+ */
+
+PR_EXTERN(PRStatus) PR_Rename(const char *from, const char *to);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_Access
+ * DESCRIPTION:
+ * Determine accessibility of a file.
+ * INPUTS:
+ * const char *name
+ * path name of the file
+ * PRAccessHow how
+ * specifies which access permission to check for.
+ * It can be one of the following values:
+ * PR_ACCESS_READ_OK Test for read permission
+ * PR_ACCESS_WRITE_OK Test for write permission
+ * PR_ACCESS_EXISTS Check existence of file
+ * OUTPUTS:
+ * None.
+ * RETURN: PRStatus
+ * PR_SUCCESS is returned if the requested access is permitted.
+ * Otherwise, PR_FAILURE is returned. Additional information
+ * regarding the reason for the failure may be retrieved from
+ * PR_GetError().
+ *************************************************************************
+ */
+
+typedef enum PRAccessHow {
+ PR_ACCESS_EXISTS = 1,
+ PR_ACCESS_WRITE_OK = 2,
+ PR_ACCESS_READ_OK = 3
+} PRAccessHow;
+
+PR_EXTERN(PRStatus) PR_Access(const char *name, PRAccessHow how);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_Seek, PR_Seek64
+ * DESCRIPTION:
+ * Moves read-write file offset
+ * INPUTS:
+ * PRFileDesc *fd
+ * Pointer to a PRFileDesc object.
+ * PRInt32, PRInt64 offset
+ * Specifies a value, in bytes, that is used in conjunction
+ * with the 'whence' parameter to set the file pointer. A
+ * negative value causes seeking in the reverse direction.
+ * PRSeekWhence whence
+ * Specifies how to interpret the 'offset' parameter in setting
+ * the file pointer associated with the 'fd' parameter.
+ * Values for the 'whence' parameter are:
+ * PR_SEEK_SET Sets the file pointer to the value of the
+ * 'offset' parameter
+ * PR_SEEK_CUR Sets the file pointer to its current location
+ * plus the value of the offset parameter.
+ * PR_SEEK_END Sets the file pointer to the size of the
+ * file plus the value of the offset parameter.
+ * OUTPUTS:
+ * None.
+ * RETURN: PRInt32, PRInt64
+ * Upon successful completion, the resulting pointer location,
+ * measured in bytes from the beginning of the file, is returned.
+ * If the PR_Seek() function fails, the file offset remains
+ * unchanged, and the returned value is -1. The error code can
+ * then be retrieved via PR_GetError().
+ *************************************************************************
+ */
+
+PR_EXTERN(PRInt32) PR_Seek(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence);
+PR_EXTERN(PRInt64) PR_Seek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence);
+
+/*
+ ************************************************************************
+ * FUNCTION: PR_Available
+ * DESCRIPTION:
+ * Determine the amount of data in bytes available for reading
+ * in the given file or socket.
+ * INPUTS:
+ * PRFileDesc *fd
+ * Pointer to a PRFileDesc object that refers to a file or
+ * socket.
+ * OUTPUTS:
+ * None
+ * RETURN: PRInt32, PRInt64
+ * Upon successful completion, PR_Available returns the number of
+ * bytes beyond the current read pointer that is available for
+ * reading. Otherwise, it returns a -1 and the reason for the
+ * failure can be retrieved via PR_GetError().
+ ************************************************************************
+ */
+
+PR_EXTERN(PRInt32) PR_Available(PRFileDesc *fd);
+PR_EXTERN(PRInt64) PR_Available64(PRFileDesc *fd);
+
+/*
+ ************************************************************************
+ * FUNCTION: PR_Sync
+ * DESCRIPTION:
+ * Sync any buffered data for a fd to its backing device (disk).
+ * INPUTS:
+ * PRFileDesc *fd
+ * Pointer to a PRFileDesc object that refers to a file or
+ * socket
+ * OUTPUTS:
+ * None
+ * RETURN: PRStatus
+ * PR_SUCCESS is returned if the requested access is permitted.
+ * Otherwise, PR_FAILURE is returned.
+ ************************************************************************
+ */
+
+PR_EXTERN(PRStatus) PR_Sync(PRFileDesc *fd);
+
+/************************************************************************/
+
+struct PRDirEntry {
+ const char *name; /* name of entry, relative to directory name */
+};
+
+#if !defined(NO_NSPR_10_SUPPORT)
+#define PR_DirName(dirEntry) (dirEntry->name)
+#endif
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_OpenDir
+ * DESCRIPTION:
+ * Open the directory by the given name
+ * INPUTS:
+ * const char *name
+ * path name of the directory to be opened
+ * OUTPUTS:
+ * None
+ * RETURN: PRDir *
+ * If the directory is sucessfully opened, a PRDir object is
+ * dynamically allocated and a pointer to it is returned.
+ * If the directory cannot be opened, a NULL pointer is returned.
+ * MEMORY:
+ * Upon successful completion, the return value points to
+ * dynamically allocated memory.
+ *************************************************************************
+ */
+
+PR_EXTERN(PRDir*) PR_OpenDir(const char *name);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_ReadDir
+ * DESCRIPTION:
+ * INPUTS:
+ * PRDir *dir
+ * pointer to a PRDir object that designates an open directory
+ * PRDirFlags flags
+ * PR_SKIP_NONE Do not skip any files
+ * PR_SKIP_DOT Skip the directory entry "." that
+ * represents the current directory
+ * PR_SKIP_DOT_DOT Skip the directory entry ".." that
+ * represents the parent directory.
+ * PR_SKIP_BOTH Skip both '.' and '..'
+ * PR_SKIP_HIDDEN Skip hidden files
+ * OUTPUTS:
+ * RETURN: PRDirEntry*
+ * Returns a pointer to the next entry in the directory. Returns
+ * a NULL pointer upon reaching the end of the directory or when an
+ * error occurs. The actual reason can be retrieved via PR_GetError().
+ *************************************************************************
+ */
+
+typedef enum PRDirFlags {
+ PR_SKIP_NONE = 0x0,
+ PR_SKIP_DOT = 0x1,
+ PR_SKIP_DOT_DOT = 0x2,
+ PR_SKIP_BOTH = 0x3,
+ PR_SKIP_HIDDEN = 0x4
+} PRDirFlags;
+
+PR_EXTERN(PRDirEntry*) PR_ReadDir(PRDir *dir, PRDirFlags flags);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_CloseDir
+ * DESCRIPTION:
+ * Close the specified directory.
+ * INPUTS:
+ * PRDir *dir
+ * The directory to be closed.
+ * OUTPUTS:
+ * None
+ * RETURN: PRStatus
+ * If successful, will return a status of PR_SUCCESS. Otherwise
+ * a value of PR_FAILURE. The reason for the failure may be re-
+ * trieved using PR_GetError().
+ *************************************************************************
+ */
+
+PR_EXTERN(PRStatus) PR_CloseDir(PRDir *dir);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_MkDir
+ * DESCRIPTION:
+ * Create a new directory with the given name and access mode.
+ * INPUTS:
+ * const char *name
+ * The name of the directory to be created. All the path components
+ * up to but not including the leaf component must already exist.
+ * PRIntn mode
+ * See 'mode' definiton in PR_Open().
+ * OUTPUTS:
+ * None
+ * RETURN: PRStatus
+ * If successful, will return a status of PR_SUCCESS. Otherwise
+ * a value of PR_FAILURE. The reason for the failure may be re-
+ * trieved using PR_GetError().
+ *************************************************************************
+ */
+
+PR_EXTERN(PRStatus) PR_MkDir(const char *name, PRIntn mode);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_RmDir
+ * DESCRIPTION:
+ * Remove a directory by the given name.
+ * INPUTS:
+ * const char *name
+ * The name of the directory to be removed. All the path components
+ * must already exist. Only the leaf component will be removed.
+ * OUTPUTS:
+ * None
+ * RETURN: PRStatus
+ * If successful, will return a status of PR_SUCCESS. Otherwise
+ * a value of PR_FAILURE. The reason for the failure may be re-
+ * trieved using PR_GetError().
+ **************************************************************************
+ */
+
+PR_EXTERN(PRStatus) PR_RmDir(const char *name);
+
+PR_EXTERN(PRUintn) PR_NetAddrSize(const PRNetAddr* addr);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_NewUDPSocket
+ * DESCRIPTION:
+ * Create a new UDP network connection.
+ * INPUTS:
+ * None
+ * OUTPUTS:
+ * None
+ * RETURN: PRFileDesc*
+ * Upon successful completion, PR_NewUDPSocket returns a pointer
+ * to the PRFileDesc created for the newly opened UDP socket.
+ * Returns a NULL pointer if the create of a new UDP connection failed.
+ *
+ **************************************************************************
+ */
+
+PR_EXTERN(PRFileDesc*) PR_NewUDPSocket(void);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_NewTCPSocket
+ * DESCRIPTION:
+ * Create a new TCP network connection.
+ * INPUTS:
+ * None
+ * OUTPUTS:
+ * None
+ * RETURN: PRFileDesc*
+ * Upon successful completion, PR_NewTCPSocket returns a pointer
+ * to the PRFileDesc created for the newly opened TCP socket.
+ * Returns a NULL pointer if the create of a new TCP connection failed.
+ *
+ **************************************************************************
+ */
+
+PR_EXTERN(PRFileDesc*) PR_NewTCPSocket(void);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_Connect
+ * DESCRIPTION:
+ * Initiate a connection on a socket.
+ * INPUTS:
+ * PRFileDesc *fd
+ * Points to a PRFileDesc object representing a socket
+ * PRNetAddr *addr
+ * Specifies the address of the socket in its own communication
+ * space.
+ * PRIntervalTime timeout
+ * Time limit for completion of the connect operation.
+ * OUTPUTS:
+ * None
+ * RETURN: PRStatus
+ * Upon successful completion of connection initiation, PR_Connect
+ * returns PR_SUCCESS. Otherwise, it returns PR_FAILURE. Further
+ * failure information can be obtained by calling PR_GetError().
+ **************************************************************************
+ */
+
+PR_EXTERN(PRStatus) PR_Connect(
+ PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_GetConnectStatus
+ * DESCRIPTION:
+ * Get the completion status of a nonblocking connect. After
+ * a nonblocking connect is initiated with PR_Connect() (which
+ * fails with PR_IN_PROGRESS_ERROR), one should call PR_Poll()
+ * on the socket, with the in_flags PR_POLL_WRITE | PR_POLL_EXCEPT.
+ * When PR_Poll() returns, one calls PR_GetConnectStatus on the
+ * PRPollDesc structure to determine whether the nonblocking
+ * connect has succeeded or failed.
+ * INPUTS:
+ * const PRPollDesc *pd
+ * Pointer to a PRPollDesc whose fd member is the socket,
+ * and in_flags must contain PR_POLL_WRITE and PR_POLL_EXCEPT.
+ * PR_Poll() should have been called and set the out_flags.
+ * RETURN: PRStatus
+ * If the nonblocking connect has successfully completed,
+ * PR_GetConnectStatus returns PR_SUCCESS. If PR_GetConnectStatus()
+ * returns PR_FAILURE, call PR_GetError():
+ * - PR_IN_PROGRESS_ERROR: the nonblocking connect is still in
+ * progress and has not completed yet.
+ * - Other errors: the nonblocking connect has failed with this
+ * error code.
+ */
+
+PR_EXTERN(PRStatus) PR_GetConnectStatus(const PRPollDesc *pd);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_Accept
+ * DESCRIPTION:
+ * Accept a connection on a socket.
+ * INPUTS:
+ * PRFileDesc *fd
+ * Points to a PRFileDesc object representing the rendezvous socket
+ * on which the caller is willing to accept new connections.
+ * PRIntervalTime timeout
+ * Time limit for completion of the accept operation.
+ * OUTPUTS:
+ * PRNetAddr *addr
+ * Returns the address of the connecting entity in its own
+ * communication space. It may be NULL.
+ * RETURN: PRFileDesc*
+ * Upon successful acceptance of a connection, PR_Accept
+ * returns a valid file descriptor. Otherwise, it returns NULL.
+ * Further failure information can be obtained by calling PR_GetError().
+ **************************************************************************
+ */
+
+PR_EXTERN(PRFileDesc*) PR_Accept(
+ PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_Bind
+ * DESCRIPTION:
+ * Bind an address to a socket.
+ * INPUTS:
+ * PRFileDesc *fd
+ * Points to a PRFileDesc object representing a socket.
+ * PRNetAddr *addr
+ * Specifies the address to which the socket will be bound.
+ * OUTPUTS:
+ * None
+ * RETURN: PRStatus
+ * Upon successful binding of an address to a socket, PR_Bind
+ * returns PR_SUCCESS. Otherwise, it returns PR_FAILURE. Further
+ * failure information can be obtained by calling PR_GetError().
+ **************************************************************************
+ */
+
+PR_EXTERN(PRStatus) PR_Bind(PRFileDesc *fd, const PRNetAddr *addr);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_Listen
+ * DESCRIPTION:
+ * Listen for connections on a socket.
+ * INPUTS:
+ * PRFileDesc *fd
+ * Points to a PRFileDesc object representing a socket that will be
+ * used to listen for new connections.
+ * PRIntn backlog
+ * Specifies the maximum length of the queue of pending connections.
+ * OUTPUTS:
+ * None
+ * RETURN: PRStatus
+ * Upon successful completion of listen request, PR_Listen
+ * returns PR_SUCCESS. Otherwise, it returns PR_FAILURE. Further
+ * failure information can be obtained by calling PR_GetError().
+ **************************************************************************
+ */
+
+PR_EXTERN(PRStatus) PR_Listen(PRFileDesc *fd, PRIntn backlog);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_Shutdown
+ * DESCRIPTION:
+ * Shut down part of a full-duplex connection on a socket.
+ * INPUTS:
+ * PRFileDesc *fd
+ * Points to a PRFileDesc object representing a connected socket.
+ * PRIntn how
+ * Specifies the kind of disallowed operations on the socket.
+ * PR_SHUTDOWN_RCV - Further receives will be disallowed
+ * PR_SHUTDOWN_SEND - Further sends will be disallowed
+ * PR_SHUTDOWN_BOTH - Further sends and receives will be disallowed
+ * OUTPUTS:
+ * None
+ * RETURN: PRStatus
+ * Upon successful completion of shutdown request, PR_Shutdown
+ * returns PR_SUCCESS. Otherwise, it returns PR_FAILURE. Further
+ * failure information can be obtained by calling PR_GetError().
+ **************************************************************************
+ */
+
+typedef enum PRShutdownHow
+{
+ PR_SHUTDOWN_RCV = 0, /* disallow further receives */
+ PR_SHUTDOWN_SEND = 1, /* disallow further sends */
+ PR_SHUTDOWN_BOTH = 2 /* disallow further receives and sends */
+} PRShutdownHow;
+
+PR_EXTERN(PRStatus) PR_Shutdown(PRFileDesc *fd, PRShutdownHow how);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_Recv
+ * DESCRIPTION:
+ * Receive a specified number of bytes from a connected socket.
+ * The operation will block until some positive number of bytes are
+ * transferred, a time out has occurred, or there is an error.
+ * No more than 'amount' bytes will be transferred.
+ * INPUTS:
+ * PRFileDesc *fd
+ * points to a PRFileDesc object representing a socket.
+ * void *buf
+ * pointer to a buffer to hold the data received.
+ * PRInt32 amount
+ * the size of 'buf' (in bytes)
+ * PRIntn flags
+ * (OBSOLETE - must always be zero)
+ * PRIntervalTime timeout
+ * Time limit for completion of the receive operation.
+ * OUTPUTS:
+ * None
+ * RETURN: PRInt32
+ * a positive number indicates the number of bytes actually received.
+ * 0 means the network connection is closed.
+ * -1 indicates a failure. The reason for the failure is obtained
+ * by calling PR_GetError().
+ **************************************************************************
+ */
+
+PR_EXTERN(PRInt32) PR_Recv(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_Send
+ * DESCRIPTION:
+ * Send a specified number of bytes from a connected socket.
+ * The operation will block until all bytes are
+ * processed, a time out has occurred, or there is an error.
+ * INPUTS:
+ * PRFileDesc *fd
+ * points to a PRFileDesc object representing a socket.
+ * void *buf
+ * pointer to a buffer from where the data is sent.
+ * PRInt32 amount
+ * the size of 'buf' (in bytes)
+ * PRIntn flags
+ * (OBSOLETE - must always be zero)
+ * PRIntervalTime timeout
+ * Time limit for completion of the send operation.
+ * OUTPUTS:
+ * None
+ * RETURN: PRInt32
+ * A positive number indicates the number of bytes successfully processed.
+ * This number must always equal 'amount'. A -1 is an indication that the
+ * operation failed. The reason for the failure is obtained by calling
+ * PR_GetError().
+ **************************************************************************
+ */
+
+PR_EXTERN(PRInt32) PR_Send(PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_RecvFrom
+ * DESCRIPTION:
+ * Receive up to a specified number of bytes from socket which may
+ * or may not be connected.
+ * The operation will block until one or more bytes are
+ * transferred, a time out has occurred, or there is an error.
+ * No more than 'amount' bytes will be transferred.
+ * INPUTS:
+ * PRFileDesc *fd
+ * points to a PRFileDesc object representing a socket.
+ * void *buf
+ * pointer to a buffer to hold the data received.
+ * PRInt32 amount
+ * the size of 'buf' (in bytes)
+ * PRIntn flags
+ * (OBSOLETE - must always be zero)
+ * PRNetAddr *addr
+ * Specifies the address of the sending peer. It may be NULL.
+ * PRIntervalTime timeout
+ * Time limit for completion of the receive operation.
+ * OUTPUTS:
+ * None
+ * RETURN: PRInt32
+ * a positive number indicates the number of bytes actually received.
+ * 0 means the network connection is closed.
+ * -1 indicates a failure. The reason for the failure is obtained
+ * by calling PR_GetError().
+ **************************************************************************
+ */
+
+PR_EXTERN(PRInt32) PR_RecvFrom(
+ PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRNetAddr *addr, PRIntervalTime timeout);
+
+/*
+ *************************************************************************
+ * FUNCTION: PR_SendTo
+ * DESCRIPTION:
+ * Send a specified number of bytes from an unconnected socket.
+ * The operation will block until all bytes are
+ * sent, a time out has occurred, or there is an error.
+ * INPUTS:
+ * PRFileDesc *fd
+ * points to a PRFileDesc object representing an unconnected socket.
+ * void *buf
+ * pointer to a buffer from where the data is sent.
+ * PRInt32 amount
+ * the size of 'buf' (in bytes)
+ * PRIntn flags
+ * (OBSOLETE - must always be zero)
+ * PRNetAddr *addr
+ * Specifies the address of the peer.
+.* PRIntervalTime timeout
+ * Time limit for completion of the send operation.
+ * OUTPUTS:
+ * None
+ * RETURN: PRInt32
+ * A positive number indicates the number of bytes successfully sent.
+ * -1 indicates a failure. The reason for the failure is obtained
+ * by calling PR_GetError().
+ **************************************************************************
+ */
+
+PR_EXTERN(PRInt32) PR_SendTo(
+ PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ const PRNetAddr *addr, PRIntervalTime timeout);
+
+/*
+*************************************************************************
+** FUNCTION: PR_TransmitFile
+** DESCRIPTION:
+** Transmitfile sends a complete file (sourceFile) across a socket
+** (networkSocket). If headers is non-NULL, the headers will be sent across
+** the socket prior to sending the file.
+**
+** Optionally, the PR_TRANSMITFILE_CLOSE_SOCKET flag may be passed to
+** transmitfile. This flag specifies that transmitfile should close the
+** socket after sending the data.
+**
+** INPUTS:
+** PRFileDesc *networkSocket
+** The socket to send data over
+** PRFileDesc *sourceFile
+** The file to send
+** const void *headers
+** A pointer to headers to be sent before sending data
+** PRInt32 hlen
+** length of header buffers in bytes.
+** PRTransmitFileFlags flags
+** If the flags indicate that the connection should be closed,
+** it will be done immediately after transferring the file, unless
+** the operation is unsuccessful.
+.* PRIntervalTime timeout
+ * Time limit for completion of the transmit operation.
+**
+** RETURNS:
+** Returns the number of bytes written or -1 if the operation failed.
+** If an error occurs while sending the file, the PR_TRANSMITFILE_CLOSE_
+** SOCKET flag is ignored. The reason for the failure is obtained
+** by calling PR_GetError().
+**************************************************************************
+*/
+
+PR_EXTERN(PRInt32) PR_TransmitFile(
+ PRFileDesc *networkSocket, PRFileDesc *sourceFile,
+ const void *headers, PRInt32 hlen, PRTransmitFileFlags flags,
+ PRIntervalTime timeout);
+/*
+*************************************************************************
+** FUNCTION: PR_AcceptRead
+** DESCRIPTION:
+** AcceptRead accepts a new connection, returns the newly created
+** socket's descriptor and also returns the connecting peer's address.
+** AcceptRead, as its name suggests, also receives the first block of data
+** sent by the peer.
+**
+** INPUTS:
+** PRFileDesc *listenSock
+** A socket descriptor that has been called with the PR_Listen()
+** function, also known as the rendezvous socket.
+** void *buf
+** A pointer to a buffer to receive data sent by the client. This
+** buffer must be large enough to receive <amount> bytes of data
+** and two PRNetAddr structures, plus an extra 32 bytes.
+** PRInt32 amount
+** The number of bytes of client data to receive. Does not include
+** the size of the PRNetAddr structures. If 0, no data will be read
+** from the client.
+** PRIntervalTime timeout
+** The timeout interval only applies to the read portion of the
+** operation. PR_AcceptRead will block indefinitely until the
+** connection is accepted; the read will timeout after the timeout
+** interval elapses.
+** OUTPUTS:
+** PRFileDesc **acceptedSock
+** The file descriptor for the newly connected socket. This parameter
+** will only be valid if the function return does not indicate failure.
+** PRNetAddr **peerAddr,
+** The address of the remote socket. This parameter will only be
+** valid if the function return does not indicate failure. The
+** returned address is not guaranteed to be properly aligned.
+**
+** RETURNS:
+** The number of bytes read from the client or -1 on failure. The reason
+** for the failure is obtained by calling PR_GetError().
+**************************************************************************
+**/
+PR_EXTERN(PRInt32) PR_AcceptRead(
+ PRFileDesc *listenSock, PRFileDesc **acceptedSock,
+ PRNetAddr **peerAddr, void *buf, PRInt32 amount, PRIntervalTime timeout);
+
+/*
+*************************************************************************
+** FUNCTION: PR_NewTCPSocketPair
+** DESCRIPTION:
+** Create a new TCP socket pair. The returned descriptors can be used
+** interchangeably; they are interconnected full-duplex descriptors: data
+** written to one can be read from the other and vice-versa.
+**
+** INPUTS:
+** None
+** OUTPUTS:
+** PRFileDesc *fds[2]
+** The file descriptor pair for the newly created TCP sockets.
+** RETURN: PRStatus
+** Upon successful completion of TCP socket pair, PR_NewTCPSocketPair
+** returns PR_SUCCESS. Otherwise, it returns PR_FAILURE. Further
+** failure information can be obtained by calling PR_GetError().
+** XXX can we implement this on windoze and mac?
+**************************************************************************
+**/
+PR_EXTERN(PRStatus) PR_NewTCPSocketPair(PRFileDesc *fds[2]);
+
+/*
+*************************************************************************
+** FUNCTION: PR_GetSockName
+** DESCRIPTION:
+** Get socket name. Return the network address for this socket.
+**
+** INPUTS:
+** PRFileDesc *fd
+** Points to a PRFileDesc object representing the socket.
+** OUTPUTS:
+** PRNetAddr *addr
+** Returns the address of the socket in its own communication space.
+** RETURN: PRStatus
+** Upon successful completion, PR_GetSockName returns PR_SUCCESS.
+** Otherwise, it returns PR_FAILURE. Further failure information can
+** be obtained by calling PR_GetError().
+**************************************************************************
+**/
+PR_EXTERN(PRStatus) PR_GetSockName(PRFileDesc *fd, PRNetAddr *addr);
+
+/*
+*************************************************************************
+** FUNCTION: PR_GetPeerName
+** DESCRIPTION:
+** Get name of the connected peer. Return the network address for the
+** connected peer socket.
+**
+** INPUTS:
+** PRFileDesc *fd
+** Points to a PRFileDesc object representing the connected peer.
+** OUTPUTS:
+** PRNetAddr *addr
+** Returns the address of the connected peer in its own communication
+** space.
+** RETURN: PRStatus
+** Upon successful completion, PR_GetPeerName returns PR_SUCCESS.
+** Otherwise, it returns PR_FAILURE. Further failure information can
+** be obtained by calling PR_GetError().
+**************************************************************************
+**/
+PR_EXTERN(PRStatus) PR_GetPeerName(PRFileDesc *fd, PRNetAddr *addr);
+
+PR_EXTERN(PRStatus) PR_GetSocketOption(
+ PRFileDesc *fd, PRSocketOptionData *data);
+
+PR_EXTERN(PRStatus) PR_SetSocketOption(
+ PRFileDesc *fd, const PRSocketOptionData *data);
+
+/*
+ *********************************************************************
+ *
+ * Memory-mapped files
+ *
+ *********************************************************************
+ */
+
+typedef struct PRFileMap PRFileMap;
+
+/*
+ * protection options for read and write accesses of a file mapping
+ */
+typedef enum PRFileMapProtect {
+ PR_PROT_READONLY, /* read only */
+ PR_PROT_READWRITE, /* readable, and write is shared */
+ PR_PROT_WRITECOPY /* readable, and write is private (copy-on-write) */
+} PRFileMapProtect;
+
+PR_EXTERN(PRFileMap *) PR_CreateFileMap(
+ PRFileDesc *fd,
+ PRInt64 size,
+ PRFileMapProtect prot);
+
+PR_EXTERN(void *) PR_MemMap(
+ PRFileMap *fmap,
+ PRInt64 offset, /* must be aligned and sized to whole pages */
+ PRUint32 len);
+
+PR_EXTERN(PRStatus) PR_MemUnmap(void *addr, PRUint32 len);
+
+PR_EXTERN(PRStatus) PR_CloseFileMap(PRFileMap *fmap);
+
+/*
+ ******************************************************************
+ *
+ * Interprocess communication
+ *
+ ******************************************************************
+ */
+
+/*
+ * Creates an anonymous pipe and returns file descriptors for the
+ * read and write ends of the pipe.
+ */
+
+PR_EXTERN(PRStatus) PR_CreatePipe(
+ PRFileDesc **readPipe,
+ PRFileDesc **writePipe
+);
+
+/************************************************************************/
+/************** The following definitions are for poll ******************/
+/************************************************************************/
+
+struct PRPollDesc {
+ PRFileDesc* fd;
+ PRInt16 in_flags;
+ PRInt16 out_flags;
+};
+
+/*
+** Bit values for PRPollDesc.in_flags or PRPollDesc.out_flags. Binary-or
+** these together to produce the desired poll request.
+*/
+
+#if defined(_PR_POLL_BACKCOMPAT)
+
+#include <poll.h>
+#define PR_POLL_READ POLLIN
+#define PR_POLL_WRITE POLLOUT
+#define PR_POLL_EXCEPT POLLPRI
+#define PR_POLL_ERR POLLERR /* only in out_flags */
+#define PR_POLL_NVAL POLLNVAL /* only in out_flags when fd is bad */
+#define PR_POLL_HUP POLLHUP /* only in out_flags */
+
+#else /* _PR_POLL_BACKCOMPAT */
+
+#define PR_POLL_READ 0x1
+#define PR_POLL_WRITE 0x2
+#define PR_POLL_EXCEPT 0x4
+#define PR_POLL_ERR 0x8 /* only in out_flags */
+#define PR_POLL_NVAL 0x10 /* only in out_flags when fd is bad */
+#define PR_POLL_HUP 0x20 /* only in out_flags */
+
+#endif /* _PR_POLL_BACKCOMPAT */
+
+/*
+*************************************************************************
+** FUNCTION: PR_Poll
+** DESCRIPTION:
+**
+** The call returns as soon as I/O is ready on one or more of the underlying
+** file/socket objects. A count of the number of ready descriptors is
+** returned unless a timeout occurs in which case zero is returned.
+**
+** PRPollDesc.in_flags should be set to the desired request
+** (read/write/except or some combination). Upon return from this call
+** PRPollDesc.out_flags will be set to indicate what kind of i/o can be
+** performed on the respective descriptor.
+**
+** INPUTS:
+** PRPollDesc *pds A pointer to an array of PRPollDesc
+**
+** PRIntn npds The number of elements in the array
+** If this argument is zero PR_Poll is
+** equivalent to a PR_Sleep(timeout).
+**
+** PRIntervalTime timeout Amount of time the call will block waiting
+** for I/O to become ready. If this time expires
+** w/o any I/O becoming ready, the result will
+** be zero.
+**
+** OUTPUTS: None
+** RETURN:
+** PRInt32 Number of PRPollDesc's with events or zero
+** if the function timed out or -1 on failure.
+** The reason for the failure is obtained by calling
+** PR_GetError().
+** XXX can we implement this on windoze and mac?
+**************************************************************************
+*/
+PR_EXTERN(PRInt32) PR_Poll(
+ PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout);
+
+/*
+**************************************************************************
+**
+** Pollable events
+**
+** A pollable event is a special kind of file descriptor.
+** The only I/O operation you can perform on a pollable event
+** is to poll it with the PR_POLL_READ flag. You can't
+** read from or write to a pollable event.
+**
+** The purpose of a pollable event is to combine event waiting
+** with I/O waiting in a single PR_Poll call. Pollable events
+** are implemented using a pipe or a pair of TCP sockets
+** connected via the loopback address, therefore setting and
+** waiting for pollable events are expensive operating system
+** calls. Do not use pollable events for general thread
+** synchronization. Use condition variables instead.
+**
+** A pollable event has two states: set and unset. Events
+** are not queued, so there is no notion of an event count.
+** A pollable event is either set or unset.
+**
+** A new pollable event is created by a PR_NewPollableEvent
+** call and is initially in the unset state.
+**
+** PR_WaitForPollableEvent blocks the calling thread until
+** the pollable event is set, and then it atomically unsets
+** the pollable event before it returns.
+**
+** To set a pollable event, call PR_SetPollableEvent.
+**
+** One can call PR_Poll with the PR_POLL_READ flag on a pollable
+** event. When the pollable event is set, PR_Poll returns with
+** the PR_POLL_READ flag set in the out_flags.
+**
+** To close a pollable event, call PR_DestroyPollableEvent
+** (not PR_Close).
+**
+**************************************************************************
+*/
+
+PR_EXTERN(PRFileDesc *) PR_NewPollableEvent(void);
+
+PR_EXTERN(PRStatus) PR_DestroyPollableEvent(PRFileDesc *event);
+
+PR_EXTERN(PRStatus) PR_SetPollableEvent(PRFileDesc *event);
+
+PR_EXTERN(PRStatus) PR_WaitForPollableEvent(PRFileDesc *event);
+
+PR_END_EXTERN_C
+
+#endif /* prio_h___ */
diff --git a/pr/include/private/Makefile b/pr/include/private/Makefile
new file mode 100644
index 00000000..3a8b1e9f
--- /dev/null
+++ b/pr/include/private/Makefile
@@ -0,0 +1,35 @@
+#
+# 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
+
+RELEASE_HEADERS = pprio.h pprthred.h prpriv.h
+RELEASE_HEADERS_DEST = $(RELEASE_INCLUDE_DIR)/private
+
+HEADERS = $(RELEASE_HEADERS) pprmwait.h primpl.h
+
+include $(MOD_DEPTH)/config/rules.mk
+
+export:: $(HEADERS)
+ $(INSTALL) -m 444 $(HEADERS) $(DIST)/include/private
+ifeq ($(MOZ_BITS),16)
+ $(INSTALL) -m 444 $(HEADERS) $(MOZ_INCL)/private
+endif
diff --git a/pr/include/private/pprio.h b/pr/include/private/pprio.h
new file mode 100644
index 00000000..7e73b31e
--- /dev/null
+++ b/pr/include/private/pprio.h
@@ -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.
+ */
+
+/*
+** File: pprio.h
+**
+** Description: Private definitions for I/O related structures
+*/
+
+#ifndef pprio_h___
+#define pprio_h___
+
+#include "prtypes.h"
+#include "prio.h"
+
+PR_BEGIN_EXTERN_C
+
+/*
+** File descriptors of the NSPR layer can be in one of the
+** following states (stored in the 'state' field of struct
+** PRFilePrivate):
+** - _PR_FILEDESC_OPEN: The OS fd is open.
+** - _PR_FILEDESC_CLOSED: The OS fd is closed. The PRFileDesc
+** is still open but is unusable. The only operation allowed
+** on the PRFileDesc is PR_Close().
+** - _PR_FILEDESC_FREED: The OS fd is closed and the PRFileDesc
+** structure is freed.
+*/
+
+#define _PR_FILEDESC_OPEN 0xaaaaaaaa /* 1010101... */
+#define _PR_FILEDESC_CLOSED 0x55555555 /* 0101010... */
+#define _PR_FILEDESC_FREED 0x11111111
+
+/************************************************************************/
+/************************************************************************/
+
+/* Return the method tables for files, tcp sockets and udp sockets */
+PR_EXTERN(const PRIOMethods*) PR_GetFileMethods(void);
+PR_EXTERN(const PRIOMethods*) PR_GetTCPMethods(void);
+PR_EXTERN(const PRIOMethods*) PR_GetUDPMethods(void);
+
+/*
+** Convert a NSPR Socket Handle to a Native Socket handle.
+** This function will be obsoleted with the next release; avoid using it.
+*/
+PR_EXTERN(PRInt32) PR_FileDesc2NativeHandle(PRFileDesc *);
+PR_EXTERN(void) PR_ChangeFileDescNativeHandle(PRFileDesc *, PRInt32);
+PR_EXTERN(PRFileDesc*) PR_AllocFileDesc(PRInt32 osfd,
+ const PRIOMethods *methods);
+PR_EXTERN(void) PR_FreeFileDesc(PRFileDesc *fd);
+/*
+** Import an existing OS file to NSPR.
+*/
+PR_EXTERN(PRFileDesc*) PR_ImportFile(PRInt32 osfd);
+PR_EXTERN(PRFileDesc*) PR_ImportTCPSocket(PRInt32 osfd);
+PR_EXTERN(PRFileDesc*) PR_ImportUDPSocket(PRInt32 osfd);
+
+/*
+** Macros for PR_Socket
+**
+** Socket types: PR_SOCK_STREAM, PR_SOCK_DGRAM
+*/
+
+#ifdef WIN32
+
+#define PR_SOCK_STREAM 1
+#define PR_SOCK_DGRAM 2
+
+#else /* WIN32 */
+
+#define PR_SOCK_STREAM SOCK_STREAM
+#define PR_SOCK_DGRAM SOCK_DGRAM
+
+#endif /* WIN32 */
+
+/*
+** Create a new Socket; this function is obsolete.
+*/
+PR_EXTERN(PRFileDesc*) PR_Socket(PRInt32 domain, PRInt32 type, PRInt32 proto);
+
+/* FUNCTION: PR_LockFile
+** DESCRIPTION:
+** Lock a file for exclusive access.
+** RETURNS:
+** PR_SUCCESS when the lock is held
+** PR_FAILURE otherwise
+*/
+PR_EXTERN(PRStatus) PR_LockFile(PRFileDesc *fd);
+
+/* FUNCTION: PR_TLockFile
+** DESCRIPTION:
+** Test and Lock a file for exclusive access. Do not block if the
+** file cannot be locked immediately.
+** RETURNS:
+** PR_SUCCESS when the lock is held
+** PR_FAILURE otherwise
+*/
+PR_EXTERN(PRStatus) PR_TLockFile(PRFileDesc *fd);
+
+/* FUNCTION: PR_UnlockFile
+** DESCRIPTION:
+** Unlock a file which has been previously locked successfully by this
+** process.
+** RETURNS:
+** PR_SUCCESS when the lock is released
+** PR_FAILURE otherwise
+*/
+PR_EXTERN(PRStatus) PR_UnlockFile(PRFileDesc *fd);
+
+#ifdef WIN32
+/* FUNCTION: PR_NTFast_AcceptRead
+** DESCRIPTION:
+** NT has the notion of an "accept context", which is only needed in
+** order to make certain calls. By default, a socket connected via
+** AcceptEx can only do a limited number of things without updating
+** the acceptcontext. The generic version of PR_AcceptRead always
+** updates the accept context. This version does not.
+**/
+PR_EXTERN(PRInt32) PR_NTFast_AcceptRead(PRFileDesc *sd, PRFileDesc **nd,
+ PRNetAddr **raddr, void *buf, PRInt32 amount, PRIntervalTime t);
+
+typedef void (*_PR_AcceptTimeoutCallback)(void *);
+
+/* FUNCTION: PR_NTFast_AcceptRead_WithTimeoutCallback
+** DESCRIPTION:
+** The AcceptEx call combines the accept with the read function. However,
+** our daemon threads need to be able to wakeup and reliably flush their
+** log buffers if the Accept times out. However, with the current blocking
+** interface to AcceptRead, there is no way for us to timeout the Accept;
+** this is because when we timeout the Read, we can close the newly
+** socket and continue; but when we timeout the accept itself, there is no
+** new socket to timeout. So instead, this version of the function is
+** provided. After the initial timeout period elapses on the accept()
+** portion of the function, it will call the callback routine and then
+** continue the accept. If the timeout occurs on the read, it will
+** close the connection and return error.
+*/
+PR_EXTERN(PRInt32) PR_NTFast_AcceptRead_WithTimeoutCallback(
+ PRFileDesc *sd,
+ PRFileDesc **nd,
+ PRNetAddr **raddr,
+ void *buf,
+ PRInt32 amount,
+ PRIntervalTime t,
+ _PR_AcceptTimeoutCallback callback,
+ void *callback_arg);
+
+/* FUNCTION: PR_NTFast_Accept
+** DESCRIPTION:
+** NT has the notion of an "accept context", which is only needed in
+** order to make certain calls. By default, a socket connected via
+** AcceptEx can only do a limited number of things without updating
+** the acceptcontext. The generic version of PR_Accept always
+** updates the accept context. This version does not.
+**/
+PR_EXTERN(PRFileDesc*) PR_NTFast_Accept(PRFileDesc *fd, PRNetAddr *addr,
+ PRIntervalTime timeout);
+
+/* FUNCTION: PR_NTFast_Update
+** DESCRIPTION:
+** For sockets accepted with PR_NTFast_Accept or PR_NTFastAcceptRead,
+** this function will update the accept context for those sockets,
+** so that the socket can make general purpose socket calls.
+** Without calling this, the only operations supported on the socket
+** Are PR_Read, PR_Write, PR_Transmitfile, and PR_Close.
+*/
+PR_EXTERN(void) PR_NTFast_UpdateAcceptContext(PRFileDesc *acceptSock,
+ PRFileDesc *listenSock);
+
+
+/* FUNCTION: PR_NT_UseNonblock
+** DESCRIPTION:
+** This function only works on NT. It tells nspr to use nonblocking io
+** rather than async IO. There is no way to use some of each. This
+** function should be called immediately after calling PR_Init().
+**
+** WARNING: THIS FUNCTION IS A TEMPORARY HACK AND WILL BE REMOVED SHORTLY
+** (LIKE ALL FUNCTIONS IN THE PRIVATE AREA). DO NOT USE THIS FUNCTION AT
+** ALL WITHOUT CONTACTING mbelshe@netscape.com.
+*/
+PR_EXTERN(void) PR_NT_UseNonblock();
+
+#endif /* WIN32 */
+
+PR_END_EXTERN_C
+
+#endif /* pprio_h___ */
diff --git a/pr/include/private/pprmwait.h b/pr/include/private/pprmwait.h
new file mode 100644
index 00000000..a47e4668
--- /dev/null
+++ b/pr/include/private/pprmwait.h
@@ -0,0 +1,117 @@
+/* -*- 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 MAX_POLLING_INTERVAL 100
+#define _PR_POLL_COUNT_FUDGE 64
+#define MAX_POLLING_INTERVAL 100
+#define _PR_DEFAULT_HASH_LENGTH 59
+
+/*
+ * Our hash table resolves collisions by open addressing with
+ * double hashing. See Cormen, Leiserson, and Rivest,
+ * Introduction to Algorithms, p. 232, The MIT Press, 1990.
+ */
+
+#define _MW_HASH(a, m) ((((PRUptrdiff)(a) >> 4) ^ ((PRUptrdiff)(a) >> 10)) % (m))
+#define _MW_HASH2(a, m) (1 + ((((PRUptrdiff)(a) >> 4) ^ ((PRUptrdiff)(a) >> 10)) % (m - 2)))
+#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 */
+ PRUint32 p_timestamp; /* pseudo-time group had element removed */
+ 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 */
+
+#ifdef WINNT
+ /*
+ * On NT, idle threads are responsible for getting completed i/o.
+ * They need to add completed i/o to the io_ready list. Since
+ * idle threads cannot use nspr locks, we have to use an md lock
+ * to protect the io_ready list.
+ */
+ _MDLock mdlock; /* protect io_ready, waiter, and wait_list */
+ PRCList wait_list; /* used in place of io_complete. reuse
+ * waitQLinks in the PRThread structure. */
+#endif /* WINNT */
+};
+
+/**********************************************************************
+***********************************************************************
+******************** Wait group enumerations **************************
+***********************************************************************
+**********************************************************************/
+typedef struct _PRGlobalState
+{
+ PRCList group_list; /* master of the group list */
+ PRWaitGroup *group; /* the default (NULL) group */
+} _PRGlobalState;
+
+#ifdef WINNT
+extern PRStatus NT_HashRemoveInternal(PRWaitGroup *group, PRFileDesc *fd);
+#endif
+
+typedef enum {_PR_ENUM_UNSEALED=0, _PR_ENUM_SEALED=0x0eadface} _PREnumSeal;
+
+struct PRMWaitEnumerator
+{
+ PRWaitGroup *group; /* group this enumerator is bound to */
+ PRThread *thread; /* thread in midst of an enumeration */
+ _PREnumSeal seal; /* trying to detect deleted objects */
+ PRUint32 p_timestamp; /* when enumeration was (re)started */
+ PRRecvWait **waiter; /* pointer into hash table */
+ PRUintn index; /* position in hash table */
+ void *pad[4]; /* some room to grow */
+};
+
+#endif /* defined(_PPRMWAIT_H) */
+
+/* pprmwait.h */
diff --git a/pr/include/private/pprthred.h b/pr/include/private/pprthred.h
new file mode 100644
index 00000000..0c422a7f
--- /dev/null
+++ b/pr/include/private/pprthred.h
@@ -0,0 +1,344 @@
+/* -*- 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 pprthred_h___
+#define pprthred_h___
+
+/*
+** API for PR private functions. These calls are to be used by internal
+** developers only.
+*/
+#include "nspr.h"
+
+
+PR_BEGIN_EXTERN_C
+
+/*---------------------------------------------------------------------------
+** THREAD PRIVATE FUNCTIONS
+---------------------------------------------------------------------------*/
+
+/*
+** Add an exit func to be evaluated when the thread exits. Each thread
+** can have zero or more of these.
+*/
+typedef void (*PRThreadExit)(void *arg);
+PR_EXTERN(PRStatus) PR_SetThreadExit(PRUintn index, PRThreadExit func, void *arg);
+
+/*
+** Return the "index"'th at-exit function associated with thread
+** "t". Returns NULL if the index does not reference a valid at-exit
+** function.
+*/
+PR_EXTERN(PRThreadExit) PR_GetThreadExit(PRUintn index, void **argp);
+
+/*
+** 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 invokes "start(obj,arg)" and returns when the
+** function returns. The thread object is automatically destroyed.
+**
+** This call is not normally needed unless you create your own native
+** thread. PR_Init does this automatically for the primordial thread.
+*/
+PR_EXTERN(PRThread*) PR_AttachThread(PRThreadType type,
+ PRThreadPriority priority,
+ PRThreadStack *stack);
+
+/*
+** 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_EXTERN(void) PR_DetachThread(void);
+
+/*
+** Get the id of the named thread. Each thread is assigned a unique id
+** when it is created or attached.
+*/
+PR_EXTERN(PRUint32) PR_GetThreadID(PRThread *thread);
+
+/*
+** Set the procedure that is called when a thread is dumped. The procedure
+** will be applied to the argument, arg, when called. Setting the procedure
+** to NULL effectively removes it.
+*/
+typedef void (*PRThreadDumpProc)(PRFileDesc *fd, PRThread *t, void *arg);
+PR_EXTERN(void) PR_SetThreadDumpProc(
+ PRThread* thread, PRThreadDumpProc dump, void *arg);
+
+/*
+** Get this thread's affinity mask. The affinity mask is a 32 bit quantity
+** marking a bit for each processor this process is allowed to run on.
+** The processor mask is returned in the mask argument.
+** The least-significant-bit represents processor 0.
+**
+** Returns 0 on success, -1 on failure.
+*/
+PR_EXTERN(PRInt32) PR_GetThreadAffinityMask(PRThread *thread, PRUint32 *mask);
+
+/*
+** Set this thread's affinity mask.
+**
+** Returns 0 on success, -1 on failure.
+*/
+PR_EXTERN(PRInt32) PR_SetThreadAffinityMask(PRThread *thread, PRUint32 mask );
+
+/*
+** Set the default CPU Affinity mask.
+**
+*/
+PR_EXTERN(PRInt32) PR_SetCPUAffinityMask(PRUint32 mask);
+
+/*
+** Show status of all threads to standard error output.
+*/
+PR_EXTERN(void) PR_ShowStatus(void);
+
+/*
+** Set thread recycle mode to on (1) or off (0)
+*/
+PR_EXTERN(void) PR_SetThreadRecycleMode(PRUint32 flag);
+
+
+/*---------------------------------------------------------------------------
+** THREAD PRIVATE FUNCTIONS FOR GARBAGE COLLECTIBLE THREADS
+---------------------------------------------------------------------------*/
+
+/*
+** Only Garbage collectible threads participate in resume all, suspend all and
+** enumeration operations. They are also different during creation when
+** platform specific action may be needed (For example, all Solaris GC able
+** threads are bound threads).
+*/
+
+/*
+** Same as PR_CreateThread except that the thread is marked as garbage
+** collectible.
+*/
+PR_EXTERN(PRThread*) PR_CreateThreadGCAble(PRThreadType type,
+ void (*start)(void *arg),
+ void *arg,
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize);
+
+/*
+** Same as PR_AttachThread except that the thread being attached is marked as
+** garbage collectible.
+*/
+PR_EXTERN(PRThread*) PR_AttachThreadGCAble(PRThreadType type,
+ PRThreadPriority priority,
+ PRThreadStack *stack);
+
+/*
+** Mark the thread as garbage collectible.
+*/
+PR_EXTERN(void) PR_SetThreadGCAble(void);
+
+/*
+** Unmark the thread as garbage collectible.
+*/
+PR_EXTERN(void) PR_ClearThreadGCAble(void);
+
+/*
+** This routine prevents all other GC able threads from running. This call is needed by
+** the garbage collector.
+*/
+PR_EXTERN(void) PR_SuspendAll(void);
+
+/*
+** This routine unblocks all other GC able threads that were suspended from running by
+** PR_SuspendAll(). This call is needed by the garbage collector.
+*/
+PR_EXTERN(void) PR_ResumeAll(void);
+
+/*
+** Return the thread stack pointer of the given thread.
+** Needed by the garbage collector.
+*/
+PR_EXTERN(void *) PR_GetSP(PRThread *thread);
+
+/*
+** Save the registers that the GC would find interesting into the thread
+** "t". isCurrent will be non-zero if the thread state that is being
+** saved is the currently executing thread. Return the address of the
+** first register to be scanned as well as the number of registers to
+** scan in "np".
+**
+** If "isCurrent" is non-zero then it is allowed for the thread context
+** area to be used as scratch storage to hold just the registers
+** necessary for scanning.
+**
+** This function simply calls the internal function _MD_HomeGCRegisters().
+*/
+PR_EXTERN(PRWord *) PR_GetGCRegisters(PRThread *t, int isCurrent, int *np);
+
+/*
+** (Get|Set)ExecutionEnvironent
+**
+** Used by Java to associate it's execution environment so garbage collector
+** can find it. If return is NULL, then it's probably not a collectable thread.
+**
+** There's no locking required around these calls.
+*/
+PR_EXTERN(void*) GetExecutionEnvironment(PRThread *thread);
+PR_EXTERN(void) SetExecutionEnvironment(PRThread* thread, void *environment);
+
+/*
+** Enumeration function that applies "func(thread,i,arg)" to each active
+** thread in the process. The enumerator returns PR_SUCCESS if the enumeration
+** should continue, any other value is considered failure, and enumeration
+** stops, returning the failure value from PR_EnumerateThreads.
+** Needed by the garbage collector.
+*/
+typedef PRStatus (PR_CALLBACK *PREnumerator)(PRThread *t, int i, void *arg);
+PR_EXTERN(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg);
+
+/*
+** Signature of a thread stack scanning function. It is applied to every
+** contiguous group of potential pointers within a thread. Count denotes the
+** number of pointers.
+*/
+typedef PRStatus
+(PR_CALLBACK *PRScanStackFun)(PRThread* t,
+ void** baseAddr, PRUword count, void* closure);
+
+/*
+** Applies scanFun to all contiguous groups of potential pointers
+** within a thread. This includes the stack, registers, and thread-local
+** data. If scanFun returns a status value other than PR_SUCCESS the scan
+** is aborted, and the status value is returned.
+*/
+PR_EXTERN(PRStatus)
+PR_ThreadScanStackPointers(PRThread* t,
+ PRScanStackFun scanFun, void* scanClosure);
+
+/*
+** Calls PR_ThreadScanStackPointers for every thread.
+*/
+PR_EXTERN(PRStatus)
+PR_ScanStackPointers(PRScanStackFun scanFun, void* scanClosure);
+
+/*
+** Returns a conservative estimate on the amount of stack space left
+** on a thread in bytes, sufficient for making decisions about whether
+** to continue recursing or not.
+*/
+PR_EXTERN(PRUword)
+PR_GetStackSpaceLeft(PRThread* t);
+
+/*---------------------------------------------------------------------------
+** THREAD CPU PRIVATE FUNCTIONS
+---------------------------------------------------------------------------*/
+
+/*
+** Get a pointer to the primordial CPU.
+*/
+PR_EXTERN(struct _PRCPU *) _PR_GetPrimordialCPU(void);
+
+/*---------------------------------------------------------------------------
+** THREAD SYNCHRONIZATION PRIVATE FUNCTIONS
+---------------------------------------------------------------------------*/
+
+/*
+** Create a new named monitor (named for debugging purposes).
+** Monitors are re-entrant locks with a built-in condition variable.
+**
+** This may fail if memory is tight or if some operating system resource
+** is low.
+*/
+PR_EXTERN(PRMonitor*) PR_NewNamedMonitor(const char* name);
+
+/*
+** 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_EXTERN(PRBool) PR_TestAndLock(PRLock *lock);
+
+/*
+** Test and then enter the mutex associated with the monitor if it's not
+** already entered by some other thread. Return PR_FALSE if some other
+** thread owned the mutex at the time of the call.
+*/
+PR_EXTERN(PRBool) PR_TestAndEnterMonitor(PRMonitor *mon);
+
+/*
+** Return the number of times that the current thread has entered the
+** mutex. Returns zero if the current thread has not entered the mutex.
+*/
+PR_EXTERN(PRIntn) PR_GetMonitorEntryCount(PRMonitor *mon);
+
+/*
+** Just like PR_CEnterMonitor except that if the monitor is owned by
+** another thread NULL is returned.
+*/
+PR_EXTERN(PRMonitor*) PR_CTestAndEnterMonitor(void *address);
+
+
+/*---------------------------------------------------------------------------
+** PLATFORM-SPECIFIC INITIALIZATION FUNCTIONS
+---------------------------------------------------------------------------*/
+
+#if defined(IRIX)
+/*
+** Irix specific initialization funtion to be called before PR_Init
+** is called by the application. Sets the CONF_INITUSERS and CONF_INITSIZE
+** attributes of the shared arena set up by nspr.
+**
+** The environment variables _NSPR_IRIX_INITUSERS and _NSPR_IRIX_INITSIZE
+** can also be used to set these arena attributes. If _NSPR_IRIX_INITUSERS
+** is set, but not _NSPR_IRIX_INITSIZE, the value of the CONF_INITSIZE
+** attribute of the nspr arena is scaled as a function of the
+** _NSPR_IRIX_INITUSERS value.
+**
+** If the _PR_Irix_Set_Arena_Params() is called in addition to setting the
+** environment variables, the values of the environment variables are used.
+**
+*/
+PR_EXTERN(void) _PR_Irix_Set_Arena_Params(PRInt32 initusers, PRInt32 initsize);
+
+#endif /* IRIX */
+
+/* I think PR_GetMonitorEntryCount is useless. All you really want is this... */
+#define PR_InMonitor(m) (PR_GetMonitorEntryCount(m) > 0)
+
+/*---------------------------------------------------------------------------
+** Special X-Lock hack for client
+---------------------------------------------------------------------------*/
+
+#ifdef XP_UNIX
+extern void PR_XLock(void);
+extern void PR_XUnlock(void);
+extern PRBool PR_XIsLocked(void);
+#endif /* XP_UNIX */
+
+PR_END_EXTERN_C
+
+#endif /* pprthred_h___ */
diff --git a/pr/include/private/primpl.h b/pr/include/private/primpl.h
new file mode 100644
index 00000000..f4c32230
--- /dev/null
+++ b/pr/include/private/primpl.h
@@ -0,0 +1,1683 @@
+/* -*- 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 primpl_h___
+#define primpl_h___
+
+/*
+ * HP-UX 10.10's pthread.h (DCE threads) includes dce/cma.h, which
+ * has:
+ * #define sigaction _sigaction_sys
+ * This macro causes chaos if signal.h gets included before pthread.h.
+ * To be safe, we include pthread.h first.
+ */
+
+#if defined(_PR_PTHREADS)
+#include <pthread.h>
+#endif
+
+#ifdef WINNT
+/* Need to force service-pack 3 extensions to be defined by
+** setting _WIN32_WINNT to NT 4.0 for winsock.h, winbase.h, winnt.h.
+*/
+#ifndef _WIN32_WINNT
+ #define _WIN32_WINNT 0x0400
+#elif (_WIN32_WINNT < 0x0400)
+ #undef _WIN32_WINNT
+ #define _WIN32_WINNT 0x0400
+#endif /* _WIN32_WINNT */
+#endif /* WINNT */
+
+#include "nspr.h"
+#include "prpriv.h"
+
+typedef struct PRSegment PRSegment;
+
+#ifdef XP_MAC
+#include "prosdep.h"
+#include "probslet.h"
+#else
+#include "md/prosdep.h"
+#include "obsolete/probslet.h"
+#endif /* XP_MAC */
+
+/*************************************************************************
+***** A Word about Model Dependent Function Naming Convention ***********
+*************************************************************************/
+
+/*
+NSPR 2.0 must implement its function across a range of platforms
+including: MAC, Windows/16, Windows/95, Windows/NT, and several
+variants of Unix. Each implementation shares common code as well
+as having platform dependent portions. This standard describes how
+the model dependent portions are to be implemented.
+
+In header file pr/include/primpl.h, each publicly declared
+platform dependent function is declared as:
+
+PR_EXTERN void _PR_MD_FUNCTION( long arg1, long arg2 );
+#define _PR_MD_FUNCTION _MD_FUNCTION
+
+In header file pr/include/md/<platform>/_<platform>.h,
+each #define'd macro is redefined as one of:
+
+#define _MD_FUNCTION <blanks>
+#define _MD_FUNCTION <expanded macro>
+#define _MD_FUNCTION <osFunction>
+#define _MD_FUNCTION <_MD_Function>
+
+Where:
+
+<blanks> is no definition at all. In this case, the function is not implemented
+and is never called for this platform.
+For example:
+#define _MD_INIT_CPUS()
+
+<expanded macro> is a C language macro expansion.
+For example:
+#define _MD_CLEAN_THREAD(_thread) \
+ PR_BEGIN_MACRO \
+ PR_DestroyCondVar(_thread->md.asyncIOCVar); \
+ PR_DestroyLock(_thread->md.asyncIOLock); \
+ PR_END_MACRO
+
+<osFunction> is some function implemented by the host operating system.
+For example:
+#define _MD_EXIT exit
+
+<_MD_function> is the name of a function implemented for this platform in
+pr/src/md/<platform>/<soruce>.c file.
+For example:
+#define _MD_GETFILEINFO _MD_GetFileInfo
+
+In <source>.c, the implementation is:
+PR_IMPLEMENT(PRInt32) _MD_GetFileInfo(const char *fn, PRFileInfo *info);
+*/
+
+PR_BEGIN_EXTERN_C
+
+typedef struct _MDLock _MDLock;
+typedef struct _MDCVar _MDCVar;
+typedef struct _MDSegment _MDSegment;
+typedef struct _MDThread _MDThread;
+typedef struct _MDThreadStack _MDThreadStack;
+typedef struct _MDSemaphore _MDSemaphore;
+typedef struct _MDDir _MDDir;
+typedef struct _MDFileDesc _MDFileDesc;
+typedef struct _MDProcess _MDProcess;
+typedef struct _MDFileMap _MDFileMap;
+
+#if defined(_PR_PTHREADS)
+
+/*
+** The following definitions are unique to implementing NSPR using pthreads.
+** Since pthreads defines most of the thread and thread synchronization
+** stuff, this is a pretty small set.
+*/
+
+#define PT_CV_NOTIFIED_LENGTH 6
+typedef struct _PT_Notified _PT_Notified;
+struct _PT_Notified
+{
+ PRIntn length; /* # of used entries in this structure */
+ struct
+ {
+ PRCondVar *cv; /* the condition variable notified */
+ PRIntn times; /* and the number of times notified */
+ } cv[PT_CV_NOTIFIED_LENGTH];
+ _PT_Notified *link; /* link to another of these | NULL */
+};
+
+/*
+ * bits defined for pthreads 'state' field
+ */
+#define PT_THREAD_DETACHED 0x01 /* thread can't be joined */
+#define PT_THREAD_GLOBAL 0x02 /* a global thread (unlikely) */
+#define PT_THREAD_SYSTEM 0x04 /* system (not user) thread */
+#define PT_THREAD_PRIMORD 0x08 /* this is the primordial thread */
+#define PT_THREAD_ABORTED 0x10 /* thread has been interrupted */
+#define PT_THREAD_GCABLE 0x20 /* thread is garbage collectible */
+#define PT_THREAD_SUSPENDED 0x40 /* thread has been suspended */
+#define PT_THREAD_FOREIGN 0x80 /* thread is not one of ours */
+
+/*
+** Possible values for thread's suspend field
+** Note that the first two can be the same as they are really mutually exclusive,
+** i.e. both cannot be happening at the same time. We have two symbolic names
+** just as a mnemonic.
+**/
+#define PT_THREAD_RESUMED 0x80 /* thread has been resumed */
+#define PT_THREAD_SETGCABLE 0x100 /* set the GCAble flag */
+
+#if defined(DEBUG)
+
+typedef struct PTDebug
+{
+ PRTime timeStarted;
+ PRUintn predictionsFoiled;
+ PRUintn pollingListMax;
+ PRUintn continuationsServed;
+ PRUintn recyclesNeeded;
+ PRUintn quiescentIO;
+ PRUintn locks_created, locks_destroyed;
+ PRUintn locks_acquired, locks_released;
+ PRUintn cvars_created, cvars_destroyed;
+ PRUintn cvars_notified, delayed_cv_deletes;
+} PTDebug;
+
+PR_EXTERN(void) PT_GetStats(PTDebug* here);
+PR_EXTERN(void) PT_FPrintStats(PRFileDesc *fd, const char *msg);
+
+#else
+
+typedef PRUintn PTDebug;
+#define PT_GetStats(_p)
+#define PT_FPrintStats(_fd, _msg)
+
+#endif /* defined(DEBUG) */
+
+#else /* defined(_PR_PTHREADS) */
+
+typedef PRUintn PTDebug;
+#define PT_GetStats(_p)
+#define PT_FPrintStats(_fd, _msg)
+
+/*
+** This section is contains those parts needed to implement NSPR on
+** platforms in general. One would assume that the pthreads implementation
+** included lots of the same types, at least conceptually.
+*/
+
+/*
+ * Local threads only. No multiple CPU support and hence all the
+ * following routines are no-op.
+ */
+#ifdef _PR_LOCAL_THREADS_ONLY
+
+#define _PR_MD_SUSPEND_THREAD(thread)
+#define _PR_MD_RESUME_THREAD(thread)
+#define _PR_MD_SUSPEND_CPU(cpu)
+#define _PR_MD_RESUME_CPU(cpu)
+#define _PR_MD_BEGIN_SUSPEND_ALL()
+#define _PR_MD_END_SUSPEND_ALL()
+#define _PR_MD_BEGIN_RESUME_ALL()
+#define _PR_MD_END_RESUME_ALL()
+#define _PR_MD_INIT_ATTACHED_THREAD(thread) PR_FAILURE
+
+#endif
+
+typedef struct _PRCPUQueue _PRCPUQueue;
+typedef struct _PRCPU _PRCPU;
+typedef struct _MDCPU _MDCPU;
+
+struct _PRCPUQueue {
+ _MDLock runQLock; /* lock for the run + wait queues */
+ _MDLock sleepQLock; /* lock for the run + wait queues */
+ _MDLock miscQLock; /* lock for the run + wait queues */
+
+ PRCList runQ[PR_PRIORITY_LAST + 1]; /* run queue for this CPU */
+ PRUint32 runQReadyMask;
+ PRCList sleepQ;
+ PRIntervalTime sleepQmax;
+ PRCList pauseQ;
+ PRCList suspendQ;
+ PRCList waitingToJoinQ;
+
+ PRUintn numCPUs; /* number of CPUs using this Q */
+};
+
+struct _PRCPU {
+ PRCList links; /* link list of CPUs */
+ PRUint32 id; /* id for this CPU */
+
+ union {
+ PRInt32 bits;
+ PRUint8 missed[4];
+ } u;
+ PRIntn where; /* index into u.missed */
+ PRPackedBool paused; /* cpu is paused */
+ PRPackedBool exit; /* cpu should exit */
+
+ PRThread *thread; /* native thread for this CPUThread */
+ PRThread *idle_thread; /* user-level idle thread for this CPUThread */
+
+ PRIntervalTime last_clock; /* the last time we went into
+ * _PR_ClockInterrupt() on this CPU
+ */
+
+ _PRCPUQueue *queue;
+
+ _MDCPU md;
+};
+
+typedef struct _PRInterruptTable {
+ const char *name;
+ PRUintn missed_bit;
+ void (*handler)(void);
+} _PRInterruptTable;
+
+#define _PR_CPU_PTR(_qp) \
+ ((_PRCPU*) ((char*) (_qp) - offsetof(_PRCPU,links)))
+
+#if !defined(IRIX) && !defined(WIN32)
+#define _MD_GET_ATTACHED_THREAD() (_PR_MD_CURRENT_THREAD())
+#endif
+
+#ifdef _PR_LOCAL_THREADS_ONLY
+
+PR_EXTERN(struct _PRCPU *) _pr_currentCPU;
+PR_EXTERN(PRThread *) _pr_currentThread;
+PR_EXTERN(PRThread *) _pr_lastThread;
+PR_EXTERN(PRInt32) _pr_intsOff;
+
+#define _MD_CURRENT_CPU() (_pr_currentCPU)
+#define _MD_SET_CURRENT_CPU(_cpu) (_pr_currentCPU = (_cpu))
+#define _MD_CURRENT_THREAD() (_pr_currentThread)
+#define _MD_SET_CURRENT_THREAD(_thread) (_pr_currentThread = (_thread))
+#define _MD_LAST_THREAD() (_pr_lastThread)
+#define _MD_SET_LAST_THREAD(t) (_pr_lastThread = t)
+
+#define _MD_GET_INTSOFF() (_pr_intsOff)
+#define _MD_SET_INTSOFF(_val) (_pr_intsOff = _val)
+
+
+/* The unbalanced curly braces in these two macros are intentional */
+#define _PR_LOCK_HEAP() { PRIntn _is; if (_pr_currentCPU) _PR_INTSOFF(_is);
+#define _PR_UNLOCK_HEAP() if (_pr_currentCPU) _PR_INTSON(_is); }
+
+#endif /* _PR_LOCAL_THREADS_ONLY */
+
+#if defined(_PR_GLOBAL_THREADS_ONLY)
+
+#define _MD_GET_INTSOFF() 0
+#define _MD_SET_INTSOFF(_val)
+#define _PR_INTSOFF(_is)
+#define _PR_FAST_INTSON(_is)
+#define _PR_INTSON(_is)
+#define _PR_THREAD_LOCK(_thread)
+#define _PR_THREAD_UNLOCK(_thread)
+#define _PR_RUNQ_LOCK(cpu)
+#define _PR_RUNQ_UNLOCK(cpu)
+#define _PR_SLEEPQ_LOCK(thread)
+#define _PR_SLEEPQ_UNLOCK(thread)
+#define _PR_MISCQ_LOCK(thread)
+#define _PR_MISCQ_UNLOCK(thread)
+#define _PR_CPU_LIST_LOCK()
+#define _PR_CPU_LIST_UNLOCK()
+
+#define _PR_ADD_RUNQ(_thread, _cpu, _pri)
+#define _PR_DEL_RUNQ(_thread)
+#define _PR_ADD_SLEEPQ(_thread, _timeout)
+#define _PR_DEL_SLEEPQ(_thread, _propogate)
+#define _PR_ADD_JOINQ(_thread, _cpu)
+#define _PR_DEL_JOINQ(_thread)
+#define _PR_ADD_SUSPENDQ(_thread, _cpu)
+#define _PR_DEL_SUSPENDQ(_thread)
+
+#define _PR_THREAD_SWITCH_CPU(_thread, _newCPU)
+
+#define _PR_IS_NATIVE_THREAD(thread) 1
+#define _PR_IS_NATIVE_THREAD_SUPPORTED() 1
+
+#else
+
+#define _PR_INTSOFF(_is) \
+ PR_BEGIN_MACRO \
+ (_is) = _PR_MD_GET_INTSOFF(); \
+ _PR_MD_SET_INTSOFF(1); \
+ PR_END_MACRO
+
+#define _PR_FAST_INTSON(_is) \
+ PR_BEGIN_MACRO \
+ _PR_MD_SET_INTSOFF(_is); \
+ PR_END_MACRO
+
+#define _PR_INTSON(_is) \
+ PR_BEGIN_MACRO \
+ if ((_is == 0) && (_PR_MD_CURRENT_CPU())->u.bits) \
+ _PR_IntsOn((_PR_MD_CURRENT_CPU())); \
+ _PR_MD_SET_INTSOFF(_is); \
+ PR_END_MACRO
+
+#ifdef _PR_LOCAL_THREADS_ONLY
+
+#define _PR_IS_NATIVE_THREAD(thread) 0
+#define _PR_THREAD_LOCK(_thread)
+#define _PR_THREAD_UNLOCK(_thread)
+#define _PR_RUNQ_LOCK(cpu)
+#define _PR_RUNQ_UNLOCK(cpu)
+#define _PR_SLEEPQ_LOCK(thread)
+#define _PR_SLEEPQ_UNLOCK(thread)
+#define _PR_MISCQ_LOCK(thread)
+#define _PR_MISCQ_UNLOCK(thread)
+#define _PR_CPU_LIST_LOCK()
+#define _PR_CPU_LIST_UNLOCK()
+
+#define _PR_ADD_RUNQ(_thread, _cpu, _pri) \
+ PR_BEGIN_MACRO \
+ PR_APPEND_LINK(&(_thread)->links, &_PR_RUNQ(_cpu)[_pri]); \
+ _PR_RUNQREADYMASK(_cpu) |= (1L << _pri); \
+ PR_END_MACRO
+
+#define _PR_DEL_RUNQ(_thread) \
+ PR_BEGIN_MACRO \
+ _PRCPU *_cpu = _thread->cpu; \
+ PRInt32 _pri = _thread->priority; \
+ PR_REMOVE_LINK(&(_thread)->links); \
+ if (PR_CLIST_IS_EMPTY(&_PR_RUNQ(_cpu)[_pri])) \
+ _PR_RUNQREADYMASK(_cpu) &= ~(1L << _pri); \
+ PR_END_MACRO
+
+#define _PR_ADD_SLEEPQ(_thread, _timeout) \
+ _PR_AddSleepQ(_thread, _timeout);
+
+#define _PR_DEL_SLEEPQ(_thread, _propogate) \
+ _PR_DelSleepQ(_thread, _propogate);
+
+#define _PR_ADD_JOINQ(_thread, _cpu) \
+ PR_APPEND_LINK(&(_thread)->links, &_PR_WAITINGTOJOINQ(_cpu));
+
+#define _PR_DEL_JOINQ(_thread) \
+ PR_REMOVE_LINK(&(_thread)->links);
+
+#define _PR_ADD_SUSPENDQ(_thread, _cpu) \
+ PR_APPEND_LINK(&(_thread)->links, &_PR_SUSPENDQ(_cpu));
+
+#define _PR_DEL_SUSPENDQ(_thread) \
+ PR_REMOVE_LINK(&(_thread)->links);
+
+#define _PR_THREAD_SWITCH_CPU(_thread, _newCPU)
+
+#define _PR_IS_NATIVE_THREAD_SUPPORTED() 0
+
+#else /* _PR_LOCAL_THREADS_ONLY */
+
+/* These are for the "combined" thread model */
+
+#define _PR_THREAD_LOCK(_thread) \
+ _PR_MD_LOCK(&(_thread)->threadLock);
+
+#define _PR_THREAD_UNLOCK(_thread) \
+ _PR_MD_UNLOCK(&(_thread)->threadLock);
+
+#define _PR_RUNQ_LOCK(_cpu) \
+ PR_BEGIN_MACRO \
+ _PR_MD_LOCK(&(_cpu)->queue->runQLock );\
+ PR_END_MACRO
+
+#define _PR_RUNQ_UNLOCK(_cpu) \
+ PR_BEGIN_MACRO \
+ _PR_MD_UNLOCK(&(_cpu)->queue->runQLock );\
+ PR_END_MACRO
+
+#define _PR_SLEEPQ_LOCK(_cpu) \
+ _PR_MD_LOCK(&(_cpu)->queue->sleepQLock );
+
+#define _PR_SLEEPQ_UNLOCK(_cpu) \
+ _PR_MD_UNLOCK(&(_cpu)->queue->sleepQLock );
+
+#define _PR_MISCQ_LOCK(_cpu) \
+ _PR_MD_LOCK(&(_cpu)->queue->miscQLock );
+
+#define _PR_MISCQ_UNLOCK(_cpu) \
+ _PR_MD_UNLOCK(&(_cpu)->queue->miscQLock );
+
+#define _PR_CPU_LIST_LOCK() _PR_MD_LOCK(&_pr_cpuLock)
+#define _PR_CPU_LIST_UNLOCK() _PR_MD_UNLOCK(&_pr_cpuLock)
+
+#define QUEUE_RUN 0x1
+#define QUEUE_SLEEP 0x2
+#define QUEUE_JOIN 0x4
+#define QUEUE_SUSPEND 0x8
+#define QUEUE_LOCK 0x10
+
+#define _PR_ADD_RUNQ(_thread, _cpu, _pri) \
+ PR_BEGIN_MACRO \
+ PR_APPEND_LINK(&(_thread)->links, &_PR_RUNQ(_cpu)[_pri]); \
+ _PR_RUNQREADYMASK(_cpu) |= (1L << _pri); \
+ PR_ASSERT((_thread)->queueCount == 0); \
+ (_thread)->queueCount = QUEUE_RUN; \
+ PR_END_MACRO
+
+#define _PR_DEL_RUNQ(_thread) \
+ PR_BEGIN_MACRO \
+ _PRCPU *_cpu = _thread->cpu; \
+ PRInt32 _pri = _thread->priority; \
+ PR_REMOVE_LINK(&(_thread)->links); \
+ if (PR_CLIST_IS_EMPTY(&_PR_RUNQ(_cpu)[_pri])) \
+ _PR_RUNQREADYMASK(_cpu) &= ~(1L << _pri); \
+ PR_ASSERT((_thread)->queueCount == QUEUE_RUN);\
+ (_thread)->queueCount = 0; \
+ PR_END_MACRO
+
+#define _PR_ADD_SLEEPQ(_thread, _timeout) \
+ PR_ASSERT((_thread)->queueCount == 0); \
+ (_thread)->queueCount = QUEUE_SLEEP; \
+ _PR_AddSleepQ(_thread, _timeout);
+
+#define _PR_DEL_SLEEPQ(_thread, _propogate) \
+ PR_ASSERT((_thread)->queueCount == QUEUE_SLEEP);\
+ (_thread)->queueCount = 0; \
+ _PR_DelSleepQ(_thread, _propogate);
+
+#define _PR_ADD_JOINQ(_thread, _cpu) \
+ PR_ASSERT((_thread)->queueCount == 0); \
+ (_thread)->queueCount = QUEUE_JOIN; \
+ PR_APPEND_LINK(&(_thread)->links, &_PR_WAITINGTOJOINQ(_cpu));
+
+#define _PR_DEL_JOINQ(_thread) \
+ PR_ASSERT((_thread)->queueCount == QUEUE_JOIN);\
+ (_thread)->queueCount = 0; \
+ PR_REMOVE_LINK(&(_thread)->links);
+
+#define _PR_ADD_SUSPENDQ(_thread, _cpu) \
+ PR_ASSERT((_thread)->queueCount == 0); \
+ (_thread)->queueCount = QUEUE_SUSPEND; \
+ PR_APPEND_LINK(&(_thread)->links, &_PR_SUSPENDQ(_cpu));
+
+#define _PR_DEL_SUSPENDQ(_thread) \
+ PR_ASSERT((_thread)->queueCount == QUEUE_SUSPEND);\
+ (_thread)->queueCount = 0; \
+ PR_REMOVE_LINK(&(_thread)->links);
+
+#define _PR_THREAD_SWITCH_CPU(_thread, _newCPU) \
+ (_thread)->cpu = (_newCPU);
+
+#define _PR_IS_NATIVE_THREAD(thread) (thread->flags & _PR_GLOBAL_SCOPE)
+#define _PR_IS_NATIVE_THREAD_SUPPORTED() 1
+
+#endif /* _PR_LOCAL_THREADS_ONLY */
+
+#endif /* _PR_GLOBAL_THREADS_ONLY */
+
+#define _PR_SET_RESCHED_FLAG() _PR_MD_CURRENT_CPU()->u.missed[3] = 1
+#define _PR_CLEAR_RESCHED_FLAG() _PR_MD_CURRENT_CPU()->u.missed[3] = 0
+
+extern _PRInterruptTable _pr_interruptTable[];
+
+/* Bits for _pr_interruptState.u.missed[0,1] */
+#define _PR_MISSED_CLOCK 0x1
+#define _PR_MISSED_IO 0x2
+#define _PR_MISSED_CHILD 0x4
+
+extern void _PR_IntsOn(_PRCPU *cpu);
+
+PR_EXTERN(void) _PR_WakeupCPU(void);
+PR_EXTERN(void) _PR_PauseCPU(void);
+
+/************************************************************************/
+
+#define _PR_LOCK_LOCK(_lock) \
+ _PR_MD_LOCK(&(_lock)->ilock);
+#define _PR_LOCK_UNLOCK(_lock) \
+ _PR_MD_UNLOCK(&(_lock)->ilock);
+
+extern PRThread * _PR_AssignLock(PRLock *lock);
+
+#define _PR_LOCK_PTR(_qp) \
+ ((PRLock*) ((char*) (_qp) - offsetof(PRLock,links)))
+
+/************************************************************************/
+
+#define _PR_CVAR_LOCK(_cvar) \
+ _PR_MD_LOCK(&(_cvar)->ilock);
+#define _PR_CVAR_UNLOCK(_cvar) \
+ _PR_MD_UNLOCK(&(_cvar)->ilock);
+
+extern PRStatus _PR_WaitCondVar(
+ PRThread *thread, PRCondVar *cvar, PRLock *lock, PRIntervalTime timeout);
+extern PRUint32 _PR_CondVarToString(PRCondVar *cvar, char *buf, PRUint32 buflen);
+
+PR_EXTERN(void) _PR_Notify(PRMonitor *mon, PRBool all, PRBool sticky);
+
+typedef struct _PRPerThreadExit {
+ PRThreadExit func;
+ void *arg;
+} _PRPerThreadExit;
+
+/* PRThread.flags */
+#define _PR_SYSTEM 0x01
+#define _PR_INTERRUPT 0x02
+#define _PR_ATTACHED 0x04 /* created via PR_AttachThread */
+#define _PR_PRIMORDIAL 0x08 /* the thread that called PR_Init */
+#define _PR_ON_SLEEPQ 0x10 /* thread is on the sleepQ */
+#define _PR_ON_PAUSEQ 0x20 /* thread is on the pauseQ */
+#define _PR_SUSPENDING 0x40 /* thread wants to suspend */
+#define _PR_GLOBAL_SCOPE 0x80 /* thread is global scope */
+#define _PR_IDLE_THREAD 0x200 /* this is an idle thread */
+#define _PR_GCABLE_THREAD 0x400 /* this is a collectable thread */
+#define _PR_BOUND_THREAD 0x800 /* a bound thread */
+
+/* PRThread.state */
+#define _PR_UNBORN 0
+#define _PR_RUNNABLE 1
+#define _PR_RUNNING 2
+#define _PR_LOCK_WAIT 3
+#define _PR_COND_WAIT 4
+#define _PR_JOIN_WAIT 5
+#define _PR_IO_WAIT 6
+#define _PR_SUSPENDED 7
+#define _PR_DEAD_STATE 8 /* for debugging */
+
+/* PRThreadStack.flags */
+#define _PR_STACK_VM 0x1 /* using vm instead of malloc */
+#define _PR_STACK_MAPPED 0x2 /* vm is mapped */
+#define _PR_STACK_PRIMORDIAL 0x4 /* stack for primordial thread */
+
+/*
+** If the default stcksize from the client is zero, we need to pick a machine
+** dependent value. This is only for standard user threads. For custom threads,
+** 0 has a special meaning.
+** Adjust stackSize. Round up to a page boundary.
+*/
+#if (!defined(HAVE_CUSTOM_USER_THREADS))
+#define _PR_ADJUST_STACKSIZE(stackSize) \
+ PR_BEGIN_MACRO \
+ if (stackSize == 0) \
+ stackSize = _MD_DEFAULT_STACK_SIZE; \
+ stackSize = (stackSize + (1 << _pr_pageShift) - 1) >> _pr_pageShift; \
+ stackSize <<= _pr_pageShift; \
+ PR_END_MACRO
+#else
+#define _PR_ADJUST_STACKSIZE(stackSize)
+#endif
+
+#define _PR_PENDING_INTERRUPT(_thread) ((_thread)->flags & _PR_INTERRUPT)
+
+#define _PR_THREAD_PTR(_qp) \
+ ((PRThread*) ((char*) (_qp) - offsetof(PRThread,links)))
+
+#define _PR_ACTIVE_THREAD_PTR(_qp) \
+ ((PRThread*) ((char*) (_qp) - offsetof(PRThread,active)))
+
+#define _PR_THREAD_CONDQ_PTR(_qp) \
+ ((PRThread*) ((char*) (_qp) - offsetof(PRThread,waitQLinks)))
+
+#define _PR_THREAD_MD_TO_PTR(_md) \
+ ((PRThread*) ((char*) (_md) - offsetof(PRThread,md)))
+
+#define _PR_THREAD_STACK_TO_PTR(_stack) \
+ ((PRThread*) (_stack->thr))
+
+extern PRCList _pr_active_local_threadQ;
+extern PRCList _pr_active_global_threadQ;
+extern PRCList _pr_cpuQ;
+extern _MDLock _pr_cpuLock;
+extern PRInt32 _pr_md_idle_cpus;
+
+#define _PR_ACTIVE_LOCAL_THREADQ() _pr_active_local_threadQ
+#define _PR_ACTIVE_GLOBAL_THREADQ() _pr_active_global_threadQ
+#define _PR_CPUQ() _pr_cpuQ
+#define _PR_RUNQ(_cpu) ((_cpu)->queue->runQ)
+#define _PR_RUNQREADYMASK(_cpu) ((_cpu)->queue->runQReadyMask)
+#define _PR_SLEEPQ(_cpu) ((_cpu)->queue->sleepQ)
+#define _PR_SLEEPQMAX(_cpu) ((_cpu)->queue->sleepQmax)
+#define _PR_PAUSEQ(_cpu) ((_cpu)->queue->pauseQ)
+#define _PR_SUSPENDQ(_cpu) ((_cpu)->queue->suspendQ)
+#define _PR_WAITINGTOJOINQ(_cpu) ((_cpu)->queue->waitingToJoinQ)
+
+extern PRUint32 _pr_recycleThreads; /* Flag for behavior on thread cleanup */
+extern PRLock *_pr_deadQLock;
+extern PRUint32 _pr_numNativeDead;
+extern PRUint32 _pr_numUserDead;
+extern PRCList _pr_deadNativeQ;
+extern PRCList _pr_deadUserQ;
+#define _PR_DEADNATIVEQ _pr_deadNativeQ
+#define _PR_DEADUSERQ _pr_deadUserQ
+#define _PR_DEADQ_LOCK PR_Lock(_pr_deadQLock);
+#define _PR_DEADQ_UNLOCK PR_Unlock(_pr_deadQLock);
+#define _PR_INC_DEADNATIVE (_pr_numNativeDead++)
+#define _PR_DEC_DEADNATIVE (_pr_numNativeDead--)
+#define _PR_NUM_DEADNATIVE (_pr_numNativeDead)
+#define _PR_INC_DEADUSER (_pr_numUserDead++)
+#define _PR_DEC_DEADUSER (_pr_numUserDead--)
+#define _PR_NUM_DEADUSER (_pr_numUserDead)
+
+extern PRUint32 _pr_utid;
+
+extern struct _PRCPU *_pr_primordialCPU;
+
+extern PRLock *_pr_activeLock; /* lock for userActive and systemActive */
+extern PRInt32 _pr_userActive; /* number of active user threads */
+extern PRInt32 _pr_systemActive; /* number of active system threads */
+extern PRInt32 _pr_primordialExitCount; /* number of user threads left
+ * before the primordial thread
+ * can exit. */
+extern PRCondVar *_pr_primordialExitCVar; /* the condition variable for
+ * notifying the primordial thread
+ * when all other user threads
+ * have terminated. */
+
+extern PRUintn _pr_maxPTDs;
+
+extern PRLock *_pr_terminationCVLock;
+
+/*************************************************************************
+* Internal routines either called by PR itself or from machine-dependent *
+* code. *
+*************************************************************************/
+
+extern void _PR_ClockInterrupt(void);
+
+extern void _PR_Schedule(void);
+extern void _PR_SetThreadPriority(
+ PRThread* thread, PRThreadPriority priority);
+PR_EXTERN(void) _PR_Unlock(PRLock *lock);
+
+PR_EXTERN(void) _PR_SuspendThread(PRThread *t);
+PR_EXTERN(void) _PR_ResumeThread(PRThread *t);
+
+extern PRThreadStack * _PR_NewStack(PRUint32 stackSize);
+extern void _PR_FreeStack(PRThreadStack *stack);
+extern PRBool _PR_NotifyThread (PRThread *thread, PRThread *me);
+extern void _PR_NotifyLockedThread (PRThread *thread);
+
+PR_EXTERN(void) _PR_AddSleepQ(PRThread *thread, PRIntervalTime timeout);
+PR_EXTERN(void) _PR_DelSleepQ(PRThread *thread, PRBool propogate_time);
+
+extern void _PR_AddThreadToRunQ(PRThread *me, PRThread *thread);
+
+PR_EXTERN(PRThread*) _PR_CreateThread(PRThreadType type,
+ void (*start)(void *arg),
+ void *arg,
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize,
+ PRUint32 flags);
+
+extern void _PR_NativeDestroyThread(PRThread *thread);
+extern void _PR_UserDestroyThread(PRThread *thread);
+
+extern PRThread* _PRI_AttachThread(
+ PRThreadType type, PRThreadPriority priority,
+ PRThreadStack *stack, PRUint32 flags);
+
+extern void _PRI_DetachThread(void);
+
+
+#define _PR_IO_PENDING(_thread) ((_thread)->io_pending)
+
+PR_EXTERN(void) _PR_MD_INIT_CPUS();
+#define _PR_MD_INIT_CPUS _MD_INIT_CPUS
+
+PR_EXTERN(void) _PR_MD_WAKEUP_CPUS();
+#define _PR_MD_WAKEUP_CPUS _MD_WAKEUP_CPUS
+
+/* Interrupts related */
+
+PR_EXTERN(void) _PR_MD_STOP_INTERRUPTS(void);
+#define _PR_MD_STOP_INTERRUPTS _MD_STOP_INTERRUPTS
+
+PR_EXTERN(void) _PR_MD_ENABLE_CLOCK_INTERRUPTS(void);
+#define _PR_MD_ENABLE_CLOCK_INTERRUPTS _MD_ENABLE_CLOCK_INTERRUPTS
+
+PR_EXTERN(void) _PR_MD_DISABLE_CLOCK_INTERRUPTS(void);
+#define _PR_MD_DISABLE_CLOCK_INTERRUPTS _MD_DISABLE_CLOCK_INTERRUPTS
+
+PR_EXTERN(void) _PR_MD_BLOCK_CLOCK_INTERRUPTS(void);
+#define _PR_MD_BLOCK_CLOCK_INTERRUPTS _MD_BLOCK_CLOCK_INTERRUPTS
+
+PR_EXTERN(void) _PR_MD_UNBLOCK_CLOCK_INTERRUPTS(void);
+#define _PR_MD_UNBLOCK_CLOCK_INTERRUPTS _MD_UNBLOCK_CLOCK_INTERRUPTS
+
+/* The _PR_MD_WAIT_LOCK and _PR_MD_WAKEUP_WAITER functions put to sleep and
+ * awaken a thread which is waiting on a lock or cvar.
+ */
+extern PRStatus _PR_MD_WAIT(PRThread *, PRIntervalTime timeout);
+#define _PR_MD_WAIT _MD_WAIT
+
+extern PRStatus _PR_MD_WAKEUP_WAITER(PRThread *);
+#define _PR_MD_WAKEUP_WAITER _MD_WAKEUP_WAITER
+
+#ifndef _PR_LOCAL_THREADS_ONLY /* not if only local threads supported */
+PR_EXTERN(void) _PR_MD_CLOCK_INTERRUPT(void);
+#define _PR_MD_CLOCK_INTERRUPT _MD_CLOCK_INTERRUPT
+#endif
+
+/* Stack debugging */
+PR_EXTERN(void) _PR_MD_INIT_STACK(PRThreadStack *ts, PRIntn redzone);
+#define _PR_MD_INIT_STACK _MD_INIT_STACK
+
+PR_EXTERN(void) _PR_MD_CLEAR_STACK(PRThreadStack* ts);
+#define _PR_MD_CLEAR_STACK _MD_CLEAR_STACK
+
+/* CPU related */
+PR_EXTERN(PRInt32) _PR_MD_GET_INTSOFF(void);
+#define _PR_MD_GET_INTSOFF _MD_GET_INTSOFF
+
+PR_EXTERN(void) _PR_MD_SET_INTSOFF(PRInt32 _val);
+#define _PR_MD_SET_INTSOFF _MD_SET_INTSOFF
+
+PR_EXTERN(_PRCPU*) _PR_MD_CURRENT_CPU(void);
+#define _PR_MD_CURRENT_CPU _MD_CURRENT_CPU
+
+PR_EXTERN(void) _PR_MD_SET_CURRENT_CPU(_PRCPU *cpu);
+#define _PR_MD_SET_CURRENT_CPU _MD_SET_CURRENT_CPU
+
+PR_EXTERN(void) _PR_MD_INIT_RUNNING_CPU(_PRCPU *cpu);
+#define _PR_MD_INIT_RUNNING_CPU _MD_INIT_RUNNING_CPU
+
+/*
+ * Returns the number of threads awoken or 0 if a timeout occurred;
+ */
+extern PRInt32 _PR_MD_PAUSE_CPU(PRIntervalTime timeout);
+#define _PR_MD_PAUSE_CPU _MD_PAUSE_CPU
+
+extern void _PR_MD_CLEANUP_BEFORE_EXIT(void);
+#define _PR_MD_CLEANUP_BEFORE_EXIT _MD_CLEANUP_BEFORE_EXIT
+
+extern void _PR_MD_EXIT(PRIntn status);
+#define _PR_MD_EXIT _MD_EXIT
+
+/* Locks related */
+
+PR_EXTERN(PRStatus) _PR_MD_NEW_LOCK(_MDLock *md);
+#define _PR_MD_NEW_LOCK _MD_NEW_LOCK
+
+PR_EXTERN(void) _PR_MD_FREE_LOCK(_MDLock *md);
+#define _PR_MD_FREE_LOCK _MD_FREE_LOCK
+
+PR_EXTERN(void) _PR_MD_LOCK(_MDLock *md);
+#define _PR_MD_LOCK _MD_LOCK
+
+PR_EXTERN(PRBool) _PR_MD_TEST_AND_LOCK(_MDLock *md);
+#define _PR_MD_TEST_AND_LOCK _MD_TEST_AND_LOCK
+
+PR_EXTERN(void) _PR_MD_UNLOCK(_MDLock *md);
+#define _PR_MD_UNLOCK _MD_UNLOCK
+
+PR_EXTERN(void) _PR_MD_IOQ_LOCK(void);
+#define _PR_MD_IOQ_LOCK _MD_IOQ_LOCK
+
+PR_EXTERN(void) _PR_MD_IOQ_UNLOCK(void);
+#define _PR_MD_IOQ_UNLOCK _MD_IOQ_UNLOCK
+
+#ifndef _PR_LOCAL_THREADS_ONLY /* not if only local threads supported */
+/* Semaphore related -- only for native threads */
+#ifdef HAVE_CVAR_BUILT_ON_SEM
+PR_EXTERN(void) _PR_MD_NEW_SEM(_MDSemaphore *md, PRUintn value);
+#define _PR_MD_NEW_SEM _MD_NEW_SEM
+
+PR_EXTERN(void) _PR_MD_DESTROY_SEM(_MDSemaphore *md);
+#define _PR_MD_DESTROY_SEM _MD_DESTROY_SEM
+
+PR_EXTERN(PRStatus) _PR_MD_TIMED_WAIT_SEM(
+ _MDSemaphore *md, PRIntervalTime timeout);
+#define _PR_MD_TIMED_WAIT_SEM _MD_TIMED_WAIT_SEM
+
+PR_EXTERN(PRStatus) _PR_MD_WAIT_SEM(_MDSemaphore *md);
+#define _PR_MD_WAIT_SEM _MD_WAIT_SEM
+
+PR_EXTERN(void) _PR_MD_POST_SEM(_MDSemaphore *md);
+#define _PR_MD_POST_SEM _MD_POST_SEM
+#endif /* HAVE_CVAR_BUILT_ON_SEM */
+
+#endif
+
+/* Condition Variables related -- only for native threads */
+
+#ifndef _PR_LOCAL_THREADS_ONLY /* not if only local threads supported */
+PR_EXTERN(PRInt32) _PR_MD_NEW_CV(_MDCVar *md);
+#define _PR_MD_NEW_CV _MD_NEW_CV
+
+PR_EXTERN(void) _PR_MD_FREE_CV(_MDCVar *md);
+#define _PR_MD_FREE_CV _MD_FREE_CV
+
+PR_EXTERN(void) _PR_MD_WAIT_CV(
+ _MDCVar *mdCVar,_MDLock *mdLock,PRIntervalTime timeout);
+#define _PR_MD_WAIT_CV _MD_WAIT_CV
+
+PR_EXTERN(void) _PR_MD_NOTIFY_CV(_MDCVar *md, _MDLock *lock);
+#define _PR_MD_NOTIFY_CV _MD_NOTIFY_CV
+
+PR_EXTERN(void) _PR_MD_NOTIFYALL_CV(_MDCVar *md, _MDLock *lock);
+#define _PR_MD_NOTIFYALL_CV _MD_NOTIFYALL_CV
+#endif /* _PR_LOCAL_THREADS_ONLY */
+
+/* Threads related */
+PR_EXTERN(PRThread*) _PR_MD_CURRENT_THREAD(void);
+#define _PR_MD_CURRENT_THREAD _MD_CURRENT_THREAD
+
+PR_EXTERN(PRThread*) _PR_MD_GET_ATTACHED_THREAD(void);
+#define _PR_MD_GET_ATTACHED_THREAD _MD_GET_ATTACHED_THREAD
+
+PR_EXTERN(PRThread*) _PR_MD_LAST_THREAD(void);
+#define _PR_MD_LAST_THREAD _MD_LAST_THREAD
+
+PR_EXTERN(void) _PR_MD_SET_CURRENT_THREAD(PRThread *thread);
+#define _PR_MD_SET_CURRENT_THREAD _MD_SET_CURRENT_THREAD
+
+PR_EXTERN(void) _PR_MD_SET_LAST_THREAD(PRThread *thread);
+#define _PR_MD_SET_LAST_THREAD _MD_SET_LAST_THREAD
+
+extern PRStatus _PR_MD_INIT_THREAD(PRThread *thread);
+#define _PR_MD_INIT_THREAD _MD_INIT_THREAD
+
+extern void _PR_MD_EXIT_THREAD(PRThread *thread);
+#define _PR_MD_EXIT_THREAD _MD_EXIT_THREAD
+
+#ifndef _PR_LOCAL_THREADS_ONLY /* not if only local threads supported */
+
+PR_EXTERN(PRStatus) _PR_MD_INIT_ATTACHED_THREAD(PRThread *thread);
+#define _PR_MD_INIT_ATTACHED_THREAD _MD_INIT_ATTACHED_THREAD
+
+extern void _PR_MD_SUSPEND_THREAD(PRThread *thread);
+#define _PR_MD_SUSPEND_THREAD _MD_SUSPEND_THREAD
+
+extern void _PR_MD_RESUME_THREAD(PRThread *thread);
+#define _PR_MD_RESUME_THREAD _MD_RESUME_THREAD
+
+extern void _PR_MD_SUSPEND_CPU(_PRCPU *cpu);
+#define _PR_MD_SUSPEND_CPU _MD_SUSPEND_CPU
+
+extern void _PR_MD_RESUME_CPU(_PRCPU *cpu);
+#define _PR_MD_RESUME_CPU _MD_RESUME_CPU
+
+extern void _PR_MD_BEGIN_SUSPEND_ALL(void);
+#define _PR_MD_BEGIN_SUSPEND_ALL _MD_BEGIN_SUSPEND_ALL
+
+extern void _PR_MD_END_SUSPEND_ALL(void);
+#define _PR_MD_END_SUSPEND_ALL _MD_END_SUSPEND_ALL
+
+extern void _PR_MD_BEGIN_RESUME_ALL(void);
+#define _PR_MD_BEGIN_RESUME_ALL _MD_BEGIN_RESUME_ALL
+
+extern void _PR_MD_END_RESUME_ALL(void);
+#define _PR_MD_END_RESUME_ALL _MD_END_RESUME_ALL
+
+#if defined(IRIX)
+PR_EXTERN(void) _PR_IRIX_CHILD_PROCESS(void);
+#endif /* IRIX */
+
+#endif /* !_PR_LOCAL_THREADS_ONLY */
+
+extern void _PR_MD_CLEAN_THREAD(PRThread *thread);
+#define _PR_MD_CLEAN_THREAD _MD_CLEAN_THREAD
+
+#ifdef HAVE_CUSTOM_USER_THREADS
+extern void _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(PRThread *);
+#define _PR_MD_CREATE_PRIMORDIAL_USER_THREAD _MD_CREATE_PRIMORDIAL_USER_THREAD
+
+extern PRThread* _PR_MD_CREATE_USER_THREAD(
+ PRUint32 stacksize,
+ void (*start)(void *),
+ void *arg);
+#define _PR_MD_CREATE_USER_THREAD _MD_CREATE_USER_THREAD
+#endif
+
+extern void _PR_MD_INIT_PRIMORDIAL_THREAD(PRThread *thread);
+#define _PR_MD_INIT_PRIMORDIAL_THREAD _MD_INIT_PRIMORDIAL_THREAD
+
+extern PRStatus _PR_MD_CREATE_THREAD(
+ PRThread *thread,
+ void (*start) (void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize);
+#define _PR_MD_CREATE_THREAD _MD_CREATE_THREAD
+
+extern void _PR_MD_YIELD(void);
+#define _PR_MD_YIELD _MD_YIELD
+
+extern void _PR_MD_SET_PRIORITY(_MDThread *md, PRThreadPriority newPri);
+#define _PR_MD_SET_PRIORITY _MD_SET_PRIORITY
+
+PR_EXTERN(void) _PR_MD_SUSPENDALL(void);
+#define _PR_MD_SUSPENDALL _MD_SUSPENDALL
+
+PR_EXTERN(void) _PR_MD_RESUMEALL(void);
+#define _PR_MD_RESUMEALL _MD_RESUMEALL
+
+extern void _PR_MD_INIT_CONTEXT(
+ PRThread *thread, char *top, void (*start) (void), PRBool *status);
+#define _PR_MD_INIT_CONTEXT _MD_INIT_CONTEXT
+
+extern void _PR_MD_SWITCH_CONTEXT(PRThread *thread);
+#define _PR_MD_SWITCH_CONTEXT _MD_SWITCH_CONTEXT
+
+extern void _PR_MD_RESTORE_CONTEXT(PRThread *thread);
+#define _PR_MD_RESTORE_CONTEXT _MD_RESTORE_CONTEXT
+
+/* Directory enumeration related */
+extern PRStatus _PR_MD_OPEN_DIR(_MDDir *md,const char *name);
+#define _PR_MD_OPEN_DIR _MD_OPEN_DIR
+
+extern char * _PR_MD_READ_DIR(_MDDir *md, PRIntn flags);
+#define _PR_MD_READ_DIR _MD_READ_DIR
+
+extern PRInt32 _PR_MD_CLOSE_DIR(_MDDir *md);
+#define _PR_MD_CLOSE_DIR _MD_CLOSE_DIR
+
+/* I/O related */
+extern void _PR_MD_MAKE_NONBLOCK(PRFileDesc *fd);
+#define _PR_MD_MAKE_NONBLOCK _MD_MAKE_NONBLOCK
+
+/* File I/O related */
+extern PRInt32 _PR_MD_OPEN(const char *name, PRIntn osflags, PRIntn mode);
+#define _PR_MD_OPEN _MD_OPEN
+
+extern PRInt32 _PR_MD_CLOSE_FILE(PRInt32 osfd);
+#define _PR_MD_CLOSE_FILE _MD_CLOSE_FILE
+
+extern PRInt32 _PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 amount);
+#define _PR_MD_READ _MD_READ
+
+extern PRInt32 _PR_MD_WRITE(PRFileDesc *fd, const void *buf, PRInt32 amount);
+#define _PR_MD_WRITE _MD_WRITE
+
+extern PRInt32 _PR_MD_WRITEV(
+ PRFileDesc *fd, struct PRIOVec *iov,
+ PRInt32 iov_size, PRIntervalTime timeout);
+#define _PR_MD_WRITEV _MD_WRITEV
+
+extern PRInt32 _PR_MD_FSYNC(PRFileDesc *fd);
+#define _PR_MD_FSYNC _MD_FSYNC
+
+extern PRInt32 _PR_MD_DELETE(const char *name);
+#define _PR_MD_DELETE _MD_DELETE
+
+extern PRInt32 _PR_MD_RENAME(const char *from, const char *to);
+#define _PR_MD_RENAME _MD_RENAME
+
+extern PRInt32 _PR_MD_ACCESS(const char *name, PRAccessHow how);
+#define _PR_MD_ACCESS _MD_ACCESS
+
+extern PRInt32 _PR_MD_STAT(const char *name, struct stat *buf);
+#define _PR_MD_STAT _MD_STAT
+
+extern PRInt32 _PR_MD_MKDIR(const char *name, PRIntn mode);
+#define _PR_MD_MKDIR _MD_MKDIR
+
+extern PRInt32 _PR_MD_RMDIR(const char *name);
+#define _PR_MD_RMDIR _MD_RMDIR
+
+/* Socket I/O related */
+extern void _PR_MD_INIT_IO(void);
+#define _PR_MD_INIT_IO _MD_INIT_IO
+
+extern PRInt32 _PR_MD_CLOSE_SOCKET(PRInt32 osfd);
+#define _PR_MD_CLOSE_SOCKET _MD_CLOSE_SOCKET
+
+extern PRInt32 _PR_MD_CONNECT(
+ PRFileDesc *fd, const PRNetAddr *addr,
+ PRUint32 addrlen, PRIntervalTime timeout);
+#define _PR_MD_CONNECT _MD_CONNECT
+
+extern PRInt32 _PR_MD_ACCEPT(
+ PRFileDesc *fd, PRNetAddr *addr,
+ PRUint32 *addrlen, PRIntervalTime timeout);
+#define _PR_MD_ACCEPT _MD_ACCEPT
+
+extern PRInt32 _PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen);
+#define _PR_MD_BIND _MD_BIND
+
+extern PRInt32 _PR_MD_LISTEN(PRFileDesc *fd, PRIntn backlog);
+#define _PR_MD_LISTEN _MD_LISTEN
+
+extern PRInt32 _PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how);
+#define _PR_MD_SHUTDOWN _MD_SHUTDOWN
+
+extern PRInt32 _PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout);
+#define _PR_MD_RECV _MD_RECV
+
+extern PRInt32 _PR_MD_SEND(
+ PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout);
+#define _PR_MD_SEND _MD_SEND
+
+extern PRInt32 _PR_MD_ACCEPT_READ(PRFileDesc *sd, PRInt32 *newSock,
+ PRNetAddr **raddr, void *buf, PRInt32 amount,
+ PRIntervalTime timeout);
+#define _PR_MD_ACCEPT_READ _MD_ACCEPT_READ
+extern PRInt32 _PR_EmulateAcceptRead(PRFileDesc *sd, PRFileDesc **nd,
+ PRNetAddr **raddr, void *buf, PRInt32 amount,
+ PRIntervalTime timeout);
+
+#ifdef WIN32
+extern PRInt32 _PR_MD_FAST_ACCEPT(PRFileDesc *fd, PRNetAddr *addr,
+ PRUint32 *addrlen, PRIntervalTime timeout,
+ PRBool fast,
+ _PR_AcceptTimeoutCallback callback,
+ void *callbackArg);
+
+extern PRInt32 _PR_MD_FAST_ACCEPT_READ(PRFileDesc *sd, PRInt32 *newSock,
+ PRNetAddr **raddr, void *buf, PRInt32 amount,
+ PRIntervalTime timeout, PRBool fast,
+ _PR_AcceptTimeoutCallback callback,
+ void *callbackArg);
+
+extern void _PR_MD_UPDATE_ACCEPT_CONTEXT(PRInt32 s, PRInt32 ls);
+#define _PR_MD_UPDATE_ACCEPT_CONTEXT _MD_UPDATE_ACCEPT_CONTEXT
+#endif /* WIN32 */
+
+extern PRInt32 _PR_MD_TRANSMITFILE(
+ PRFileDesc *sock, PRFileDesc *file,
+ const void *headers, PRInt32 hlen, PRInt32 flags,
+ PRIntervalTime timeout);
+#define _PR_MD_TRANSMITFILE _MD_TRANSMITFILE
+extern PRInt32 _PR_EmulateTransmitFile(PRFileDesc *sd, PRFileDesc *fd,
+ const void *headers, PRInt32 hlen, PRTransmitFileFlags flags,
+ PRIntervalTime timeout);
+
+extern PRStatus _PR_MD_GETSOCKNAME(
+ PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen);
+#define _PR_MD_GETSOCKNAME _MD_GETSOCKNAME
+
+extern PRStatus _PR_MD_GETPEERNAME(
+ PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen);
+#define _PR_MD_GETPEERNAME _MD_GETPEERNAME
+
+extern PRStatus _PR_MD_GETSOCKOPT(
+ PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen);
+#define _PR_MD_GETSOCKOPT _MD_GETSOCKOPT
+
+extern PRStatus _PR_MD_SETSOCKOPT(
+ PRFileDesc *fd, PRInt32 level, PRInt32 optname,
+ const char* optval, PRInt32 optlen);
+#define _PR_MD_SETSOCKOPT _MD_SETSOCKOPT
+
+extern PRStatus PR_CALLBACK _PR_SocketGetSocketOption(
+ PRFileDesc *fd, PRSocketOptionData *data);
+
+extern PRStatus PR_CALLBACK _PR_SocketSetSocketOption(
+ PRFileDesc *fd, const PRSocketOptionData *data);
+
+extern PRInt32 _PR_MD_RECVFROM(
+ PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout);
+#define _PR_MD_RECVFROM _MD_RECVFROM
+
+extern PRInt32 _PR_MD_SENDTO(
+ PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout);
+#define _PR_MD_SENDTO _MD_SENDTO
+
+extern PRInt32 _PR_MD_SOCKETPAIR(int af, int type, int flags, PRInt32 *osfd);
+#define _PR_MD_SOCKETPAIR _MD_SOCKETPAIR
+
+extern PRInt32 _PR_MD_SOCKET(int af, int type, int flags);
+#define _PR_MD_SOCKET _MD_SOCKET
+
+extern PRInt32 _PR_MD_SOCKETAVAILABLE(PRFileDesc *fd);
+#define _PR_MD_SOCKETAVAILABLE _MD_SOCKETAVAILABLE
+
+extern PRInt32 _PR_MD_PR_POLL(PRPollDesc *pds, PRIntn npds,
+ PRIntervalTime timeout);
+#define _PR_MD_PR_POLL _MD_PR_POLL
+
+
+#define _PR_PROCESS_TIMEOUT_INTERRUPT_ERRORS(me) \
+ if (_PR_PENDING_INTERRUPT(me)) { \
+ me->flags &= ~_PR_INTERRUPT; \
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); \
+ } else { \
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0); \
+ }
+
+#ifndef NO_NSPR_10_SUPPORT
+
+PR_EXTERN(void *) _PR_MD_GET_SP(PRThread *thread);
+#define _PR_MD_GET_SP _MD_GET_SP
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* defined(_PR_PTHREADS) */
+
+/************************************************************************/
+/*************************************************************************
+** The remainder of the definitions are shared by pthreads and the classic
+** NSPR code. These too may be conditionalized.
+*************************************************************************/
+/************************************************************************/
+
+extern PRInt32 _PR_MD_LSEEK(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence);
+#define _PR_MD_LSEEK _MD_LSEEK
+
+extern PRInt64 _PR_MD_LSEEK64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence);
+#define _PR_MD_LSEEK64 _MD_LSEEK64
+
+extern PRInt32 _PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info);
+#define _PR_MD_GETFILEINFO _MD_GETFILEINFO
+
+extern PRInt32 _PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info);
+#define _PR_MD_GETFILEINFO64 _MD_GETFILEINFO64
+
+extern PRInt32 _PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info);
+#define _PR_MD_GETOPENFILEINFO _MD_GETOPENFILEINFO
+
+extern PRInt32 _PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info);
+#define _PR_MD_GETOPENFILEINFO64 _MD_GETOPENFILEINFO64
+
+
+/*****************************************************************************/
+/************************** File descriptor caching **************************/
+/*****************************************************************************/
+extern void _PR_InitFdCache(void);
+extern void _PR_CleanupFdCache(void);
+extern PRFileDesc *_PR_Getfd(void);
+extern void _PR_Putfd(PRFileDesc *fd);
+
+/*
+ * These flags are used by NSPR temporarily in the poll
+ * descriptor's out_flags field to record the mapping of
+ * NSPR's poll flags to the system poll flags.
+ *
+ * If _PR_POLL_READ_SYS_WRITE bit is set, it means the
+ * PR_POLL_READ flag specified by the topmost layer is
+ * mapped to the WRITE flag at the system layer. Similarly
+ * for the other three _PR_POLL_XXX_SYS_YYY flags. It is
+ * assumed that the PR_POLL_EXCEPT flag doesn't get mapped
+ * to other flags.
+ */
+#define _PR_POLL_READ_SYS_READ 0x1
+#define _PR_POLL_READ_SYS_WRITE 0x2
+#define _PR_POLL_WRITE_SYS_READ 0x4
+#define _PR_POLL_WRITE_SYS_WRITE 0x8
+
+/*
+** These methods are coerced into file descriptor methods table
+** when the intended service is inappropriate for the particular
+** type of file descriptor.
+*/
+extern PRIntn _PR_InvalidInt(void);
+extern PRInt16 _PR_InvalidInt16(void);
+extern PRInt64 _PR_InvalidInt64(void);
+extern PRStatus _PR_InvalidStatus(void);
+extern PRFileDesc *_PR_InvalidDesc(void);
+
+extern PRIOMethods _pr_faulty_methods;
+
+extern PRStatus _PR_MapOptionName(
+ PRSockOption optname, PRInt32 *level, PRInt32 *name);
+extern void _PR_InitThreads(
+ PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs);
+
+PR_EXTERN(void) _PR_MD_START_INTERRUPTS(void);
+#define _PR_MD_START_INTERRUPTS _MD_START_INTERRUPTS
+
+PR_EXTERN(void) _PR_MD_INIT_LOCKS(void);
+#define _PR_MD_INIT_LOCKS _MD_INIT_LOCKS
+
+struct PRLock {
+#if defined(_PR_PTHREADS)
+ pthread_mutex_t mutex; /* the underlying lock */
+ _PT_Notified notified; /* array of conditions notified */
+ pthread_t owner; /* current lock owner */
+#else /* defined(_PR_PTHREADS) */
+ PRCList links; /* linkage for PRThread.lockList */
+ struct PRThread *owner; /* current lock owner */
+ PRCList waitQ; /* list of threads waiting for lock */
+ PRThreadPriority priority; /* priority of lock */
+ PRThreadPriority boostPriority; /* boosted priority of lock owner */
+ _MDLock ilock; /* Internal Lock to protect user-level fields */
+#endif /* defined(_PR_PTHREADS) */
+};
+
+extern void _PR_InitLocks(void);
+
+struct PRCondVar {
+ PRLock *lock; /* associated lock that protects the condition */
+#if defined(_PR_PTHREADS)
+ pthread_cond_t cv; /* underlying pthreads condition */
+ PRInt32 notify_pending; /* CV has destroy pending notification */
+#else /* defined(_PR_PTHREADS) */
+ PRCList condQ; /* Condition variable wait Q */
+ _MDLock ilock; /* Internal Lock to protect condQ */
+ _MDCVar md;
+#endif /* defined(_PR_PTHREADS) */
+};
+
+/************************************************************************/
+
+struct PRMonitor {
+ const char* name; /* monitor name for debugging */
+#if defined(_PR_PTHREADS)
+ PRLock lock; /* the lock struture structure */
+ pthread_t owner; /* the owner of the lock or zero */
+ PRCondVar *cvar; /* condition variable queue */
+#else /* defined(_PR_PTHREADS) */
+ PRCondVar *cvar; /* associated lock and condition variable queue */
+#endif /* defined(_PR_PTHREADS) */
+ PRUint32 entryCount; /* # of times re-entered */
+};
+
+/************************************************************************/
+
+struct PRSemaphore {
+ PRCondVar *cvar; /* associated lock and condition variable queue */
+ PRUintn count; /* the value of the counting semaphore */
+ PRUint32 waiters; /* threads waiting on the semaphore */
+#if defined(_PR_PTHREADS)
+#else /* defined(_PR_PTHREADS) */
+ _MDSemaphore md;
+#endif /* defined(_PR_PTHREADS) */
+};
+
+PR_EXTERN(void) _PR_InitSem(void);
+
+/************************************************************************/
+
+/* XXX this needs to be exported (sigh) */
+struct PRThreadStack {
+ PRCList links;
+ PRUintn flags;
+
+ char *allocBase; /* base of stack's allocated memory */
+ PRUint32 allocSize; /* size of stack's allocated memory */
+ char *stackBottom; /* bottom of stack from C's point of view */
+ char *stackTop; /* top of stack from C's point of view */
+ PRUint32 stackSize; /* size of usable portion of the stack */
+
+ PRSegment *seg;
+ PRThread* thr; /* back pointer to thread owning this stack */
+
+#if defined(_PR_PTHREADS)
+#else /* defined(_PR_PTHREADS) */
+ _MDThreadStack md;
+#endif /* defined(_PR_PTHREADS) */
+};
+
+/*
+ * Thread private data destructor array
+ * There is a destructor (or NULL) associated with each key and
+ * applied to all threads known to the system.
+ * Storage allocated in prtpd.c.
+ */
+extern PRThreadPrivateDTOR *_pr_tpd_destructors;
+extern void _PR_DestroyThreadPrivate(PRThread*);
+
+typedef void (PR_CALLBACK *_PRStartFn)(void *);
+
+struct PRThread {
+ PRUint32 state; /* thread's creation state */
+ PRThreadPriority priority; /* apparent priority, loosly defined */
+
+ void *arg; /* argument to the client's entry point */
+ _PRStartFn startFunc; /* the root of the client's thread */
+
+ PRThreadStack *stack; /* info about thread's stack (for GC) */
+ void *environment; /* pointer to execution environment */
+
+ PRThreadDumpProc dump; /* dump thread info out */
+ void *dumpArg; /* argument for the dump function */
+
+ /*
+ ** Per thread private data
+ */
+ PRUint32 tpdLength; /* thread's current vector length */
+ void **privateData; /* private data vector or NULL */
+ PRInt32 errorStringSize; /* byte length of current error string | zero */
+ PRErrorCode errorCode; /* current NSPR error code | zero */
+ PRInt32 osErrorCode; /* mapping of errorCode | zero */
+ char *errorString; /* current error string | NULL */
+
+#if defined(_PR_PTHREADS)
+ pthread_t id; /* pthread identifier for the thread */
+ PRBool okToDelete; /* ok to delete the PRThread struct? */
+ PRCondVar *io_cv; /* a condition used to run i/o */
+ PRCondVar *waiting; /* where the thread is waiting | NULL */
+ void *sp; /* recorded sp for garbage collection */
+ PRThread *next, *prev; /* simple linked list of all threads */
+ PRUint32 suspend; /* used to store suspend and resume flags */
+#ifdef PT_NO_SIGTIMEDWAIT
+ pthread_mutex_t suspendResumeMutex;
+ pthread_cond_t suspendResumeCV;
+#endif
+#else /* defined(_PR_PTHREADS) */
+ _MDLock threadLock; /* Lock to protect thread state variables.
+ * Protects the following fields:
+ * state
+ * priority
+ * links
+ * wait
+ * cpu
+ */
+ PRUint32 queueCount;
+ PRUint32 waitCount;
+
+ PRCList active; /* on list of all active threads */
+ PRCList links;
+ PRCList waitQLinks; /* when thread is PR_Wait'ing */
+ PRCList lockList; /* list of locks currently holding */
+ PRIntervalTime sleep; /* sleep time when thread is sleeping */
+ struct _wait {
+ struct PRLock *lock;
+ struct PRCondVar *cvar;
+ } wait;
+
+ PRUint32 id;
+ PRUint32 flags;
+ PRUint32 no_sched; /* Don't schedule the thread to run.
+ * This flag has relevance only when
+ * multiple NSPR CPUs are created.
+ * When a thread is de-scheduled, there
+ * is a narrow window of time in which
+ * the thread is put on the run queue
+ * but the scheduler is actually using
+ * the stack of this thread. It is safe
+ * to run this thread on a different CPU
+ * only when its stack is not in use on
+ * any other CPU. The no_sched flag is
+ * set during this interval to prevent
+ * the thread from being scheduled on a
+ * different CPU.
+ */
+ PRUint32 numExits;
+ _PRPerThreadExit *ptes;
+
+ /* thread termination condition variable for join */
+ PRCondVar *term;
+
+ _PRCPU *cpu; /* cpu to which this thread is bound */
+ PRUint32 threadAllocatedOnStack;/* boolean */
+
+ /* When an async IO is in progress and a second async IO cannot be
+ * initiated, the io_pending flag is set to true. Some platforms will
+ * not use the io_pending flag. If the io_pending flag is true, then
+ * io_fd is the OS-file descriptor on which IO is pending.
+ */
+ PRBool io_pending;
+ PRInt32 io_fd;
+
+ /* If a timeout occurs or if an outstanding IO is interrupted and the
+ * OS doesn't support a real cancellation (NT or MAC), then the
+ * io_suspended flag will be set to true. The thread will be resumed
+ * but may run into trouble issuing additional IOs until the io_pending
+ * flag can be cleared
+ */
+ PRBool io_suspended;
+
+ _MDThread md;
+#endif /* defined(_PR_PTHREADS) */
+};
+
+struct PRProcessAttr {
+ PRFileDesc *stdinFd;
+ PRFileDesc *stdoutFd;
+ PRFileDesc *stderrFd;
+ char *currentDirectory;
+};
+
+struct PRProcess {
+ _MDProcess md;
+};
+
+struct PRFileMap {
+ PRFileDesc *fd;
+ PRFileMapProtect prot;
+ _MDFileMap md;
+};
+
+/************************************************************************/
+
+struct PRFilePrivate {
+ PRInt32 state;
+ PRBool nonblocking;
+ PRFileDesc *next;
+ PRIntn lockCount;
+ _MDFileDesc md;
+};
+
+struct PRDir {
+ PRDirEntry d;
+ _MDDir md;
+};
+
+extern void _PR_InitSegs(void);
+extern void _PR_InitStacks(void);
+extern void _PR_InitTPD(void);
+extern void _PR_InitMem(void);
+extern void _PR_InitEnv(void);
+extern void _PR_InitCMon(void);
+extern void _PR_InitIO(void);
+extern void _PR_InitLog(void);
+extern void _PR_InitNet(void);
+extern void _PR_InitClock(void);
+extern void _PR_InitLinker(void);
+extern void _PR_InitAtomic(void);
+extern void _PR_InitCPUs(void);
+extern void _PR_InitDtoa(void);
+extern void _PR_InitMW(void);
+extern void _PR_NotifyCondVar(PRCondVar *cvar, PRThread *me);
+extern void _PR_CleanupThread(PRThread *thread);
+extern void _PR_CleanupTPD(void);
+extern void _PR_Cleanup(void);
+extern void _PR_LogCleanup(void);
+extern void _PR_InitLayerCache(void);
+
+extern PRBool _pr_initialized;
+extern void _PR_ImplicitInitialization(void);
+extern PRBool _PR_Obsolete(const char *obsolete, const char *preferred);
+
+/************************************************************************/
+
+struct PRSegment {
+ void *vaddr;
+ PRUint32 size;
+ PRUintn flags;
+#if defined(_PR_PTHREADS)
+#else /* defined(_PR_PTHREADS) */
+ _MDSegment md;
+#endif /* defined(_PR_PTHREADS) */
+};
+
+/* PRSegment.flags */
+#define _PR_SEG_VM 0x1
+
+/***********************************************************************
+** FUNCTION: _PR_NewSegment()
+** DESCRIPTION:
+** 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.
+** INPUTS: size: size of the desired memory segment
+** vaddr: address at which the newly aquired segment is to be
+** mapped into memory.
+** OUTPUTS: a memory segment is allocated, a PRSegment is allocated
+** RETURN: pointer to PRSegment
+***********************************************************************/
+extern PRSegment* _PR_NewSegment(PRUint32 size, void *vaddr);
+
+/***********************************************************************
+** FUNCTION: _PR_DestroySegment()
+** DESCRIPTION:
+** The memory segment and the PRSegment are freed
+** INPUTS: seg: pointer to PRSegment to be freed
+** OUTPUTS: the the PRSegment and its associated memory segment are freed
+** RETURN: void
+***********************************************************************/
+extern void _PR_DestroySegment(PRSegment *seg);
+
+/************************************************************************/
+
+extern PRInt32 _pr_pageSize;
+extern PRInt32 _pr_pageShift;
+
+extern PRLogModuleInfo *_pr_clock_lm;
+extern PRLogModuleInfo *_pr_cmon_lm;
+extern PRLogModuleInfo *_pr_io_lm;
+extern PRLogModuleInfo *_pr_cvar_lm;
+extern PRLogModuleInfo *_pr_mon_lm;
+extern PRLogModuleInfo *_pr_linker_lm;
+extern PRLogModuleInfo *_pr_sched_lm;
+extern PRLogModuleInfo *_pr_thread_lm;
+extern PRLogModuleInfo *_pr_gc_lm;
+
+extern PRFileDesc *_pr_stdin;
+extern PRFileDesc *_pr_stdout;
+extern PRFileDesc *_pr_stderr;
+
+#if defined(_PR_INET6)
+extern PRBool _pr_ipv6_enabled; /* defined in prnetdb.c */
+#endif
+
+/* Overriding malloc, free, etc. */
+#if !defined(_PR_NO_PREEMPT) && defined(XP_UNIX) \
+ && !defined(_PR_PTHREADS) && !defined(_PR_GLOBAL_THREADS_ONLY) \
+ && !defined(PURIFY) \
+ && !defined(RHAPSODY) \
+ && !(defined (UNIXWARE) && defined (USE_SVR4_THREADS))
+#define _PR_OVERRIDE_MALLOC
+#endif
+
+/*************************************************************************
+* External machine-dependent code provided by each OS. * *
+*************************************************************************/
+
+/* Initialization related */
+extern void _PR_MD_EARLY_INIT(void);
+#define _PR_MD_EARLY_INIT _MD_EARLY_INIT
+
+extern void _PR_MD_INTERVAL_INIT(void);
+#define _PR_MD_INTERVAL_INIT _MD_INTERVAL_INIT
+
+PR_EXTERN(void) _PR_MD_INIT_SEGS(void);
+#define _PR_MD_INIT_SEGS _MD_INIT_SEGS
+
+PR_EXTERN(void) _PR_MD_FINAL_INIT(void);
+#define _PR_MD_FINAL_INIT _MD_FINAL_INIT
+
+/* Process control */
+
+extern PRProcess * _PR_MD_CREATE_PROCESS(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr);
+#define _PR_MD_CREATE_PROCESS _MD_CREATE_PROCESS
+
+extern PRStatus _PR_MD_DETACH_PROCESS(PRProcess *process);
+#define _PR_MD_DETACH_PROCESS _MD_DETACH_PROCESS
+
+extern PRStatus _PR_MD_WAIT_PROCESS(PRProcess *process, PRInt32 *exitCode);
+#define _PR_MD_WAIT_PROCESS _MD_WAIT_PROCESS
+
+extern PRStatus _PR_MD_KILL_PROCESS(PRProcess *process);
+#define _PR_MD_KILL_PROCESS _MD_KILL_PROCESS
+
+/* Current Time */
+PR_EXTERN(PRTime) _PR_MD_NOW(void);
+#define _PR_MD_NOW _MD_NOW
+
+/* Environment related */
+extern char* _PR_MD_GET_ENV(const char *name);
+#define _PR_MD_GET_ENV _MD_GET_ENV
+
+extern PRIntn _PR_MD_PUT_ENV(const char *name);
+#define _PR_MD_PUT_ENV _MD_PUT_ENV
+
+/* Atomic operations */
+
+extern void _PR_MD_INIT_ATOMIC(void);
+#define _PR_MD_INIT_ATOMIC _MD_INIT_ATOMIC
+
+extern PRInt32 _PR_MD_ATOMIC_INCREMENT(PRInt32 *);
+#define _PR_MD_ATOMIC_INCREMENT _MD_ATOMIC_INCREMENT
+
+extern PRInt32 _PR_MD_ATOMIC_ADD(PRInt32 *, PRInt32);
+#define _PR_MD_ATOMIC_ADD _MD_ATOMIC_ADD
+
+extern PRInt32 _PR_MD_ATOMIC_DECREMENT(PRInt32 *);
+#define _PR_MD_ATOMIC_DECREMENT _MD_ATOMIC_DECREMENT
+
+extern PRInt32 _PR_MD_ATOMIC_SET(PRInt32 *, PRInt32);
+#define _PR_MD_ATOMIC_SET _MD_ATOMIC_SET
+
+/* Segment related */
+PR_EXTERN(PRStatus) _PR_MD_ALLOC_SEGMENT(PRSegment *seg, PRUint32 size, void *vaddr);
+#define _PR_MD_ALLOC_SEGMENT _MD_ALLOC_SEGMENT
+
+PR_EXTERN(void) _PR_MD_FREE_SEGMENT(PRSegment *seg);
+#define _PR_MD_FREE_SEGMENT _MD_FREE_SEGMENT
+
+/* Garbage collection */
+
+/*
+** Save the registers that the GC would find interesting into the thread
+** "t". isCurrent will be non-zero if the thread state that is being
+** saved is the currently executing thread. Return the address of the
+** first register to be scanned as well as the number of registers to
+** scan in "np".
+**
+** If "isCurrent" is non-zero then it is allowed for the thread context
+** area to be used as scratch storage to hold just the registers
+** necessary for scanning.
+*/
+extern PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np);
+
+/* Time intervals */
+
+extern PRIntervalTime _PR_MD_GET_INTERVAL(void);
+#define _PR_MD_GET_INTERVAL _MD_GET_INTERVAL
+
+extern PRIntervalTime _PR_MD_INTERVAL_PER_SEC(void);
+#define _PR_MD_INTERVAL_PER_SEC _MD_INTERVAL_PER_SEC
+
+/* Affinity masks */
+
+extern PRInt32 _PR_MD_SETTHREADAFFINITYMASK(PRThread *thread, PRUint32 mask );
+#define _PR_MD_SETTHREADAFFINITYMASK _MD_SETTHREADAFFINITYMASK
+
+extern PRInt32 _PR_MD_GETTHREADAFFINITYMASK(PRThread *thread, PRUint32 *mask);
+#define _PR_MD_GETTHREADAFFINITYMASK _MD_GETTHREADAFFINITYMASK
+
+/* File locking */
+
+extern PRStatus _PR_MD_LOCKFILE(PRInt32 osfd);
+#define _PR_MD_LOCKFILE _MD_LOCKFILE
+
+extern PRStatus _PR_MD_TLOCKFILE(PRInt32 osfd);
+#define _PR_MD_TLOCKFILE _MD_TLOCKFILE
+
+extern PRStatus _PR_MD_UNLOCKFILE(PRInt32 osfd);
+#define _PR_MD_UNLOCKFILE _MD_UNLOCKFILE
+
+/* Memory-mapped files */
+
+extern PRStatus _PR_MD_CREATE_FILE_MAP(PRFileMap *fmap, PRInt64 size);
+#define _PR_MD_CREATE_FILE_MAP _MD_CREATE_FILE_MAP
+
+extern void * _PR_MD_MEM_MAP(
+ PRFileMap *fmap,
+ PRInt64 offset,
+ PRUint32 len);
+#define _PR_MD_MEM_MAP _MD_MEM_MAP
+
+extern PRStatus _PR_MD_MEM_UNMAP(void *addr, PRUint32 size);
+#define _PR_MD_MEM_UNMAP _MD_MEM_UNMAP
+
+extern PRStatus _PR_MD_CLOSE_FILE_MAP(PRFileMap *fmap);
+#define _PR_MD_CLOSE_FILE_MAP _MD_CLOSE_FILE_MAP
+
+/* Socket call error code */
+
+PR_EXTERN(PRInt32) _PR_MD_GET_SOCKET_ERROR(void);
+#define _PR_MD_GET_SOCKET_ERROR _MD_GET_SOCKET_ERROR
+
+/* Get name of current host */
+extern PRStatus _PR_MD_GETHOSTNAME(char *name, PRUint32 namelen);
+#define _PR_MD_GETHOSTNAME _MD_GETHOSTNAME
+
+PR_END_EXTERN_C
+
+#endif /* primpl_h___ */
diff --git a/pr/include/private/prpriv.h b/pr/include/private/prpriv.h
new file mode 100644
index 00000000..30f85426
--- /dev/null
+++ b/pr/include/private/prpriv.h
@@ -0,0 +1,34 @@
+/* -*- 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 prpriv_h___
+#define prpriv_h___
+
+/*
+ * NSPR 2.0 Private API
+ */
+
+#ifndef XP_MAC
+#include "private/pprio.h"
+#include "private/pprthred.h"
+#else
+#include "pprio.h"
+#include "pprthred.h"
+#endif
+
+#endif /* prpriv_h___ */
diff --git a/pr/include/prlink.h b/pr/include/prlink.h
new file mode 100644
index 00000000..058a0b20
--- /dev/null
+++ b/pr/include/prlink.h
@@ -0,0 +1,140 @@
+/* -*- 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 prlink_h___
+#define prlink_h___
+
+/*
+** API to static and dynamic linking.
+*/
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+typedef struct PRLibrary PRLibrary;
+
+typedef struct PRStaticLinkTable {
+ const char *name;
+ void (*fp)();
+} PRStaticLinkTable;
+
+/*
+** Change the default library path to the given string. The string is
+** copied. This call will fail if it runs out of memory.
+**
+** The string provided as 'path' is copied. The caller can do whatever is
+** convenient with the argument when the function is complete.
+*/
+PR_EXTERN(PRStatus) PR_SetLibraryPath(const char *path);
+
+/*
+** Return a character string which contains the path used to search for
+** dynamically loadable libraries.
+**
+** The returned value is basically a copy of a PR_SetLibraryPath().
+** The storage is allocated by the runtime and becomes the responsibilty
+** of the caller.
+*/
+PR_EXTERN(char*) PR_GetLibraryPath(void);
+
+/*
+** Given a directory name "dir" and a library name "lib" construct a full
+** path name that will refer to the actual dynamically loaded
+** library. This does not test for existance of said file, it just
+** constructs the full filename. The name constructed is system dependent
+** and prepared for PR_LoadLibrary. The result must be free'd when the
+** caller is done with it.
+**
+** The storage for the result is allocated by the runtime and becomes the
+** responsibility of the caller.
+*/
+PR_EXTERN(char*) PR_GetLibraryName(const char *dir, const char *lib);
+
+/*
+**
+** Free the memory allocated, for the caller, by PR_GetLibraryName
+*/
+PR_EXTERN(void) PR_FreeLibraryName(char *mem);
+
+/*
+** Given a library "name" try to load the library. The argument "name"
+** is a machine-dependent name for the library, such as the full pathname
+** returned by PR_GetLibraryName. If the library is already loaded,
+** this function will avoid loading the library twice.
+**
+** If the library is loaded successfully, then a pointer to the PRLibrary
+** structure representing the library is returned. Otherwise, NULL is
+** returned.
+**
+** This increments the reference count of the library.
+*/
+PR_EXTERN(PRLibrary*) PR_LoadLibrary(const char *name);
+
+/*
+** Unload a previously loaded library. If the library was a static
+** library then the static link table will no longer be referenced. The
+** associated PRLibrary object is freed.
+**
+** PR_FAILURE is returned if the library cannot be unloaded.
+**
+** This function decrements the reference count of the library.
+*/
+PR_EXTERN(PRStatus) PR_UnloadLibrary(PRLibrary *lib);
+
+/*
+** Given the name of a procedure, return the address of the function that
+** implements the procedure, or NULL if no such function can be
+** found. This does not find symbols in the main program (the ".exe");
+** use PR_LoadStaticLibrary to register symbols in the main program. If
+** "lib" is NULL then all currently loaded libraries and the main program
+** are searched.
+**
+** This function does not modify the reference count of the library.
+*/
+PR_EXTERN(void*) PR_FindSymbol(PRLibrary *lib, const char *name);
+
+/*
+** Finds a symbol in one of the currently loaded libraries. Given the
+** name of a procedure, return the address of the function that
+** implements the procedure, and return the library that contains that
+** symbol, or NULL if no such function can be found. This does not find
+** symbols in the main program (the ".exe"); use PR_AddStaticLibrary to
+** register symbols in the main program.
+**
+** This increments the reference count of the library.
+*/
+PR_EXTERN(void*) PR_FindSymbolAndLibrary(const char *name,
+ PRLibrary* *lib);
+
+/*
+** Register a static link table with the runtime under the name
+** "name". The symbols present in the static link table will be made
+** available to PR_FindSymbol. If "name" is null then the symbols will be
+** made available to the library which represents the executable. The
+** tables are not copied.
+**
+** Returns the library object if successful, null otherwise.
+**
+** This increments the reference count of the library.
+*/
+PR_EXTERN(PRLibrary*) PR_LoadStaticLibrary(
+ const char *name, const PRStaticLinkTable *table);
+
+PR_END_EXTERN_C
+
+#endif /* prlink_h___ */
diff --git a/pr/include/prlock.h b/pr/include/prlock.h
new file mode 100644
index 00000000..2bcac261
--- /dev/null
+++ b/pr/include/prlock.h
@@ -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.
+ */
+
+/*
+** File: prlock.h
+** Description: API to basic locking functions of NSPR.
+**
+**
+** NSPR provides basic locking mechanisms for thread synchronization. Locks
+** are lightweight resource contention controls that prevent multiple threads
+** from accessing something (code/data) simultaneously.
+**/
+
+#ifndef prlock_h___
+#define prlock_h___
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+/**********************************************************************/
+/************************* TYPES AND CONSTANTS ************************/
+/**********************************************************************/
+
+/*
+ * PRLock --
+ *
+ * NSPR represents the lock as an opaque entity to the client of the
+ * API. All routines operate on a pointer to this opaque entity.
+ */
+
+typedef struct PRLock PRLock;
+
+/**********************************************************************/
+/****************************** FUNCTIONS *****************************/
+/**********************************************************************/
+
+/***********************************************************************
+** FUNCTION: PR_NewLock
+** DESCRIPTION:
+** Returns a pointer to a newly created opaque lock object.
+** INPUTS: void
+** OUTPUTS: void
+** RETURN: PRLock*
+** If the lock can not be created because of resource constraints, NULL
+** is returned.
+**
+***********************************************************************/
+PR_EXTERN(PRLock*) PR_NewLock(void);
+
+/***********************************************************************
+** FUNCTION: PR_DestroyLock
+** DESCRIPTION:
+** Destroys a given opaque lock object.
+** INPUTS: PRLock *lock
+** Lock to be freed.
+** OUTPUTS: void
+** RETURN: None
+***********************************************************************/
+PR_EXTERN(void) PR_DestroyLock(PRLock *lock);
+
+/***********************************************************************
+** FUNCTION: PR_Lock
+** DESCRIPTION:
+** Lock a lock.
+** INPUTS: PRLock *lock
+** Lock to locked.
+** OUTPUTS: void
+** RETURN: None
+***********************************************************************/
+PR_EXTERN(void) PR_Lock(PRLock *lock);
+
+/***********************************************************************
+** FUNCTION: PR_Unlock
+** DESCRIPTION:
+** Unlock a lock. Unlocking an unlocked lock has undefined results.
+** INPUTS: PRLock *lock
+** Lock to unlocked.
+** OUTPUTS: void
+** RETURN: PR_STATUS
+** Returns PR_FAILURE if the caller does not own the lock.
+***********************************************************************/
+PR_EXTERN(PRStatus) PR_Unlock(PRLock *lock);
+
+PR_END_EXTERN_C
+
+#endif /* prlock_h___ */
diff --git a/pr/include/prlog.h b/pr/include/prlog.h
new file mode 100644
index 00000000..8369c64c
--- /dev/null
+++ b/pr/include/prlog.h
@@ -0,0 +1,237 @@
+/* -*- 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 prlog_h___
+#define prlog_h___
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+/*
+** prlog.h -- Declare interfaces to NSPR's Logging service
+**
+** NSPR provides a logging service that is used by NSPR itself and is
+** available to client programs.
+**
+** To use the service from a client program, you should create a
+** PRLogModuleInfo structure by calling PR_NewLogModule(). After
+** creating the LogModule, you can write to the log using the PR_LOG()
+** macro.
+**
+** Initialization of the log service is handled by NSPR initialization.
+**
+** At execution time, you must enable the log service. To enable the
+** log service, set the environment variable: NSPR_LOG_MODULES
+** variable.
+**
+** NSPR_LOG_MODULES variable has the form:
+**
+** <moduleName>:<value>[, <moduleName>:<value>]*
+**
+** Where:
+** <moduleName> is the name passed to PR_NewLogModule().
+** <value> is a numeric constant, e.g. 5. This value is the maximum
+** value of a log event, enumerated by PRLogModuleLevel, that you want
+** written to the log.
+**
+** For example: to record all events of greater value than or equal to
+** PR_LOG_ERROR for a LogModule names "gizmo", say:
+**
+** set NSPR_LOG_MODULES=gizmo:2
+**
+** Note that you must specify the numeric value of PR_LOG_ERROR.
+**
+** Special LogModule names are provided for controlling NSPR's log
+** service at execution time. These controls should be set in the
+** NSPR_LOG_MODULES environment variable at execution time to affect
+** NSPR's log service for your application.
+**
+** The special LogModule "all" enables all LogModules. To enable all
+** LogModule calls to PR_LOG(), say:
+**
+** set NSPR_LOG_MODULES=all:5
+**
+** The special LogModule name "sync" tells the NSPR log service to do
+** unbuffered logging.
+**
+** The special LogModule name "buffsize:<size>" tells NSPR to set the
+** log buffer to <size>.
+**
+** The environment variable NSPR_LOG_FILE specifies the log file to use
+** unless the default of "stderr" is acceptable. For MS Windows
+** systems, NSPR_LOG_FILE can be set to a special value: "WinDebug"
+** (case sensitive). This value causes PR_LOG() output to be written
+** using the Windows API OutputDebugString(). OutputDebugString()
+** writes to the debugger window; some people find this helpful.
+**
+**
+** To put log messages in your programs, use the PR_LOG macro:
+**
+** PR_LOG(<module>, <level>, (<printfString>, <args>*));
+**
+** Where <module> is the address of a PRLogModuleInfo structure, and
+** <level> is one of the levels defined by the enumeration:
+** PRLogModuleLevel. <args> is a printf() style of argument list. That
+** is: (fmtstring, ...).
+**
+** Example:
+**
+** main() {
+** PRIntn one = 1;
+** PRLogModuleInfo * myLm = PR_NewLogModule("gizmo");
+** PR_LOG( myLm, PR_LOG_ALWAYS, ("Log this! %d\n", one));
+** return;
+** }
+**
+** Note the use of printf() style arguments as the third agrument(s) to
+** PR_LOG().
+**
+** After compiling and linking you application, set the environment:
+**
+** SET NSPR_LOGMODULES=gizmo:5
+** SET NSPR_LOG_FILE=logfile.txt
+**
+** When you execute your application, the string "Log this! 1" will be
+** written to the file "logfile.txt".
+**
+** Note to NSPR engineers: a number of PRLogModuleInfo structures are
+** defined and initialized in prinit.c. See this module for ideas on
+** what to log where.
+**
+*/
+
+typedef enum PRLogModuleLevel {
+ PR_LOG_NONE = 0, /* nothing */
+ PR_LOG_ALWAYS = 1, /* always printed */
+ PR_LOG_ERROR = 2, /* error messages */
+ PR_LOG_WARNING = 3, /* warning messages */
+ PR_LOG_DEBUG = 4, /* debug messages */
+
+ PR_LOG_NOTICE = PR_LOG_DEBUG, /* notice messages */
+ PR_LOG_WARN = PR_LOG_WARNING, /* warning messages */
+ PR_LOG_MIN = PR_LOG_DEBUG, /* minimal debugging messages */
+ PR_LOG_MAX = PR_LOG_DEBUG /* maximal debugging messages */
+} PRLogModuleLevel;
+
+/*
+** One of these structures is created for each module that uses logging.
+** "name" is the name of the module
+** "level" is the debugging level selected for that module
+*/
+typedef struct PRLogModuleInfo {
+ const char *name;
+ PRLogModuleLevel level;
+ struct PRLogModuleInfo *next;
+} PRLogModuleInfo;
+
+/*
+** Create a new log module.
+*/
+PR_EXTERN(PRLogModuleInfo*) PR_NewLogModule(const char *name);
+
+/*
+** Set the file to use for logging. Returns PR_FALSE if the file cannot
+** be created
+*/
+PR_EXTERN(PRBool) PR_SetLogFile(const char *name);
+
+/*
+** Set the size of the logging buffer. If "buffer_size" is zero then the
+** logging becomes "synchronous" (or unbuffered).
+*/
+PR_EXTERN(void) PR_SetLogBuffering(PRIntn buffer_size);
+
+/*
+** Print a string to the log. "fmt" is a PR_snprintf format type. All
+** messages printed to the log are preceeded by the name of the thread
+** and a time stamp. Also, the routine provides a missing newline if one
+** is not provided.
+*/
+PR_EXTERN(void) PR_LogPrint(const char *fmt, ...);
+
+/*
+** Flush the log to its file.
+*/
+PR_EXTERN(void) PR_LogFlush(void);
+
+/*
+** Windoze 16 can't support a large static string space for all of the
+** various debugging strings so logging is not enabled for it.
+*/
+#if (defined(DEBUG) || defined(FORCE_PR_LOG)) && !defined(WIN16)
+#define PR_LOGGING 1
+
+#define PR_LOG_TEST(_module,_level) \
+ ((_module)->level >= (_level))
+
+/*
+** Log something.
+** "module" is the address of a PRLogModuleInfo structure
+** "level" is the desired logging level
+** "args" is a variable length list of arguments to print, in the following
+** format: ("printf style format string", ...)
+*/
+#define PR_LOG(_module,_level,_args) \
+ PR_BEGIN_MACRO \
+ if (PR_LOG_TEST(_module,_level)) { \
+ PR_LogPrint _args; \
+ } \
+ PR_END_MACRO
+
+#else /* (defined(DEBUG) || defined(FORCE_PR_LOG)) && !defined(WIN16) */
+
+#undef PR_LOGGING
+#define PR_LOG_TEST(module,level) 0
+#define PR_LOG(module,level,args)
+
+#endif /* (defined(DEBUG) || defined(FORCE_PR_LOG)) && !defined(WIN16) */
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#ifdef PR_LOGGING
+#define PR_LOG_BEGIN PR_LOG
+#define PR_LOG_END PR_LOG
+#define PR_LOG_DEFINE PR_NewLogModule
+#else
+#define PR_LOG_BEGIN(module,level,args)
+#define PR_LOG_END(module,level,args)
+#define PR_LOG_DEFINE(_name) NULL
+#endif /* PR_LOGGING */
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#if defined(DEBUG)
+
+PR_EXTERN(void) PR_Assert(const char *s, const char *file, PRIntn ln);
+#define PR_ASSERT(_expr) \
+ ((_expr)?((void)0):PR_Assert(# _expr,__FILE__,__LINE__))
+
+#define PR_NOT_REACHED(_reasonStr) \
+ PR_Assert(_reasonStr,__FILE__,__LINE__)
+
+#else
+
+#define PR_ASSERT(expr) ((void) 0)
+#define PR_NOT_REACHED(reasonStr)
+
+#endif /* defined(DEBUG) */
+
+PR_END_EXTERN_C
+
+#endif /* prlog_h___ */
diff --git a/pr/include/prlong.h b/pr/include/prlong.h
new file mode 100644
index 00000000..8f0da6dd
--- /dev/null
+++ b/pr/include/prlong.h
@@ -0,0 +1,409 @@
+/* -*- 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: prlong.h
+** Description: Portable access to 64 bit numerics
+**
+** Long-long (64-bit signed integer type) support. Some C compilers
+** don't support 64 bit integers yet, so we use these macros to
+** support both machines that do and don't.
+**/
+#ifndef prlong_h___
+#define prlong_h___
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+/***********************************************************************
+** DEFINES: LL_MaxInt
+** LL_MinInt
+** LL_Zero
+** DESCRIPTION:
+** Various interesting constants and static variable
+** initializer
+***********************************************************************/
+#if defined(HAVE_WATCOM_BUG_2)
+PRInt64 __pascal __loadds __export
+ LL_MaxInt(void);
+PRInt64 __pascal __loadds __export
+ LL_MinInt(void);
+PRInt64 __pascal __loadds __export
+ LL_Zero(void);
+#else
+PR_EXTERN(PRInt64) LL_MaxInt(void);
+PR_EXTERN(PRInt64) LL_MinInt(void);
+PR_EXTERN(PRInt64) LL_Zero(void);
+#endif
+
+#define LL_MAXINT LL_MaxInt()
+#define LL_MININT LL_MinInt()
+#define LL_ZERO LL_Zero()
+
+#if defined(HAVE_LONG_LONG)
+
+#if PR_BYTES_PER_LONG == 8
+#define LL_INIT(hi, lo) ((hi ## L << 32) + lo ## L)
+#elif defined(WIN32) || defined(WIN16)
+#define LL_INIT(hi, lo) ((hi ## i64 << 32) + lo ## i64)
+#else
+#define LL_INIT(hi, lo) ((hi ## LL << 32) + lo ## LL)
+#endif
+
+/***********************************************************************
+** MACROS: LL_*
+** DESCRIPTION:
+** The following macros define portable access to the 64 bit
+** math facilities.
+**
+***********************************************************************/
+
+/***********************************************************************
+** MACROS: LL_<relational operators>
+**
+** LL_IS_ZERO Test for zero
+** LL_EQ Test for equality
+** LL_NE Test for inequality
+** LL_GE_ZERO Test for zero or positive
+** LL_CMP Compare two values
+***********************************************************************/
+#define LL_IS_ZERO(a) ((a) == 0)
+#define LL_EQ(a, b) ((a) == (b))
+#define LL_NE(a, b) ((a) != (b))
+#define LL_GE_ZERO(a) ((a) >= 0)
+#define LL_CMP(a, op, b) ((PRInt64)(a) op (PRInt64)(b))
+#define LL_UCMP(a, op, b) ((PRUint64)(a) op (PRUint64)(b))
+
+/***********************************************************************
+** MACROS: LL_<logical operators>
+**
+** LL_AND Logical and
+** LL_OR Logical or
+** LL_XOR Logical exclusion
+** LL_OR2 A disgusting deviation
+** LL_NOT Negation (one's compliment)
+***********************************************************************/
+#define LL_AND(r, a, b) ((r) = (a) & (b))
+#define LL_OR(r, a, b) ((r) = (a) | (b))
+#define LL_XOR(r, a, b) ((r) = (a) ^ (b))
+#define LL_OR2(r, a) ((r) = (r) | (a))
+#define LL_NOT(r, a) ((r) = ~(a))
+
+/***********************************************************************
+** MACROS: LL_<mathematical operators>
+**
+** LL_NEG Negation (two's compliment)
+** LL_ADD Summation (two's compliment)
+** LL_SUB Difference (two's compliment)
+***********************************************************************/
+#define LL_NEG(r, a) ((r) = -(a))
+#define LL_ADD(r, a, b) ((r) = (a) + (b))
+#define LL_SUB(r, a, b) ((r) = (a) - (b))
+
+/***********************************************************************
+** MACROS: LL_<mathematical operators>
+**
+** LL_MUL Product (two's compliment)
+** LL_DIV Quotient (two's compliment)
+** LL_MOD Modulus (two's compliment)
+***********************************************************************/
+#define LL_MUL(r, a, b) ((r) = (a) * (b))
+#define LL_DIV(r, a, b) ((r) = (a) / (b))
+#define LL_MOD(r, a, b) ((r) = (a) % (b))
+
+/***********************************************************************
+** MACROS: LL_<shifting operators>
+**
+** LL_SHL Shift left [0..64] bits
+** LL_SHR Shift right [0..64] bits with sign extension
+** LL_USHR Unsigned shift right [0..64] bits
+** LL_ISHL Signed shift left [0..64] bits
+***********************************************************************/
+#define LL_SHL(r, a, b) ((r) = (PRInt64)(a) << (b))
+#define LL_SHR(r, a, b) ((r) = (PRInt64)(a) >> (b))
+#define LL_USHR(r, a, b) ((r) = (PRUint64)(a) >> (b))
+#define LL_ISHL(r, a, b) ((r) = (PRInt64)(a) << (b))
+
+/***********************************************************************
+** MACROS: LL_<conversion operators>
+**
+** LL_L2I Convert to signed 32 bit
+** LL_L2UI Convert to unsigned 32 bit
+** LL_L2F Convert to floating point
+** LL_L2D Convert to floating point
+** LL_I2L Convert signed to 64 bit
+** LL_UI2L Convert unsigned to 64 bit
+** LL_F2L Convert float to 64 bit
+** LL_D2L Convert float to 64 bit
+***********************************************************************/
+#define LL_L2I(i, l) ((i) = (PRInt32)(l))
+#define LL_L2UI(ui, l) ((ui) = (PRUint32)(l))
+#define LL_L2F(f, l) ((f) = (PRFloat64)(l))
+#define LL_L2D(d, l) ((d) = (PRFloat64)(l))
+
+#define LL_I2L(l, i) ((l) = (PRInt64)(i))
+#define LL_UI2L(l, ui) ((l) = (PRInt64)(ui))
+#define LL_F2L(l, f) ((l) = (PRInt64)(f))
+#define LL_D2L(l, d) ((l) = (PRInt64)(d))
+
+/***********************************************************************
+** MACROS: LL_UDIVMOD
+** DESCRIPTION:
+** Produce both a quotient and a remainder given an unsigned
+** INPUTS: PRUint64 a: The dividend of the operation
+** PRUint64 b: The quotient of the operation
+** OUTPUTS: PRUint64 *qp: pointer to quotient
+** PRUint64 *rp: pointer to remainder
+***********************************************************************/
+#define LL_UDIVMOD(qp, rp, a, b) \
+ (*(qp) = ((PRUint64)(a) / (b)), \
+ *(rp) = ((PRUint64)(a) % (b)))
+
+#else /* !HAVE_LONG_LONG */
+
+#ifdef IS_LITTLE_ENDIAN
+#define LL_INIT(hi, lo) {PR_INT32(lo), PR_INT32(hi)}
+#else
+#define LL_INIT(hi, lo) {PR_INT32(hi), PR_INT32(lo)}
+#endif
+
+#define LL_IS_ZERO(a) (((a).hi == 0) && ((a).lo == 0))
+#define LL_EQ(a, b) (((a).hi == (b).hi) && ((a).lo == (b).lo))
+#define LL_NE(a, b) (((a).hi != (b).hi) || ((a).lo != (b).lo))
+#define LL_GE_ZERO(a) (((a).hi >> 31) == 0)
+
+#define LL_CMP(a, op, b) (((PRInt32)(a).hi op (PRInt32)(b).hi) || \
+ (((a).hi == (b).hi) && ((a).lo op (b).lo)))
+#define LL_UCMP(a, op, b) (((a).hi op (b).hi) || \
+ (((a).hi == (b).hi) && ((a).lo op (b).lo)))
+
+#define LL_AND(r, a, b) ((r).lo = (a).lo & (b).lo, \
+ (r).hi = (a).hi & (b).hi)
+#define LL_OR(r, a, b) ((r).lo = (a).lo | (b).lo, \
+ (r).hi = (a).hi | (b).hi)
+#define LL_XOR(r, a, b) ((r).lo = (a).lo ^ (b).lo, \
+ (r).hi = (a).hi ^ (b).hi)
+#define LL_OR2(r, a) ((r).lo = (r).lo | (a).lo, \
+ (r).hi = (r).hi | (a).hi)
+#define LL_NOT(r, a) ((r).lo = ~(a).lo, \
+ (r).hi = ~(a).hi)
+
+#define LL_NEG(r, a) ((r).lo = -(PRInt32)(a).lo, \
+ (r).hi = -(PRInt32)(a).hi - ((r).lo != 0))
+#define LL_ADD(r, a, b) { \
+ PRInt64 _a, _b; \
+ _a = a; _b = b; \
+ (r).lo = _a.lo + _b.lo; \
+ (r).hi = _a.hi + _b.hi + ((r).lo < _b.lo); \
+}
+
+#define LL_SUB(r, a, b) { \
+ PRInt64 _a, _b; \
+ _a = a; _b = b; \
+ (r).lo = _a.lo - _b.lo; \
+ (r).hi = _a.hi - _b.hi - (_a.lo < _b.lo); \
+}
+
+#define LL_MUL(r, a, b) { \
+ PRInt64 _a, _b; \
+ _a = a; _b = b; \
+ LL_MUL32(r, _a.lo, _b.lo); \
+ (r).hi += _a.hi * _b.lo + _a.lo * _b.hi; \
+}
+
+#define _lo16(a) ((a) & PR_BITMASK(16))
+#define _hi16(a) ((a) >> 16)
+
+#define LL_MUL32(r, a, b) { \
+ PRUint32 _a1, _a0, _b1, _b0, _y0, _y1, _y2, _y3; \
+ _a1 = _hi16(a), _a0 = _lo16(a); \
+ _b1 = _hi16(b), _b0 = _lo16(b); \
+ _y0 = _a0 * _b0; \
+ _y1 = _a0 * _b1; \
+ _y2 = _a1 * _b0; \
+ _y3 = _a1 * _b1; \
+ _y1 += _hi16(_y0); /* can't carry */ \
+ _y1 += _y2; /* might carry */ \
+ if (_y1 < _y2) \
+ _y3 += (PRUint32)(PR_BIT(16)); /* propagate */ \
+ (r).lo = (_lo16(_y1) << 16) + _lo16(_y0); \
+ (r).hi = _y3 + _hi16(_y1); \
+}
+
+#define LL_UDIVMOD(qp, rp, a, b) ll_udivmod(qp, rp, a, b)
+
+PR_EXTERN(void) ll_udivmod(PRUint64 *qp, PRUint64 *rp, PRUint64 a, PRUint64 b);
+
+#define LL_DIV(r, a, b) { \
+ PRInt64 _a, _b; \
+ PRUint32 _negative = (PRInt32)(a).hi < 0; \
+ if (_negative) { \
+ LL_NEG(_a, a); \
+ } else { \
+ _a = a; \
+ } \
+ if ((PRInt32)(b).hi < 0) { \
+ _negative ^= 1; \
+ LL_NEG(_b, b); \
+ } else { \
+ _b = b; \
+ } \
+ LL_UDIVMOD(&(r), 0, _a, _b); \
+ if (_negative) \
+ LL_NEG(r, r); \
+}
+
+#define LL_MOD(r, a, b) { \
+ PRInt64 _a, _b; \
+ PRUint32 _negative = (PRInt32)(a).hi < 0; \
+ if (_negative) { \
+ LL_NEG(_a, a); \
+ } else { \
+ _a = a; \
+ } \
+ if ((PRInt32)(b).hi < 0) { \
+ LL_NEG(_b, b); \
+ } else { \
+ _b = b; \
+ } \
+ LL_UDIVMOD(0, &(r), _a, _b); \
+ if (_negative) \
+ LL_NEG(r, r); \
+}
+
+#define LL_SHL(r, a, b) { \
+ if (b) { \
+ PRInt64 _a; \
+ _a = a; \
+ if ((b) < 32) { \
+ (r).lo = _a.lo << ((b) & 31); \
+ (r).hi = (_a.hi << ((b) & 31)) | (_a.lo >> (32 - (b))); \
+ } else { \
+ (r).lo = 0; \
+ (r).hi = _a.lo << ((b) & 31); \
+ } \
+ } else { \
+ (r) = (a); \
+ } \
+}
+
+/* a is an PRInt32, b is PRInt32, r is PRInt64 */
+#define LL_ISHL(r, a, b) { \
+ if (b) { \
+ PRInt64 _a; \
+ _a.lo = (a); \
+ _a.hi = 0; \
+ if ((b) < 32) { \
+ (r).lo = (a) << ((b) & 31); \
+ (r).hi = ((a) >> (32 - (b))); \
+ } else { \
+ (r).lo = 0; \
+ (r).hi = (a) << ((b) & 31); \
+ } \
+ } else { \
+ (r).lo = (a); \
+ (r).hi = 0; \
+ } \
+}
+
+#define LL_SHR(r, a, b) { \
+ if (b) { \
+ PRInt64 _a; \
+ _a = a; \
+ if ((b) < 32) { \
+ (r).lo = (_a.hi << (32 - (b))) | (_a.lo >> ((b) & 31)); \
+ (r).hi = (PRInt32)_a.hi >> ((b) & 31); \
+ } else { \
+ (r).lo = (PRInt32)_a.hi >> ((b) & 31); \
+ (r).hi = (PRInt32)_a.hi >> 31; \
+ } \
+ } else { \
+ (r) = (a); \
+ } \
+}
+
+#define LL_USHR(r, a, b) { \
+ if (b) { \
+ PRInt64 _a; \
+ _a = a; \
+ if ((b) < 32) { \
+ (r).lo = (_a.hi << (32 - (b))) | (_a.lo >> ((b) & 31)); \
+ (r).hi = _a.hi >> ((b) & 31); \
+ } else { \
+ (r).lo = _a.hi >> ((b) & 31); \
+ (r).hi = 0; \
+ } \
+ } else { \
+ (r) = (a); \
+ } \
+}
+
+#define LL_L2I(i, l) ((i) = (l).lo)
+#define LL_L2UI(ui, l) ((ui) = (l).lo)
+#define LL_L2F(f, l) { double _d; LL_L2D(_d, l); (f) = (PRFloat64)_d; }
+
+#define LL_L2D(d, l) { \
+ int _negative; \
+ PRInt64 _absval; \
+ \
+ _negative = (l).hi >> 31; \
+ if (_negative) { \
+ LL_NEG(_absval, l); \
+ } else { \
+ _absval = l; \
+ } \
+ (d) = (double)_absval.hi * 4.294967296e9 + _absval.lo; \
+ if (_negative) \
+ (d) = -(d); \
+}
+
+#define LL_I2L(l, i) { PRInt32 _i = (i) >> 31; (l).lo = (i); (l).hi = _i; }
+#define LL_UI2L(l, ui) ((l).lo = (ui), (l).hi = 0)
+#define LL_F2L(l, f) { double _d = (double)f; LL_D2L(l, _d); }
+
+#define LL_D2L(l, d) { \
+ int _negative; \
+ double _absval, _d_hi; \
+ PRInt64 _lo_d; \
+ \
+ _negative = ((d) < 0); \
+ _absval = _negative ? -(d) : (d); \
+ \
+ (l).hi = _absval / 4.294967296e9; \
+ (l).lo = 0; \
+ LL_L2D(_d_hi, l); \
+ _absval -= _d_hi; \
+ _lo_d.hi = 0; \
+ if (_absval < 0) { \
+ _lo_d.lo = -_absval; \
+ LL_SUB(l, l, _lo_d); \
+ } else { \
+ _lo_d.lo = _absval; \
+ LL_ADD(l, l, _lo_d); \
+ } \
+ \
+ if (_negative) \
+ LL_NEG(l, l); \
+}
+
+#endif /* !HAVE_LONG_LONG */
+
+PR_END_EXTERN_C
+
+#endif /* prlong_h___ */
diff --git a/pr/include/prmem.h b/pr/include/prmem.h
new file mode 100644
index 00000000..2a6c766a
--- /dev/null
+++ b/pr/include/prmem.h
@@ -0,0 +1,140 @@
+/* -*- 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: prmem.h
+** Description: API to NSPR 2.0 memory management functions
+**
+*/
+#ifndef prmem_h___
+#define prmem_h___
+
+#include "prtypes.h"
+#include <stddef.h>
+#include <stdlib.h>
+
+PR_BEGIN_EXTERN_C
+
+/*
+** Thread safe memory allocation.
+**
+** NOTE: pr wraps up malloc, free, calloc, realloc so they are already
+** thread safe (and are not declared here - look in stdlib.h).
+*/
+
+/*
+** PR_Malloc, PR_Calloc, PR_Realloc, and PR_Free have the same signatures
+** as their libc equivalent malloc, calloc, realloc, and free, and have
+** the same semantics. (Note that the argument type size_t is replaced
+** by PRUint32.) Memory allocated by PR_Malloc, PR_Calloc, or PR_Realloc
+** must be freed by PR_Free.
+*/
+
+PR_EXTERN(void *) PR_Malloc(PRUint32 size);
+
+PR_EXTERN(void *) PR_Calloc(PRUint32 nelem, PRUint32 elsize);
+
+PR_EXTERN(void *) PR_Realloc(void *ptr, PRUint32 size);
+
+PR_EXTERN(void) PR_Free(void *ptr);
+
+/*
+** The following are some convenience macros defined in terms of
+** PR_Malloc, PR_Calloc, PR_Realloc, and PR_Free.
+*/
+
+/***********************************************************************
+** FUNCTION: PR_MALLOC()
+** DESCRIPTION:
+** PR_NEW() allocates an untyped item of size _size from the heap.
+** INPUTS: _size: size in bytes of item to be allocated
+** OUTPUTS: untyped pointer to the node allocated
+** RETURN: pointer to node or error returned from malloc().
+***********************************************************************/
+#define PR_MALLOC(_bytes) (PR_Malloc((_bytes)))
+
+/***********************************************************************
+** FUNCTION: PR_NEW()
+** DESCRIPTION:
+** PR_NEW() allocates an item of type _struct from the heap.
+** INPUTS: _struct: a data type
+** OUTPUTS: pointer to _struct
+** RETURN: pointer to _struct or error returns from malloc().
+***********************************************************************/
+#define PR_NEW(_struct) ((_struct *) PR_MALLOC(sizeof(_struct)))
+
+/***********************************************************************
+** FUNCTION: PR_REALLOC()
+** DESCRIPTION:
+** PR_REALLOC() re-allocates _ptr bytes from the heap as a _size
+** untyped item.
+** INPUTS: _ptr: pointer to node to reallocate
+** _size: size of node to allocate
+** OUTPUTS: pointer to node allocated
+** RETURN: pointer to node allocated
+***********************************************************************/
+#define PR_REALLOC(_ptr, _size) (PR_Realloc((_ptr), (_size)))
+
+/***********************************************************************
+** FUNCTION: PR_CALLOC()
+** DESCRIPTION:
+** PR_CALLOC() allocates a _size bytes untyped item from the heap
+** and sets the allocated memory to all 0x00.
+** INPUTS: _size: size of node to allocate
+** OUTPUTS: pointer to node allocated
+** RETURN: pointer to node allocated
+***********************************************************************/
+#define PR_CALLOC(_size) (PR_Calloc(1, (_size)))
+
+/***********************************************************************
+** FUNCTION: PR_NEWZAP()
+** DESCRIPTION:
+** PR_NEWZAP() allocates an item of type _struct from the heap
+** and sets the allocated memory to all 0x00.
+** INPUTS: _struct: a data type
+** OUTPUTS: pointer to _struct
+** RETURN: pointer to _struct
+***********************************************************************/
+#define PR_NEWZAP(_struct) ((_struct*)PR_Calloc(1, sizeof(_struct)))
+
+/***********************************************************************
+** FUNCTION: PR_DELETE()
+** DESCRIPTION:
+** PR_DELETE() unallocates an object previosly allocated via PR_NEW()
+** or PR_NEWZAP() to the heap.
+** INPUTS: pointer to previously allocated object
+** OUTPUTS: the referenced object is returned to the heap
+** RETURN: void
+***********************************************************************/
+#define PR_DELETE(_ptr) { PR_Free(_ptr); (_ptr) = NULL; }
+
+/***********************************************************************
+** FUNCTION: PR_FREEIF()
+** DESCRIPTION:
+** PR_FREEIF() conditionally unallocates an object previously allocated
+** vial PR_NEW() or PR_NEWZAP(). If the pointer to the object is
+** equal to zero (0), the object is not released.
+** INPUTS: pointer to previously allocated object
+** OUTPUTS: the referenced object is conditionally returned to the heap
+** RETURN: void
+***********************************************************************/
+#define PR_FREEIF(_ptr) if (_ptr) PR_DELETE(_ptr)
+
+PR_END_EXTERN_C
+
+#endif /* prmem_h___ */
diff --git a/pr/include/prmon.h b/pr/include/prmon.h
new file mode 100644
index 00000000..17fb699a
--- /dev/null
+++ b/pr/include/prmon.h
@@ -0,0 +1,94 @@
+/* -*- 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 prmon_h___
+#define prmon_h___
+
+#include "prtypes.h"
+#include "prinrval.h"
+
+PR_BEGIN_EXTERN_C
+
+typedef struct PRMonitor PRMonitor;
+
+/*
+** Create a new monitor. Monitors are re-entrant locks with a single built-in
+** condition variable.
+**
+** This may fail if memory is tight or if some operating system resource
+** is low.
+*/
+PR_EXTERN(PRMonitor*) PR_NewMonitor(void);
+
+/*
+** Destroy a monitor. The caller is responsible for guaranteeing that the
+** monitor is no longer in use. There must be no thread waiting on the monitor's
+** condition variable and that the lock is not held.
+**
+*/
+PR_EXTERN(void) PR_DestroyMonitor(PRMonitor *mon);
+
+/*
+** Enter the lock associated with the monitor. If the calling thread currently
+** is in the monitor, the call to enter will silently succeed. In either case,
+** it will increment the entry count by one.
+*/
+PR_EXTERN(void) PR_EnterMonitor(PRMonitor *mon);
+
+/*
+** Decrement the entry count associated with the monitor. If the decremented
+** entry count is zero, the monitor is exited. Returns PR_FAILURE if the
+** calling thread has not entered the monitor.
+*/
+PR_EXTERN(PRStatus) PR_ExitMonitor(PRMonitor *mon);
+
+/*
+** Wait for a notify on the monitor's condition variable. Sleep for "ticks"
+** amount of time (if "ticks" is PR_INTERVAL_NO_TIMEOUT then the sleep is
+** indefinite).
+**
+** While the thread is waiting it exits the monitor (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" timeout elapses.
+**
+** Returns PR_FAILURE if the caller has not entered the monitor.
+*/
+PR_EXTERN(PRStatus) PR_Wait(PRMonitor *mon, PRIntervalTime ticks);
+
+/*
+** Notify a thread waiting on the monitor's condition variable. If a thread
+** is waiting on the condition variable (using PR_Wait) then it is awakened
+** and attempts to reenter the monitor.
+*/
+PR_EXTERN(PRStatus) PR_Notify(PRMonitor *mon);
+
+/*
+** Notify all of the threads waiting on the monitor's condition variable.
+** All of threads waiting on the condition are scheduled to reenter the
+** monitor.
+*/
+PR_EXTERN(PRStatus) PR_NotifyAll(PRMonitor *mon);
+
+PR_END_EXTERN_C
+
+#endif /* prmon_h___ */
diff --git a/pr/include/prmwait.h b/pr/include/prmwait.h
new file mode 100644
index 00000000..91f44393
--- /dev/null
+++ b/pr/include/prmwait.h
@@ -0,0 +1,393 @@
+/* -*- 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(_PRMWAIT_H)
+#else
+#define _PRMWAIT_H
+
+#include "prio.h"
+#include "prtypes.h"
+#include "prclist.h"
+
+PR_BEGIN_EXTERN_C
+
+/********************************************************************************/
+/********************************************************************************/
+/********************************************************************************/
+/****************************** WARNING ****************************/
+/********************************************************************************/
+/**************************** This is work in progress. *************************/
+/************************** Do not make any assumptions *************************/
+/************************** about the stability of this *************************/
+/************************** API or the underlying imple- ************************/
+/************************** mentation. ************************/
+/********************************************************************************/
+/********************************************************************************/
+
+/*
+** STRUCTURE: PRWaitGroup
+** DESCRIPTION:
+** The client may define several wait groups in order to semantically
+** tie a collection of file descriptors for a single purpose. This allows
+** easier dispatching of threads that returned with active file descriptors
+** from the wait function.
+*/
+typedef struct PRWaitGroup PRWaitGroup;
+
+/*
+** ENUMERATION: PRMWStatus
+** DESCRIPTION:
+** This enumeration is used to indicate the completion status of
+** a receive wait object. Generally stated, a positive value indicates
+** that the operation is not yet complete. A zero value indicates
+** success (similar to PR_SUCCESS) and any negative value is an
+** indication of failure. The reason for the failure can be retrieved
+** by calling PR_GetError().
+**
+** PR_MW_PENDING The operation is still pending. None of the other
+** fields of the object are currently valid.
+** PR_MW_SUCCESS The operation is complete and it was successful.
+** PR_MW_FAILURE The operation failed. The reason for the failure
+** can be retrieved by calling PR_GetError().
+** PR_MW_TIMEOUT The amount of time allowed for by the object's
+** 'timeout' field has expired w/o the operation
+** otherwise coming to closure.
+** PR_MW_INTERRUPT The operation was cancelled, either by the client
+** calling PR_CancelWaitFileDesc() or destroying the
+** entire wait group (PR_DestroyWaitGroup()).
+*/
+typedef enum PRMWStatus
+{
+ PR_MW_PENDING = 1,
+ PR_MW_SUCCESS = 0,
+ PR_MW_FAILURE = -1,
+ PR_MW_TIMEOUT = -2,
+ PR_MW_INTERRUPT = -3
+} PRMWStatus;
+
+/*
+** STRUCTURE: PRMemoryDescriptor
+** DESCRIPTION:
+** THis is a descriptor for an interval of memory. It contains a
+** pointer to the first byte of that memory and the length (in
+** bytes) of the interval.
+*/
+typedef struct PRMemoryDescriptor
+{
+ void *start; /* pointer to first byte of memory */
+ PRSize length; /* length (in bytes) of memory interval */
+} PRMemoryDescriptor;
+
+/*
+** STRUCTURE: PRMWaitClientData
+** DESCRIPTION:
+** An opague stucture for which a client MAY give provide a concrete
+** definition and associate with a receive descriptor. The NSPR runtime
+** does not manage this field. It is completely up to the client.
+*/
+typedef struct PRMWaitClientData PRMWaitClientData;
+
+/*
+** STRUCTURE: PRRecvWait
+** DESCRIPTION:
+** A receive wait object contains the file descriptor that is subject
+** to the wait and the amount of time (beginning epoch established
+** when the object is presented to the runtime) the the channel should
+** block before abandoning the process.
+**
+** The success of the wait operation will be noted in the object's
+** 'outcome' field. The fields are not valid when the NSPR runtime
+** is in possession of the object.
+**
+** The memory descriptor describes an interval of writable memory
+** in the caller's address space where data from an initial read
+** can be placed. The description may indicate a null interval.
+*/
+typedef struct PRRecvWait
+{
+ PRCList internal; /* internal runtime linkages */
+
+ PRFileDesc *fd; /* file descriptor associated w/ object */
+ PRMWStatus outcome; /* outcome of the current/last operation */
+ PRIntervalTime timeout; /* time allowed for entire operation */
+
+ PRInt32 bytesRecv; /* number of bytes transferred into buffer */
+ PRMemoryDescriptor buffer; /* where to store first segment of input data */
+ PRMWaitClientData *client; /* pointer to arbitrary client defined data */
+} PRRecvWait;
+
+/*
+** STRUCTURE: PRMWaitEnumerator
+** DESCRIPTION:
+** An enumeration object is used to store the state of an existing
+** enumeration over a wait group. The opaque object must be allocated
+** by the client and the reference presented on each call to the
+** pseudo-stateless enumerator. The enumeration objects are sharable
+** only in serial fashion.
+*/
+typedef struct PRMWaitEnumerator PRMWaitEnumerator;
+
+
+/*
+** FUNCTION: PR_AddWaitFileDesc
+** DESCRIPTION:
+** This function will effectively add a file descriptor to the
+** list of those waiting for network receive. The new descriptor
+** will be semantically tied to the wait group specified.
+**
+** The ownership for the storage pointed to by 'desc' is temporarily
+** passed over the the NSPR runtime. It will be handed back by the
+** function PR_WaitRecvReady().
+**
+** INPUTS
+** group A reference to a PRWaitGroup or NULL. Wait groups are
+** created by calling PR_CreateWaitGroup() and are used
+** to semantically group various file descriptors by the
+** client's application.
+** desc A reference to a valid PRRecvWait. The object of the
+** reference must be preserved and not be modified
+** until its ownership is returned to the client.
+** RETURN
+** PRStatus An indication of success. If equal to PR_FAILUE details
+** of the failure are avaiable via PR_GetError().
+**
+** ERRORS
+** PR_INVALID_ARGUMENT_ERROR
+** Invalid 'group' identifier or duplicate 'desc' object.
+** PR_OUT_OF_MEMORY_ERROR
+** Insuffient memory for internal data structures.
+** PR_INVALID_STATE_ERROR
+** The group is being destroyed.
+*/
+PR_EXTERN(PRStatus) PR_AddWaitFileDesc(PRWaitGroup *group, PRRecvWait *desc);
+
+/*
+** FUNCTION: PR_WaitRecvReady
+** DESCRIPTION:
+** PR_WaitRecvReady will block the calling thread until one of the
+** file descriptors that have been added via PR_AddWaitFileDesc is
+** available for input I/O.
+** INPUT
+** group A pointer to a valid PRWaitGroup or NULL (the null
+** group. The function will block the caller until a
+** channel from the wait group becomes ready for receive
+** or there is some sort of error.
+** RETURN
+** PRReciveWait
+** When the caller is resumed it is either returned a
+** valid pointer to a previously added receive wait or
+** a NULL. If the latter, the function has terminated
+** for a reason that can be determined by calling
+** PR_GetError().
+** If a valid pointer is returned, the reference is to the
+** file descriptor contained in the receive wait object.
+** The outcome of the wait operation may still fail, and
+** if it has, that fact will be noted in the object's
+** outcome field. Details can be retrieved from PR_GetError().
+**
+** ERRORS
+** PR_INVALID_ARGUMENT_ERROR
+** The 'group' is not known by the runtime.
+** PR_PENDING_INTERRUPT_ERROR
+ The thread was interrupted.
+** PR_INVALID_STATE_ERROR
+** The group is being destroyed.
+*/
+PR_EXTERN(PRRecvWait*) PR_WaitRecvReady(PRWaitGroup *group);
+
+/*
+** FUNCTION: PR_CancelWaitFileDesc
+** DESCRIPTION:
+** PR_CancelWaitFileDesc is provided as a means for cancelling operations
+** on objects previously submitted by use of PR_AddWaitFileDesc(). If
+** the runtime knows of the object, it will be marked as having failed
+** because it was interrupted (similar to PR_Interrupt()). The first
+** available thread waiting on the group will be made to return the
+** PRRecvWait object with the outcome noted.
+**
+** INPUTS
+** group The wait group under which the wait receive object was
+** added.
+** desc A pointer to the wait receive object that is to be
+** cancelled.
+** RETURN
+** PRStatus If the wait receive object was located and associated
+** with the specified wait group, the status returned will
+** be PR_SUCCESS. There is still a race condition that would
+** permit the offected object to complete normally, but it
+** is assured that it will complete in the near future.
+** If the receive object or wait group are invalid, the
+** function will return with a status of PR_FAILURE.
+**
+** ERRORS
+** PR_INVALID_ARGUMENT_ERROR
+** The 'group' argument is not recognized as a valid group.
+** PR_COLLECTION_EMPTY_ERROR
+** There are no more receive wait objects in the group's
+** collection.
+** PR_INVALID_STATE_ERROR
+** The group is being destroyed.
+*/
+PR_EXTERN(PRStatus) PR_CancelWaitFileDesc(PRWaitGroup *group, PRRecvWait *desc);
+
+/*
+** FUNCTION: PR_CancelWaitGroup
+** DESCRIPTION:
+** PR_CancelWaitGroup is provided as a means for cancelling operations
+** on objects previously submitted by use of PR_AddWaitFileDesc(). Each
+** successive call will return a pointer to a PRRecvWait object that
+** was previously registered via PR_AddWaitFileDesc(). If no wait
+** objects are associated with the wait group, a NULL will be returned.
+** This function should be called in a loop until a NULL is returned
+** to reclaim all the wait objects prior to calling PR_DestroyWaitGroup().
+**
+** INPUTS
+** group The wait group under which the wait receive object was
+** added.
+** RETURN
+** PRRecvWait* If the wait group is valid and at least one receive wait
+** object is present in the group, that object will be
+** marked as PR_MW_INTERRUPT'd and removed from the group's
+** queues. Otherwise a NULL will be returned and the reason
+** for the NULL may be retrieved by calling PR_GetError().
+**
+** ERRORS
+** PR_INVALID_ARGUMENT_ERROR
+** PR_GROUP_EMPTY_ERROR
+*/
+PR_EXTERN(PRRecvWait*) PR_CancelWaitGroup(PRWaitGroup *group);
+
+/*
+** FUNCTION: PR_CreateWaitGroup
+** DESCRIPTION:
+** A wait group is an opaque object that a client may create in order
+** to semantically group various wait requests. Each wait group is
+** unique, including the default wait group (NULL). A wait request
+** that was added under a wait group will only be serviced by a caller
+** that specified the same wait group.
+**
+** INPUT
+** size The size of the hash table to be used to contain the
+** receive wait objects. This is just the initial size.
+** It will grow as it needs to, but to avoid that hassle
+** one can suggest a suitable size initially. It should
+** be ~30% larger than the maximum number of receive wait
+** objects expected.
+** RETURN
+** PRWaitGroup If successful, the function will return a pointer to an
+** object that was allocated by and owned by the runtime.
+** The reference remains valid until it is explicitly destroyed
+** by calling PR_DestroyWaitGroup().
+**
+** ERRORS
+** PR_OUT_OF_MEMORY_ERROR
+*/
+PR_EXTERN(PRWaitGroup*) PR_CreateWaitGroup(PRInt32 size);
+
+/*
+** FUNCTION: PR_DestroyWaitGroup
+** DESCRIPTION:
+** Undo the effects of PR_CreateWaitGroup(). Any receive wait operations
+** on the group will be treated as if the each had been the target of a
+** PR_CancelWaitFileDesc().
+**
+** INPUT
+** group Reference to a wait group previously allocated using
+** PR_CreateWaitGroup().
+** RETURN
+** PRStatus Will be PR_SUCCESS if the wait group was valid and there
+** are no receive wait objects in that group. Otherwise
+** will indicate PR_FAILURE.
+**
+** ERRORS
+** PR_INVALID_ARGUMENT_ERROR
+** The 'group' argument does not reference a known object.
+** PR_INVALID_STATE_ERROR
+** The group still contains receive wait objects.
+*/
+PR_EXTERN(PRStatus) PR_DestroyWaitGroup(PRWaitGroup *group);
+
+/*
+** FUNCTION: PR_CreateMWaitEnumerator
+** DESCRIPTION:
+** The PR_CreateMWaitEnumerator() function returns a reference to an
+** opaque PRMWaitEnumerator object. The enumerator object is required
+** as an argument for each successive call in the stateless enumeration
+** of the indicated wait group.
+**
+** group The wait group that the enumeration is intended to
+** process. It may be be the default wait group (NULL).
+** RETURN
+** PRMWaitEnumerator* group
+** A reference to an object that will be used to store
+** intermediate state of enumerations.
+** ERRORS
+** Errors are indicated by the function returning a NULL.
+** PR_INVALID_ARGUMENT_ERROR
+** The 'group' argument does not reference a known object.
+** PR_OUT_OF_MEMORY_ERROR
+*/
+PR_EXTERN(PRMWaitEnumerator*) PR_CreateMWaitEnumerator(PRWaitGroup *group);
+
+/*
+** FUNCTION: PR_DestroyMWaitEnumerator
+** DESCRIPTION:
+** Destroys the object created by PR_CreateMWaitEnumerator(). The reference
+** used as an argument becomes invalid.
+**
+** INPUT
+** PRMWaitEnumerator* enumerator
+** The PRMWaitEnumerator object to destroy.
+** RETURN
+** PRStatus
+** PR_SUCCESS if successful, PR_FAILURE otherwise.
+** ERRORS
+** PR_INVALID_ARGUMENT_ERROR
+** The enumerator is invalid.
+*/
+PR_EXTERN(PRStatus) PR_DestroyMWaitEnumerator(PRMWaitEnumerator* enumerator);
+
+/*
+** FUNCTION: PR_EnumerateWaitGroup
+** DESCRIPTION:
+** PR_EnumerateWaitGroup is a thread safe enumerator over a wait group.
+** Each call to the enumerator must present a valid PRMWaitEnumerator
+** rererence and a pointer to the "previous" element returned from the
+** enumeration process or a NULL.
+**
+** An enumeration is started by passing a NULL as the "previous" value.
+** Subsequent calls to the enumerator must pass in the result of the
+** previous call. The enumeration end is signaled by the runtime returning
+** a NULL as the result.
+**
+** Modifications to the content of the wait group are allowed during
+** an enumeration. The effect is that the enumeration may have to be
+** "reset" and that may result in duplicates being returned from the
+** enumeration.
+**
+** An enumeration may be abandoned at any time. The runtime is not
+** keeping any state, so there are no issues in that regard.
+*/
+PR_EXTERN(PRRecvWait*) PR_EnumerateWaitGroup(
+ PRMWaitEnumerator *enumerator, const PRRecvWait *previous);
+
+PR_END_EXTERN_C
+
+#endif /* defined(_PRMWAIT_H) */
+
+/* prmwait.h */
diff --git a/pr/include/prnetdb.h b/pr/include/prnetdb.h
new file mode 100644
index 00000000..0f6d0a97
--- /dev/null
+++ b/pr/include/prnetdb.h
@@ -0,0 +1,295 @@
+/* -*- 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 prnetdb_h___
+#define prnetdb_h___
+
+#include "prtypes.h"
+#include "prio.h"
+
+PR_BEGIN_EXTERN_C
+
+
+/*
+ *********************************************************************
+ * Translate an Internet address to/from a character string
+ *********************************************************************
+ */
+PR_EXTERN(PRStatus) PR_StringToNetAddr(
+ const char *string, PRNetAddr *addr);
+
+PR_EXTERN(PRStatus) PR_NetAddrToString(
+ const PRNetAddr *addr, char *string, PRUint32 size);
+
+/*
+** Structures returned by network data base library. All addresses are
+** supplied in host order, and returned in network order (suitable for
+** use in system calls).
+*/
+/*
+** Beware that WINSOCK.H defines h_addrtype and h_length as short.
+** Client code does direct struct copies of hostent to PRHostEnt and
+** hence the ifdef.
+*/
+typedef struct PRHostEnt {
+ char *h_name; /* official name of host */
+ char **h_aliases; /* alias list */
+#if defined(WIN32) || defined(WIN16)
+ PRInt16 h_addrtype; /* host address type */
+ PRInt16 h_length; /* length of address */
+#else
+ PRInt32 h_addrtype; /* host address type */
+ PRInt32 h_length; /* length of address */
+#endif
+ char **h_addr_list; /* list of addresses from name server */
+} PRHostEnt;
+
+/* A safe size to use that will mostly work... */
+#if (defined(AIX) && defined(_THREAD_SAFE)) || defined(OSF1)
+#define PR_NETDB_BUF_SIZE sizeof(struct protoent_data)
+#else
+#define PR_NETDB_BUF_SIZE 1024
+#endif
+
+/***********************************************************************
+** FUNCTION:
+** DESCRIPTION: PR_GetHostByName()
+** Lookup a host by name.
+**
+** INPUTS:
+** char *hostname Character string defining the host name of interest
+** char *buf A scratch buffer for the runtime to return result.
+** This buffer is allocated by the caller.
+** PRIntn bufsize Number of bytes in 'buf'. A recommnded value to
+** use is PR_NETDB_BUF_SIZE.
+** OUTPUTS:
+** PRHostEnt *hostentry
+** This structure is filled in by the runtime if
+** the function returns PR_SUCCESS. This structure
+** is allocated by the caller.
+** RETURN:
+** PRStatus PR_SUCCESS if the lookup succeeds. If it fails
+** the result will be PR_FAILURE and the reason
+** for the failure can be retrieved by PR_GetError().
+***********************************************************************/
+PR_EXTERN(PRStatus) PR_GetHostByName(
+ const char *hostname, char *buf, PRIntn bufsize, PRHostEnt *hostentry);
+
+/***********************************************************************
+** FUNCTION:
+** DESCRIPTION: PR_GetHostByAddr()
+** Lookup a host entry by its network address.
+**
+** INPUTS:
+** char *hostaddr IP address of host in question
+** char *buf A scratch buffer for the runtime to return result.
+** This buffer is allocated by the caller.
+** PRIntn bufsize Number of bytes in 'buf'. A recommnded value to
+** use is PR_NETDB_BUF_SIZE.
+** OUTPUTS:
+** PRHostEnt *hostentry
+** This structure is filled in by the runtime if
+** the function returns PR_SUCCESS. This structure
+** is allocated by the caller.
+** RETURN:
+** PRStatus PR_SUCCESS if the lookup succeeds. If it fails
+** the result will be PR_FAILURE and the reason
+** for the failure can be retrieved by PR_GetError().
+***********************************************************************/
+PR_EXTERN(PRStatus) PR_GetHostByAddr(
+ const PRNetAddr *hostaddr, char *buf, PRIntn bufsize, PRHostEnt *hostentry);
+
+/***********************************************************************
+** FUNCTION: PR_EnumerateHostEnt()
+** DESCRIPTION:
+** A stateless enumerator over a PRHostEnt structure acquired from
+** PR_GetHostByName() PR_GetHostByAddr() to evaluate the possible
+** network addresses.
+**
+** INPUTS:
+** PRIntn enumIndex Index of the enumeration. The enumeration starts
+** and ends with a value of zero.
+**
+** PRHostEnt *hostEnt A pointer to a host entry struct that was
+** previously returned by PR_GetHostByName() or
+** PR_GetHostByAddr().
+**
+** PRUint16 port The port number to be assigned as part of the
+** PRNetAddr.
+**
+** OUTPUTS:
+** PRNetAddr *address A pointer to an address structure that will be
+** filled in by the call to the enumeration if the
+** result of the call is greater than zero.
+**
+** RETURN:
+** PRIntn The value that should be used for the next call
+** of the enumerator ('enumIndex'). The enumeration
+** is ended if this value is returned zero.
+** If a value of -1 is returned, the enumeration
+** has failed. The reason for the failure can be
+** retrieved by calling PR_GetError().
+***********************************************************************/
+PR_EXTERN(PRIntn) PR_EnumerateHostEnt(
+ PRIntn enumIndex, const PRHostEnt *hostEnt, PRUint16 port, PRNetAddr *address);
+
+/***********************************************************************
+** FUNCTION: PR_InitializeNetAddr(),
+** DESCRIPTION:
+** Initialize the fields of a PRNetAddr, assigning well known values as
+** appropriate.
+**
+** INPUTS
+** PRNetAddrValue val The value to be assigned to the IP Address portion
+** of the network address. This can only specify the
+** special well known values that are equivalent to
+** INADDR_ANY and INADDR_LOOPBACK.
+**
+** PRUInt16 port The port number to be assigned in the structure.
+**
+** OUTPUTS:
+** PRNetAddr *addr The address to be manipulated.
+**
+** RETURN:
+** PRStatus To indicate success or failure. If the latter, the
+** reason for the failure can be retrieved by calling
+** PR_GetError();
+***********************************************************************/
+typedef enum PRNetAddrValue
+{
+ PR_IpAddrNull, /* do NOT overwrite the IP address */
+ PR_IpAddrAny, /* assign logical INADDR_ANY to IP address */
+ PR_IpAddrLoopback /* assign logical INADDR_LOOPBACK */
+} PRNetAddrValue;
+
+PR_EXTERN(PRStatus) PR_InitializeNetAddr(
+ PRNetAddrValue val, PRUint16 port, PRNetAddr *addr);
+
+/***********************************************************************
+** FUNCTION:
+** DESCRIPTION: PR_GetProtoByName()
+** Lookup a protocol entry based on protocol's name
+**
+** INPUTS:
+** char *protocolname Character string of the protocol's name.
+** char *buf A scratch buffer for the runtime to return result.
+** This buffer is allocated by the caller.
+** PRIntn bufsize Number of bytes in 'buf'. A recommnded value to
+** use is PR_NETDB_BUF_SIZE.
+** OUTPUTS:
+** PRHostEnt *PRProtoEnt
+** This structure is filled in by the runtime if
+** the function returns PR_SUCCESS. This structure
+** is allocated by the caller.
+** RETURN:
+** PRStatus PR_SUCCESS if the lookup succeeds. If it fails
+** the result will be PR_FAILURE and the reason
+** for the failure can be retrieved by PR_GetError().
+***********************************************************************/
+
+typedef struct PRProtoEnt {
+ char *p_name; /* official protocol name */
+ char **p_aliases; /* alias list */
+#if defined(WIN32) || defined(WIN16)
+ PRInt16 p_num; /* protocol # */
+#else
+ PRInt32 p_num; /* protocol # */
+#endif
+} PRProtoEnt;
+
+PR_EXTERN(PRStatus) PR_GetProtoByName(
+ const char* protocolname, char* buffer, PRInt32 bufsize, PRProtoEnt* result);
+
+/***********************************************************************
+** FUNCTION:
+** DESCRIPTION: PR_GetProtoByNumber()
+** Lookup a protocol entry based on protocol's number
+**
+** INPUTS:
+** PRInt32 protocolnumber
+** Number assigned to the protocol.
+** char *buf A scratch buffer for the runtime to return result.
+** This buffer is allocated by the caller.
+** PRIntn bufsize Number of bytes in 'buf'. A recommnded value to
+** use is PR_NETDB_BUF_SIZE.
+** OUTPUTS:
+** PRHostEnt *PRProtoEnt
+** This structure is filled in by the runtime if
+** the function returns PR_SUCCESS. This structure
+** is allocated by the caller.
+** RETURN:
+** PRStatus PR_SUCCESS if the lookup succeeds. If it fails
+** the result will be PR_FAILURE and the reason
+** for the failure can be retrieved by PR_GetError().
+***********************************************************************/
+PR_EXTERN(PRStatus) PR_GetProtoByNumber(
+ PRInt32 protocolnumber, char* buffer, PRInt32 bufsize, PRProtoEnt* result);
+
+/***********************************************************************
+** FUNCTION:
+** DESCRIPTION: PR_SetIPv6Enable()
+** Enable IPv6 capability on a platform that supports the architecture.
+**
+** Note: IPv6 must first be enabled for the host platform. If it is not,
+** the function will always return PR_FAILURE on any attempt to
+** change the setting.
+**
+** INPUTS:
+** PRBool itIs
+** Assign it a value of PR_TRUE to turn on IPv6
+** addressing, PR_FALSE to turn it off.
+** RETURN:
+** PRStatus PR_SUCCESS if the IPv6 is enabled for this particular
+** host. Otherwise it will return failure and GetError()
+** will confirm the result by indicating that the
+** protocol is not supported
+** (PR_PROTOCOL_NOT_SUPPORTED_ERROR)
+***********************************************************************/
+PR_EXTERN(PRStatus) PR_SetIPv6Enable(PRBool itIs);
+
+/***********************************************************************
+** FUNCTIONS: PR_ntohs, PR_ntohl, PR_ntohll, PR_htons, PR_htonl, PR_htonll
+**
+** DESCRIPTION: API entries for the common byte ordering routines.
+**
+** PR_ntohs 16 bit conversion from network to host
+** PR_ntohl 32 bit conversion from network to host
+** PR_ntohll 64 bit conversion from network to host
+** PR_htons 16 bit conversion from host to network
+** PR_htonl 32 bit conversion from host to network
+** PR_ntonll 64 bit conversion from host to network
+**
+***********************************************************************/
+PR_EXTERN(PRUint16) PR_ntohs(PRUint16);
+PR_EXTERN(PRUint32) PR_ntohl(PRUint32);
+PR_EXTERN(PRUint64) PR_ntohll(PRUint64);
+PR_EXTERN(PRUint16) PR_htons(PRUint16);
+PR_EXTERN(PRUint32) PR_htonl(PRUint32);
+PR_EXTERN(PRUint64) PR_htonll(PRUint64);
+
+/***********************************************************************
+** FUNCTION: PR_FamilyInet
+**
+** DESCRIPTION: Routine to get value of address family for Internet Protocol
+**
+***********************************************************************/
+PR_EXTERN(PRUint16) PR_FamilyInet(void);
+
+PR_END_EXTERN_C
+
+#endif /* prnetdb_h___ */
diff --git a/pr/include/prolock.h b/pr/include/prolock.h
new file mode 100644
index 00000000..b9dcd9f9
--- /dev/null
+++ b/pr/include/prolock.h
@@ -0,0 +1,196 @@
+/* -*- 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 prolock_h___
+#define prolock_h___
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+#if defined(DEBUG) || defined(FORCE_NSPR_ORDERED_LOCKS)
+
+/*
+** A locking mechanism, built on the existing PRLock definiion,
+** is provided that will permit applications to define a Lock
+** Hierarchy (or Lock Ordering) schema. An application designed
+** using the Ordered Lock functions will terminate with a
+** diagnostic message when a lock inversion condition is
+** detected.
+**
+** The lock ordering detection is complile-time enabled only. in
+** optimized builds of NSPR, the Ordered Lock functions map
+** directly to PRLock functions, providing no lock order
+** detection.
+**
+** The Ordered Lock Facility is compiled in when DEBUG is defined at
+** compile time. Ordered Lock can be forced on in optimized builds by
+** defining FORCE_NSPR_ORDERED_LOCK at compile time. Both the
+** application using Ordered Lock and NSPR must be compiled with the
+** facility enabled to achieve the desired results.
+**
+** Application designers should use the macro interfaces to the Ordered
+** Lock facility to ensure that it is compiled out in optimized builds.
+**
+** Application designers are responsible for defining their own
+** lock hierarchy.
+**
+** Ordered Lock is thread-safe and SMP safe.
+**
+** See Also: prlock.h
+**
+** /lth. 10-Jun-1998.
+**
+*/
+
+/*
+** Opaque type for ordered lock.
+** ... Don't even think of looking in here.
+**
+*/
+
+typedef void * PROrderedLock;
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_CreateOrderedLock() -- Create an Ordered Lock
+**
+** DESCRIPTION: PR_CreateOrderedLock() creates an ordered lock.
+**
+** INPUTS:
+** order: user defined order of this lock.
+** name: name of the lock. For debugging purposes.
+**
+** OUTPUTS: returned
+**
+** RETURNS: PR_OrderedLock pointer
+**
+** RESTRICTIONS:
+**
+*/
+#define PR_CREATE_ORDERED_LOCK(order,name)\
+ PR_CreateOrderedLock((order),(name))
+
+PR_EXTERN(PROrderedLock *)
+ PR_CreateOrderedLock(
+ PRInt32 order,
+ const char *name
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_DestroyOrderedLock() -- Destroy an Ordered Lock
+**
+** DESCRIPTION: PR_DestroyOrderedLock() destroys the ordered lock
+** referenced by lock.
+**
+** INPUTS: lock: pointer to a PROrderedLock
+**
+** OUTPUTS: the lock is destroyed
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#define PR_DESTROY_ORDERED_LOCK(lock) PR_DestroyOrderedLock((lock))
+
+PR_EXTERN(void)
+ PR_DestroyOrderedLock(
+ PROrderedLock *lock
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_LockOrderedLock() -- Lock an ordered lock
+**
+** DESCRIPTION: PR_LockOrderedLock() locks the ordered lock
+** referenced by lock. If the order of lock is less than or equal
+** to the order of the highest lock held by the locking thread,
+** the function asserts.
+**
+** INPUTS: lock: a pointer to a PROrderedLock
+**
+** OUTPUTS: The lock is held or the fucntion asserts.
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#define PR_LOCK_ORDERED_LOCK(lock) PR_LockOrderedLock((lock))
+
+PR_EXTERN(void)
+ PR_LockOrderedLock(
+ PROrderedLock *lock
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_UnlockOrderedLock() -- unlock and Ordered Lock
+**
+** DESCRIPTION: PR_UnlockOrderedLock() unlocks the lock referenced
+** by lock.
+**
+** INPUTS: lock: a pointer to a PROrderedLock
+**
+** OUTPUTS: the lock is unlocked
+**
+** RETURNS:
+** PR_SUCCESS
+** PR_FAILURE
+**
+** RESTRICTIONS:
+**
+*/
+#define PR_UNLOCK_ORDERED_LOCK(lock) PR_UnlockOrderedLock((lock))
+
+PR_EXTERN(PRStatus)
+ PR_UnlockOrderedLock(
+ PROrderedLock *lock
+);
+
+#else /* !(defined(DEBUG) || defined(FORCE_NSPR_ORDERED_LOCKS)) */
+/*
+** Map PROrderedLock and methods onto PRLock when ordered locking
+** is not compiled in.
+**
+*/
+#include <prlock.h>
+
+typedef PRLock PROrderedLock;
+
+#define PR_CREATE_ORDERED_LOCK(order) PR_NewLock()
+#define PR_DESTROY_ORDERED_LOCK(lock) PR_DestroyLock((lock))
+#define PR_LOCK_ORDERED_LOCK(lock) PR_Lock((lock))
+#define PR_UNLOCK_ORDERED_LOCK(lock) PR_Unlock((lock))
+
+#endif /* !(defined(DEBUG) || defined(FORCE_NSPR_ORDERED_LOCKS)) */
+
+PR_END_EXTERN_C
+
+#endif /* prolock_h___ */
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pr/include/prpdce.h b/pr/include/prpdce.h
new file mode 100644
index 00000000..b0bc7853
--- /dev/null
+++ b/pr/include/prpdce.h
@@ -0,0 +1,99 @@
+/* -*- 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: prpdce.h
+ * Description: This file is the API defined to allow for DCE (aka POSIX)
+ * thread emulation in an NSPR environment. It is not the
+ * intent that this be a fully supported API.
+ */
+
+#if !defined(PRPDCE_H)
+#define PRPDCE_H
+
+#include "prlock.h"
+#include "prcvar.h"
+#include "prtypes.h"
+#include "prinrval.h"
+
+PR_BEGIN_EXTERN_C
+
+#define _PR_NAKED_CV_LOCK (PRLock*)0xdce1dce1
+
+/*
+** Test and acquire a lock.
+**
+** If the lock is acquired by the calling thread, the
+** return value will be PR_SUCCESS. If the lock is
+** already held, by another thread or this thread, the
+** result will be PR_FAILURE.
+*/
+PR_EXTERN(PRStatus) PRP_TryLock(PRLock *lock);
+
+/*
+** Create a naked condition variable
+**
+** A "naked" condition variable is one that is not created bound
+** to a lock. The CV created with this function is the only type
+** that may be used in the subsequent "naked" condition variable
+** operations (see PRP_NakedWait, PRP_NakedNotify, PRP_NakedBroadcast);
+*/
+PR_EXTERN(PRCondVar*) PRP_NewNakedCondVar(void);
+
+/*
+** Destroy a naked condition variable
+**
+** Destroy the condition variable created by PR_NewNakedCondVar.
+*/
+PR_EXTERN(void) PRP_DestroyNakedCondVar(PRCondVar *cvar);
+
+/*
+** Wait on a condition
+**
+** Wait on the condition variable 'cvar'. It is asserted that
+** the lock protecting the condition 'lock' is held by the
+** calling thread. If more time expires than that declared in
+** 'timeout' the condition will be notified. Waits can be
+** interrupted by another thread.
+**
+** NB: The CV ('cvar') must be one created using PR_NewNakedCondVar.
+*/
+PR_EXTERN(PRStatus) PRP_NakedWait(
+ PRCondVar *cvar, PRLock *lock, PRIntervalTime timeout);
+
+/*
+** Notify a thread waiting on a condition
+**
+** Notify the condition specified 'cvar'.
+**
+** NB: The CV ('cvar') must be one created using PR_NewNakedCondVar.
+*/
+PR_EXTERN(PRStatus) PRP_NakedNotify(PRCondVar *cvar);
+
+/*
+** Notify all threads waiting on a condition
+**
+** Notify the condition specified 'cvar'.
+**
+** NB: The CV ('cvar') must be one created using PR_NewNakedCondVar.
+*/
+PR_EXTERN(PRStatus) PRP_NakedBroadcast(PRCondVar *cvar);
+
+PR_END_EXTERN_C
+
+#endif /* PRPDCE_H */
diff --git a/pr/include/prprf.h b/pr/include/prprf.h
new file mode 100644
index 00000000..22bfa376
--- /dev/null
+++ b/pr/include/prprf.h
@@ -0,0 +1,135 @@
+/* -*- 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 prprf_h___
+#define prprf_h___
+
+/*
+** API for PR printf like routines. Supports the following formats
+** %d - decimal
+** %u - unsigned decimal
+** %x - unsigned hex
+** %X - unsigned uppercase hex
+** %o - unsigned octal
+** %hd, %hu, %hx, %hX, %ho - 16-bit versions of above
+** %ld, %lu, %lx, %lX, %lo - 32-bit versions of above
+** %lld, %llu, %llx, %llX, %llo - 64 bit versions of above
+** %s - string
+** %c - character
+** %p - pointer (deals with machine dependent pointer size)
+** %f - float
+** %g - float
+*/
+#include "prtypes.h"
+#include "prio.h"
+#include <stdio.h>
+#include <stdarg.h>
+
+PR_BEGIN_EXTERN_C
+
+/*
+** sprintf into a fixed size buffer. Guarantees that a NUL is at the end
+** of the buffer. Returns the length of the written output, NOT including
+** the NUL, or (PRUint32)-1 if an error occurs.
+*/
+PR_EXTERN(PRUint32) PR_snprintf(char *out, PRUint32 outlen, const char *fmt, ...);
+
+/*
+** sprintf into a PR_MALLOC'd buffer. Return a pointer to the malloc'd
+** buffer on success, NULL on failure. Call "PR_smprintf_free" to release
+** the memory returned.
+*/
+PR_EXTERN(char*) PR_smprintf(const char *fmt, ...);
+
+/*
+** Free the memory allocated, for the caller, by PR_smprintf
+*/
+PR_EXTERN(void) PR_smprintf_free(char *mem);
+
+/*
+** "append" sprintf into a PR_MALLOC'd buffer. "last" is the last value of
+** the PR_MALLOC'd buffer. sprintf will append data to the end of last,
+** growing it as necessary using realloc. If last is NULL, PR_sprintf_append
+** will allocate the initial string. The return value is the new value of
+** last for subsequent calls, or NULL if there is a malloc failure.
+*/
+PR_EXTERN(char*) PR_sprintf_append(char *last, const char *fmt, ...);
+
+/*
+** sprintf into a function. The function "f" is called with a string to
+** place into the output. "arg" is an opaque pointer used by the stuff
+** function to hold any state needed to do the storage of the output
+** data. The return value is a count of the number of characters fed to
+** the stuff function, or (PRUint32)-1 if an error occurs.
+*/
+typedef PRIntn (*PRStuffFunc)(void *arg, const char *s, PRUint32 slen);
+
+PR_EXTERN(PRUint32) PR_sxprintf(PRStuffFunc f, void *arg, const char *fmt, ...);
+
+/*
+** fprintf to a PRFileDesc
+*/
+PR_EXTERN(PRUint32) PR_fprintf(struct PRFileDesc* fd, const char *fmt, ...);
+
+/*
+** va_list forms of the above.
+*/
+PR_EXTERN(PRUint32) PR_vsnprintf(char *out, PRUint32 outlen, const char *fmt, va_list ap);
+PR_EXTERN(char*) PR_vsmprintf(const char *fmt, va_list ap);
+PR_EXTERN(char*) PR_vsprintf_append(char *last, const char *fmt, va_list ap);
+PR_EXTERN(PRUint32) PR_vsxprintf(PRStuffFunc f, void *arg, const char *fmt, va_list ap);
+PR_EXTERN(PRUint32) PR_vfprintf(struct PRFileDesc* fd, const char *fmt, va_list ap);
+
+/*
+***************************************************************************
+** FUNCTION: PR_sscanf
+** DESCRIPTION:
+** PR_sscanf() scans the input character string, performs data
+** conversions, and stores the converted values in the data objects
+** pointed to by its arguments according to the format control
+** string.
+**
+** PR_sscanf() behaves the same way as the sscanf() function in the
+** Standard C Library (stdio.h), with the following exceptions:
+** - PR_sscanf() handles the NSPR integer and floating point types,
+** such as PRInt16, PRInt32, PRInt64, and PRFloat64, whereas
+** sscanf() handles the standard C types like short, int, long,
+** and double.
+** - PR_sscanf() has no multibyte character support, while sscanf()
+** does.
+** INPUTS:
+** const char *buf
+** a character string holding the input to scan
+** const char *fmt
+** the format control string for the conversions
+** ...
+** variable number of arguments, each of them is a pointer to
+** a data object in which the converted value will be stored
+** OUTPUTS: none
+** RETURNS: PRInt32
+** The number of values converted and stored.
+** RESTRICTIONS:
+** Multibyte characters in 'buf' or 'fmt' are not allowed.
+***************************************************************************
+*/
+
+PR_EXTERN(PRInt32) PR_sscanf(const char *buf, const char *fmt, ...);
+
+PR_END_EXTERN_C
+
+#endif /* prprf_h___ */
diff --git a/pr/include/prproces.h b/pr/include/prproces.h
new file mode 100644
index 00000000..55c64d7f
--- /dev/null
+++ b/pr/include/prproces.h
@@ -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.
+ */
+
+#ifndef prproces_h___
+#define prproces_h___
+
+#include "prtypes.h"
+#include "prio.h"
+
+PR_BEGIN_EXTERN_C
+
+/************************************************************************/
+/*****************************PROCESS OPERATIONS*************************/
+/************************************************************************/
+
+typedef struct PRProcess PRProcess;
+typedef struct PRProcessAttr PRProcessAttr;
+
+PR_EXTERN(PRProcessAttr *) PR_NewProcessAttr(void);
+
+PR_EXTERN(void) PR_ResetProcessAttr(PRProcessAttr *attr);
+
+PR_EXTERN(void) PR_DestroyProcessAttr(PRProcessAttr *attr);
+
+PR_EXTERN(void) PR_ProcessAttrSetStdioRedirect(
+ PRProcessAttr *attr,
+ PRSpecialFD stdioFd,
+ PRFileDesc *redirectFd
+);
+
+/*
+ * OBSOLETE -- use PR_ProcessAttrSetStdioRedirect instead.
+ */
+PR_EXTERN(void) PR_SetStdioRedirect(
+ PRProcessAttr *attr,
+ PRSpecialFD stdioFd,
+ PRFileDesc *redirectFd
+);
+
+PR_EXTERN(PRStatus) PR_ProcessAttrSetCurrentDirectory(
+ PRProcessAttr *attr,
+ const char *dir
+);
+
+/*
+** Create a new process
+**
+** Create a new process executing the file specified as 'path' and with
+** the supplied arguments and environment.
+**
+** This function may fail because of illegal access (permissions),
+** invalid arguments or insufficient resources.
+**
+** A process may be created such that the creator can later synchronize its
+** termination using PR_WaitProcess().
+*/
+
+PR_EXTERN(PRProcess*) PR_CreateProcess(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr);
+
+PR_EXTERN(PRStatus) PR_CreateProcessDetached(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr);
+
+PR_EXTERN(PRStatus) PR_DetachProcess(PRProcess *process);
+
+PR_EXTERN(PRStatus) PR_WaitProcess(PRProcess *process, PRInt32 *exitCode);
+
+PR_EXTERN(PRStatus) PR_KillProcess(PRProcess *process);
+
+PR_END_EXTERN_C
+
+#endif /* prproces_h___ */
diff --git a/pr/include/prsystem.h b/pr/include/prsystem.h
new file mode 100644
index 00000000..96c57d50
--- /dev/null
+++ b/pr/include/prsystem.h
@@ -0,0 +1,79 @@
+/* -*- 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 prsystem_h___
+#define prsystem_h___
+
+/*
+** API to NSPR functions returning system info.
+*/
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+/*
+** Get the host' directory separator.
+** Pathnames are then assumed to be of the form:
+** [<sep><root_component><sep>]*(<component><sep>)<leaf_name>
+*/
+
+PR_EXTERN(char) PR_GetDirectorySeparator(void);
+
+/*
+** OBSOLETE -- the function name is misspelled.
+** Use PR_GetDirectorySeparator instead.
+*/
+
+PR_EXTERN(char) PR_GetDirectorySepartor(void);
+
+
+/* Types of information available via PR_GetSystemInfo(...) */
+typedef enum {
+ PR_SI_HOSTNAME,
+ PR_SI_SYSNAME,
+ PR_SI_RELEASE,
+ PR_SI_ARCHITECTURE
+} PRSysInfo;
+
+
+/*
+** If successful returns a null termintated string in 'buf' for
+** the information indicated in 'cmd'. If unseccussful the reason for
+** the failure can be retrieved from PR_GetError().
+**
+** The buffer is allocated by the caller and should be at least
+** SYS_INFO_BUFFER_LENGTH bytes in length.
+*/
+
+#define SYS_INFO_BUFFER_LENGTH 256
+
+PR_EXTERN(PRStatus) PR_GetSystemInfo(PRSysInfo cmd, char *buf, PRUint32 buflen);
+
+/*
+** Return the number of bytes in a page
+*/
+PR_EXTERN(PRInt32) PR_GetPageSize(void);
+
+/*
+** Return log2 of the size of a page
+*/
+PR_EXTERN(PRInt32) PR_GetPageShift(void);
+
+PR_END_EXTERN_C
+
+#endif /* prsystem_h___ */
diff --git a/pr/include/prthread.h b/pr/include/prthread.h
new file mode 100644
index 00000000..c9ff0bce
--- /dev/null
+++ b/pr/include/prthread.h
@@ -0,0 +1,254 @@
+/* -*- 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 prthread_h___
+#define prthread_h___
+
+/*
+** API for NSPR threads. On some architectures (MAC and WIN16
+** notably) pre-emptibility is not guaranteed. Hard priority scheduling
+** is not guaranteed, so programming using priority based synchronization
+** is a no-no.
+**
+** NSPR threads are scheduled based loosly on their client set priority.
+** In general, a thread of a higher priority has a statistically better
+** chance of running relative to threads of lower priority. However,
+** NSPR uses multiple strategies to provide execution vehicles for thread
+** abstraction of various host platforms. As it turns out, there is little
+** NSPR can do to affect the scheduling attributes of "GLOBAL" threads.
+** However, a semblance of GLOBAL threads is used to implement "LOCAL"
+** threads. An arbitrary number of such LOCAL threads can be assigned to
+** a single GLOBAL thread.
+**
+** For scheduling, NSPR will attempt to run the highest priority LOCAL
+** thread associated with a given GLOBAL thread. It is further assumed
+** that the host OS will apply some form of "fair" scheduling on the
+** GLOBAL threads.
+**
+** Threads have a "system flag" which when set indicates the thread
+** doesn't count for determining when the process should exit (the
+** process exits when the last user thread exits).
+**
+** Threads also have a "scope flag" which controls whether the threads
+** are scheduled in the local scope or scheduled by the OS globally. This
+** indicates whether a thread is permanently bound to a native OS thread.
+** An unbound thread competes for scheduling resources in the same process.
+**
+** Another flag is "state flag" which control whether the thread is joinable.
+** It allows other threads to wait for the created thread to reach completion.
+**
+** Threads can have "per-thread-data" attached to them. Each thread has a
+** per-thread error number and error string which are updated when NSPR
+** operations fail.
+*/
+#include "prtypes.h"
+#include "prinrval.h"
+
+PR_BEGIN_EXTERN_C
+
+typedef struct PRThread PRThread;
+typedef struct PRThreadStack PRThreadStack;
+
+typedef enum PRThreadType {
+ PR_USER_THREAD,
+ PR_SYSTEM_THREAD
+} PRThreadType;
+
+typedef enum PRThreadScope {
+ PR_LOCAL_THREAD,
+ PR_GLOBAL_THREAD
+} PRThreadScope;
+
+typedef enum PRThreadState {
+ PR_JOINABLE_THREAD,
+ PR_UNJOINABLE_THREAD
+} PRThreadState;
+
+typedef enum PRThreadPriority
+{
+ PR_PRIORITY_FIRST = 0, /* just a placeholder */
+ PR_PRIORITY_LOW = 0, /* the lowest possible priority */
+ PR_PRIORITY_NORMAL = 1, /* most common expected priority */
+ PR_PRIORITY_HIGH = 2, /* slightly more aggressive scheduling */
+ PR_PRIORITY_URGENT = 3, /* it does little good to have more than one */
+ PR_PRIORITY_LAST = 3 /* this is just a placeholder */
+} PRThreadPriority;
+
+/*
+** Create a new thread:
+** "type" is the type of thread to create
+** "start(arg)" will be invoked as the threads "main"
+** "priority" will be created thread's priority
+** "scope" will specify whether the thread is local or global
+** "state" will specify whether the thread is joinable or not
+** "stackSize" the size of the stack, in bytes. The value can be zero
+** and then a machine specific stack size will be chosen.
+**
+** This can return NULL if some kind of error occurs, such as if memory is
+** tight.
+**
+** If you want the thread to start up waiting for the creator to do
+** something, enter a lock before creating the thread and then have the
+** threads start routine enter and exit the same lock. When you are ready
+** for the thread to run, exit the lock.
+**
+** If you want to detect the completion of the created thread, the thread
+** should be created joinable. Then, use PR_JoinThread to synchrnoize the
+** termination of another thread.
+**
+** When the start function returns the thread exits. If it is the last
+** PR_USER_THREAD to exit then the process exits.
+*/
+PR_EXTERN(PRThread*) PR_CreateThread(PRThreadType type,
+ void (*start)(void *arg),
+ void *arg,
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize);
+
+/*
+** 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 blocked until the target thread completes.
+** Several threads cannot wait for the same thread to complete; one thread
+** will operate 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_EXTERN(PRStatus) PR_JoinThread(PRThread *thread);
+
+/*
+** Return the current thread object for the currently running code.
+** Never returns NULL.
+*/
+PR_EXTERN(PRThread*) PR_GetCurrentThread(void);
+#define PR_CurrentThread() PR_GetCurrentThread() /* for nspr1.0 compat. */
+
+/*
+** Get the priority of "thread".
+*/
+PR_EXTERN(PRThreadPriority) PR_GetThreadPriority(const PRThread *thread);
+
+/*
+** Change the priority of the "thread" to "priority".
+*/
+PR_EXTERN(void) PR_SetThreadPriority(PRThread *thread, PRThreadPriority priority);
+
+/*
+** 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.
+**
+** Each index is associationed with a destructor function ('dtor'). The function
+** may be specified as NULL when the index is created. If it is not NULL, the
+** function will be called when:
+** - the thread exits and the private data for the associated index
+** is not NULL,
+** - new thread private data is set and the current private data is
+** not NULL.
+**
+** 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.
+**
+** Returns PR_FAILURE if the total number of indices will exceed the maximun
+** allowed.
+*/
+typedef void (PR_CALLBACK *PRThreadPrivateDTOR)(void *priv);
+
+PR_EXTERN(PRStatus) PR_NewThreadPrivateIndex(
+ PRUintn *newIndex, PRThreadPrivateDTOR destructor);
+
+/*
+** Define some per-thread-private data.
+** "tpdIndex" 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 the index is invalid.
+*/
+PR_EXTERN(PRStatus) PR_SetThreadPrivate(PRUintn tpdIndex, void *priv);
+
+/*
+** Recover the per-thread-private data for the current thread. "tpdIndex" is
+** the index into the per-thread private data table.
+**
+** The returned value may be NULL which is indistinguishable from an error
+** condition.
+**
+** A thread can only get access to its own thread-specific-data.
+*/
+PR_EXTERN(void*) PR_GetThreadPrivate(PRUintn tpdIndex);
+
+/*
+** This routine sets the interrupt request for a target thread. The interrupt
+** request remains in the thread's state until it is delivered exactly once
+** or explicitly canceled.
+**
+** A thread that has been interrupted will fail all NSPR blocking operations
+** that return a PRStatus (I/O, waiting on a condition, etc).
+**
+** PR_Interrupt may itself fail if the target thread is invalid.
+*/
+PR_EXTERN(PRStatus) PR_Interrupt(PRThread *thread);
+
+/*
+** Clear the interrupt request for the calling thread. If no such request
+** is pending, this operation is a noop.
+*/
+PR_EXTERN(void) PR_ClearInterrupt(void);
+
+/*
+** Make the current thread sleep until "ticks" time amount of time
+** has expired. If "ticks" is PR_INTERVAL_NO_WAIT then the call is
+** equivalent to calling PR_Yield. Calling PR_Sleep with an argument
+** equivalent to PR_INTERVAL_NO_TIMEOUT is an error and will result
+** in a PR_FAILURE error return.
+*/
+PR_EXTERN(PRStatus) PR_Sleep(PRIntervalTime ticks);
+
+/*
+** Get the scoping of this thread.
+*/
+PR_EXTERN(PRThreadScope) PR_GetThreadScope(const PRThread *thread);
+
+/*
+** Get the type of this thread.
+*/
+PR_EXTERN(PRThreadType) PR_GetThreadType(const PRThread *thread);
+
+/*
+** Get the join state of this thread.
+*/
+PR_EXTERN(PRThreadState) PR_GetThreadState(const PRThread *thread);
+
+PR_END_EXTERN_C
+
+#endif /* prthread_h___ */
diff --git a/pr/include/prtime.h b/pr/include/prtime.h
new file mode 100644
index 00000000..3d91f084
--- /dev/null
+++ b/pr/include/prtime.h
@@ -0,0 +1,278 @@
+/* -*- 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.h --
+ *
+ * NSPR date and time functions
+ *
+ *-----------------------------------------------------------------------
+ */
+
+#ifndef prtime_h___
+#define prtime_h___
+
+#include "prlong.h"
+
+PR_BEGIN_EXTERN_C
+
+/**********************************************************************/
+/************************* TYPES AND CONSTANTS ************************/
+/**********************************************************************/
+
+#define PR_MSEC_PER_SEC 1000UL
+#define PR_USEC_PER_SEC 1000000UL
+#define PR_NSEC_PER_SEC 1000000000UL
+#define PR_USEC_PER_MSEC 1000UL
+#define PR_NSEC_PER_MSEC 1000000UL
+
+/*
+ * PRTime --
+ *
+ * NSPR represents basic time as 64-bit signed integers relative
+ * to midnight (00:00:00), January 1, 1970 Greenwich Mean Time (GMT).
+ * (GMT is also known as Coordinated Universal Time, UTC.)
+ * The units of time are in microseconds. Negative times are allowed
+ * to represent times prior to the January 1970 epoch. Such values are
+ * intended to be exported to other systems or converted to human
+ * readable form.
+ *
+ * Notes on porting: PRTime corresponds to time_t in ANSI C. NSPR 1.0
+ * simply uses PRInt64.
+ */
+
+typedef PRInt64 PRTime;
+
+/*
+ * Time zone and daylight saving time corrections applied to GMT to
+ * obtain the local time of some geographic location
+ */
+
+typedef struct PRTimeParameters {
+ PRInt32 tp_gmt_offset; /* the offset from GMT in seconds */
+ PRInt32 tp_dst_offset; /* contribution of DST in seconds */
+} PRTimeParameters;
+
+/*
+ * PRExplodedTime --
+ *
+ * Time broken down into human-readable components such as year, month,
+ * day, hour, minute, second, and microsecond. Time zone and daylight
+ * saving time corrections may be applied. If they are applied, the
+ * offsets from the GMT must be saved in the 'tm_params' field so that
+ * all the information is available to reconstruct GMT.
+ *
+ * Notes on porting: PRExplodedTime corrresponds to struct tm in
+ * ANSI C, with the following differences:
+ * - an additional field tm_usec;
+ * - replacing tm_isdst by tm_params;
+ * - the month field is spelled tm_month, not tm_mon;
+ * - we use absolute year, AD, not the year since 1900.
+ * The corresponding type in NSPR 1.0 is called PRTime. Below is
+ * a table of date/time type correspondence in the three APIs:
+ * API time since epoch time in components
+ * ANSI C time_t struct tm
+ * NSPR 1.0 PRInt64 PRTime
+ * NSPR 2.0 PRTime PRExplodedTime
+ */
+
+typedef struct PRExplodedTime {
+ PRInt32 tm_usec; /* microseconds past tm_sec (0-99999) */
+ PRInt32 tm_sec; /* seconds past tm_min (0-61, accomodating
+ up to two leap seconds) */
+ PRInt32 tm_min; /* minutes past tm_hour (0-59) */
+ PRInt32 tm_hour; /* hours past tm_day (0-23) */
+ PRInt32 tm_mday; /* days past tm_mon (1-31, note that it
+ starts from 1) */
+ PRInt32 tm_month; /* months past tm_year (0-11, Jan = 0) */
+ PRInt16 tm_year; /* absolute year, AD (note that we do not
+ count from 1900) */
+
+ PRInt8 tm_wday; /* calculated day of the week
+ (0-6, Sun = 0) */
+ PRInt16 tm_yday; /* calculated day of the year
+ (0-365, Jan 1 = 0) */
+
+ PRTimeParameters tm_params; /* time parameters used by conversion */
+} PRExplodedTime;
+
+/*
+ * PRTimeParamFn --
+ *
+ * A function of PRTimeParamFn type returns the time zone and
+ * daylight saving time corrections for some geographic location,
+ * given the current time in GMT. The input argument gmt should
+ * point to a PRExplodedTime that is in GMT, i.e., whose
+ * tm_params contains all 0's.
+ *
+ * For any time zone other than GMT, the computation is intended to
+ * consist of two steps:
+ * - Figure out the time zone correction, tp_gmt_offset. This number
+ * usually depends on the geographic location only. But it may
+ * also depend on the current time. For example, all of China
+ * is one time zone right now. But this situation may change
+ * in the future.
+ * - Figure out the daylight saving time correction, tp_dst_offset.
+ * This number depends on both the geographic location and the
+ * current time. Most of the DST rules are expressed in local
+ * current time. If so, one should apply the time zone correction
+ * to GMT before applying the DST rules.
+ */
+
+typedef PRTimeParameters (PR_CALLBACK_DECL *PRTimeParamFn)(const PRExplodedTime *gmt);
+
+/**********************************************************************/
+/****************************** FUNCTIONS *****************************/
+/**********************************************************************/
+
+/*
+ * The PR_Now routine returns the current time relative to the
+ * epoch, midnight, January 1, 1970 UTC. The units of the returned
+ * value are microseconds since the epoch.
+ *
+ * The values returned are not guaranteed to advance in a linear fashion
+ * due to the application of time correction protocols which synchronize
+ * computer clocks to some external time source. Consequently it should
+ * not be depended on for interval timing.
+ *
+ * The implementation is machine dependent.
+ * Cf. time_t time(time_t *tp) in ANSI C.
+ */
+#if defined(HAVE_WATCOM_BUG_2)
+PRTime __pascal __export __loadds
+#else
+PR_EXTERN(PRTime)
+#endif
+PR_Now(void);
+
+/*
+ * Expand time binding it to time parameters provided by PRTimeParamFn.
+ * The calculation is envisoned to proceed in the following steps:
+ * - From given PRTime, calculate PRExplodedTime in GMT
+ * - Apply the given PRTimeParamFn to the GMT that we just calculated
+ * to obtain PRTimeParameters.
+ * - Add the PRTimeParameters offsets to GMT to get the local time
+ * as PRExplodedTime.
+ */
+
+PR_EXTERN(void) PR_ExplodeTime(
+ PRTime usecs, PRTimeParamFn params, PRExplodedTime *exploded);
+
+/* Reverse operation of PR_ExplodeTime */
+#if defined(HAVE_WATCOM_BUG_2)
+PRTime __pascal __export __loadds
+#else
+PR_EXTERN(PRTime)
+#endif
+PR_ImplodeTime(const PRExplodedTime *exploded);
+
+/*
+ * Adjust exploded time to normalize field overflows after manipulation.
+ * Note that the following fields of PRExplodedTime should not be
+ * manipulated:
+ * - tm_month and tm_year: because the number of days in a month and
+ * number of days in a year are not constant, it is ambiguous to
+ * manipulate the month and year fields, although one may be tempted
+ * to. For example, what does "a month from January 31st" mean?
+ * - tm_wday and tm_yday: these fields are calculated by NSPR. Users
+ * should treat them as "read-only".
+ */
+
+PR_EXTERN(void) PR_NormalizeTime(PRExplodedTime *time, PRTimeParamFn params);
+
+/**********************************************************************/
+/*********************** TIME PARAMETER FUNCTIONS *********************/
+/**********************************************************************/
+
+/* Time parameters that suit current host machine */
+PR_EXTERN(PRTimeParameters) PR_LocalTimeParameters(const PRExplodedTime *gmt);
+
+/* Time parameters that represent Greenwich Mean Time */
+PR_EXTERN(PRTimeParameters) PR_GMTParameters(const PRExplodedTime *gmt);
+
+/*
+ * Time parameters that represent the US Pacific Time Zone, with the
+ * current daylight saving time rules (for testing only)
+ */
+PR_EXTERN(PRTimeParameters) PR_USPacificTimeParameters(const PRExplodedTime *gmt);
+
+/*
+ * 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_EXTERN(PRStatus) PR_ParseTimeString (
+ const char *string,
+ PRBool default_to_gmt,
+ PRTime *result);
+
+/*
+ * FIXME: should we also have a formatting function, such as asctime, ctime,
+ * and strftime in standard C library? But this would involve
+ * internationalization issues. Might want to provide a US Engligh version.
+ */
+
+/**********************************************************************/
+/*********************** OLD COMPATIBILITYFUNCTIONS *******************/
+/**********************************************************************/
+#ifndef NO_NSPR_10_SUPPORT
+
+/* Format a time value into a buffer. Same semantics as strftime() */
+PR_EXTERN(PRUint32) PR_FormatTime(char *buf, int buflen, const char *fmt,
+ const PRExplodedTime *tm);
+
+/* Format a time value into a buffer. Time is always in US English format, regardless
+ * of locale setting.
+ */
+PR_EXTERN(PRUint32)
+PR_FormatTimeUSEnglish( char* buf, PRUint32 bufSize,
+ const char* format, const PRExplodedTime* time );
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+PR_END_EXTERN_C
+
+#endif /* prtime_h___ */
diff --git a/pr/include/prtrace.h b/pr/include/prtrace.h
new file mode 100644
index 00000000..807d4e45
--- /dev/null
+++ b/pr/include/prtrace.h
@@ -0,0 +1,635 @@
+/* -*- 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 prtrace_h___
+#define prtrace_h___
+/*
+** prtrace.h -- NSPR's Trace Facility.
+**
+** The Trace Facility provides a means to trace application
+** program events within a process. When implementing an
+** application program an engineer may insert a "Trace" function
+** call, passing arguments to be traced. The "Trace" function
+** combines the user trace data with identifying data and
+** writes this data in time ordered sequence into a circular
+** in-memory buffer; when the buffer fills, it wraps.
+**
+** Functions are provided to set and/or re-configure the size of
+** the trace buffer, control what events are recorded in the
+** buffer, enable and disable tracing based on specific user
+** supplied data and other control functions. Methods are provided
+** to record the trace entries in the in-memory trace buffer to
+** a file.
+**
+** Tracing may cause a performance degredation to the application
+** depending on the number and placement of calls to the tracing
+** facility. When tracing is compiled in and all tracing is
+** disabled via the runtime controls, the overhead should be
+** minimal. ... Famous last words, eh?
+**
+** When DEBUG is defined at compile time, the Trace Facility is
+** compiled as part of NSPR and any application using NSPR's
+** header files will have tracing compiled in. When DEBUG is not
+** defined, the Trace Facility is not compiled into NSPR nor
+** exported in its header files. If the Trace Facility is
+** desired in a non-debug build, then FORCE_NSPR_TRACE may be
+** defined at compile time for both the optimized build of NSPR
+** and the application. NSPR and any application using NSPR's
+** Trace Facility must be compiled with the same level of trace
+** conditioning or unresolved references may be realized at link
+** time.
+**
+** For any of the Trace Facility methods that requires a trace
+** handle as an input argument, the caller must ensure that the
+** trace handle argument is valid. An invalid trace handle
+** argument may cause unpredictable results.
+**
+** Trace Facility methods are thread-safe and SMP safe.
+**
+** Users of the Trace Facility should use the defined macros to
+** invoke trace methods, not the function calls directly. e.g.
+** PR_TRACE( h1,0,1,2, ...); not PR_Trace(h1,0,1,2, ...);
+**
+** Application designers should be aware of the effects of
+** debug and optimized build differences when using result of the
+** Trace Facility macros in expressions.
+**
+** See Also: prcountr.h
+**
+** /lth. 08-Jun-1998.
+*/
+
+#include "prtypes.h"
+#include "prthread.h"
+#include "prtime.h"
+
+PR_BEGIN_EXTERN_C
+
+/*
+** Opaque type for the trace handle
+** ... Don't even think about looking in here.
+**
+*/
+typedef void * PRTraceHandle;
+
+#if defined (DEBUG) || defined (FORCE_NSPR_TRACE)
+/*
+** PRTraceEntry -- A trace entry in the in-memory trace buffer
+** looks like this.
+**
+*/
+typedef struct PRTraceEntry
+{
+ PRThread *thread; /* The thread creating the trace entry */
+ PRTraceHandle handle; /* PRTraceHandle creating the trace entry */
+ PRTime time; /* Value of PR_Now() at time of trace entry */
+ PRUint32 userData[8]; /* user supplied trace data */
+} PRTraceEntry;
+
+/*
+** PRTraceOption -- command operands to
+** PR_[Set|Get]TraceOption(). See descriptive meanings there.
+**
+*/
+typedef enum PRTraceOption
+{
+ PRTraceBufSize,
+ PRTraceEnable,
+ PRTraceDisable,
+ PRTraceSuspend,
+ PRTraceResume,
+ PRTraceSuspendRecording,
+ PRTraceResumeRecording,
+ PRTraceLockHandles,
+ PRTraceUnLockHandles,
+ PRTraceStopRecording
+} PRTraceOption;
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_DEFINE_TRACE() -- Define a PRTraceHandle
+**
+** DESCRIPTION: PR_DEFINE_TRACE() is used to define a trace
+** handle.
+**
+*/
+#define PR_DEFINE_TRACE(name) PRTraceHandle name
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_INIT_TRACE_HANDLE() -- Set the value of a PRTraceHandle
+**
+** DESCRIPTION:
+** PR_INIT_TRACE_HANDLE() sets the value of a PRTraceHandle
+** to value. e.g. PR_INIT_TRACE_HANDLE( myHandle, NULL );
+**
+*/
+#define PR_INIT_TRACE_HANDLE(handle,value)\
+ (handle) = (PRCounterHandle)(value)
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_CreateTrace() -- Create a trace handle
+**
+** DESCRIPTION:
+** PR_CreateTrace() creates a new trace handle. Tracing is
+** enabled for this handle when it is created. The trace handle
+** is intended for use in other Trace Facility calls.
+**
+** PR_CreateTrace() registers the QName, RName and description
+** data so that this data can be retrieved later.
+**
+** INPUTS:
+** qName: pointer to string. QName for this trace handle.
+**
+** rName: pointer to string. RName for this trace handle.
+**
+** description: pointer to string. Descriptive data about this
+** trace handle.
+**
+** OUTPUTS:
+** Creates the trace handle.
+** Registers the QName and RName with the trace facility.
+**
+** RETURNS:
+** PRTraceHandle
+**
+** RESTRICTIONS:
+** qName is limited to 31 characters.
+** rName is limited to 31 characters.
+** description is limited to 255 characters.
+**
+*/
+#define PRTRACE_NAME_MAX 31
+#define PRTRACE_DESC_MAX 255
+
+#define PR_CREATE_TRACE(handle,qName,rName,description)\
+ (handle) = PR_CreateTrace((qName),(rName),(description))
+
+PR_EXTERN(PRTraceHandle)
+ PR_CreateTrace(
+ const char *qName, /* QName for this trace handle */
+ const char *rName, /* RName for this trace handle */
+ const char *description /* description for this trace handle */
+);
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_DestroyTrace() -- Destroy a trace handle
+**
+** DESCRIPTION:
+** PR_DestroyTrace() removes the referenced trace handle and
+** associated QName, RName and description data from the Trace
+** Facility.
+**
+** INPUTS: handle. A PRTraceHandle
+**
+** OUTPUTS:
+** The trace handle is unregistered.
+** The QName, RName and description are removed.
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#define PR_DESTROY_TRACE(handle)\
+ PR_DestroyTrace((handle))
+
+PR_EXTERN(void)
+ PR_DestroyTrace(
+ PRTraceHandle handle /* Handle to be destroyed */
+);
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_Trace() -- Make a trace entry in the in-memory trace
+**
+** DESCRIPTION:
+** PR_Trace() makes an entry in the in-memory trace buffer for
+** the referenced trace handle. The next logically available
+** PRTraceEntry is used; when the next trace entry would overflow
+** the trace table, the table wraps.
+**
+** PR_Trace() for a specific trace handle may be disabled by
+** calling PR_SetTraceOption() specifying PRTraceDisable for the
+** trace handle to be disabled.
+**
+** INPUTS:
+** handle: PRTraceHandle. The trace handle for this trace.
+**
+** userData[0..7]: unsigned 32bit integers. user supplied data
+** that is copied into the PRTraceEntry
+**
+** OUTPUTS:
+** A PRTraceEntry is (conditionally) formatted in the in-memory
+** trace buffer.
+**
+** RETURNS: void.
+**
+** RESTRICTIONS:
+**
+*/
+#define PR_TRACE(handle,ud0,ud1,ud2,ud3,ud4,ud5,ud6,ud7)\
+ PR_Trace((handle),(ud0),(ud1),(ud2),(ud3),(ud4),(ud5),(ud6),(ud7))
+
+PR_EXTERN(void)
+ PR_Trace(
+ PRTraceHandle handle, /* use this trace handle */
+ PRUint32 userData0, /* User supplied data word 0 */
+ PRUint32 userData1, /* User supplied data word 1 */
+ PRUint32 userData2, /* User supplied data word 2 */
+ PRUint32 userData3, /* User supplied data word 3 */
+ PRUint32 userData4, /* User supplied data word 4 */
+ PRUint32 userData5, /* User supplied data word 5 */
+ PRUint32 userData6, /* User supplied data word 6 */
+ PRUint32 userData7 /* User supplied data word 7 */
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_SetTraceOption() -- Control the Trace Facility
+**
+** DESCRIPTION:
+** PR_SetTraceOption() controls the Trace Facility. Depending on
+** command and value, attributes of the Trace Facility may be
+** changed.
+**
+** INPUTS:
+** command: An enumerated value in the set of PRTraceOption.
+** value: pointer to the data to be set. Type of the data is
+** dependent on command; for each value of command, the type
+** and meaning of dereferenced value is shown.
+**
+** PRTraceBufSize: unsigned long: the size of the trace buffer,
+** in bytes.
+**
+** PRTraceEnable: PRTraceHandle. The trace handle to be
+** enabled.
+**
+** PRTraceDisable: PRTraceHandle. The trace handle to be
+** disabled.
+**
+** PRTraceSuspend: void. value must be NULL. All tracing is
+** suspended.
+**
+** PRTraceResume: void. value must be NULL. Tracing for all
+** previously enabled, prior to a PRTraceSuspend, is resumed.
+**
+** PRTraceStopRecording: void. value must be NULL. If recording
+** (see: ** PR_RecordTraceEntries()) is being done,
+** PRTraceStopRecording causes PR_RecordTraceEntries() to return
+** to its caller. If recording is not being done, this function
+** has no effect.
+**
+** PRTraceSuspendRecording: void. Must be NULL. If recording is
+** being done, PRTraceSuspendRecording causes further writes to
+** the trace file to be suspended. Data in the in-memory
+** trace buffer that would ordinarily be written to the
+** trace file will not be written. Trace entries will continue
+** to be entered in the in-memory buffer. If the Trace Facility
+** recording is already in a suspended state, the call has no
+** effect.
+**
+** PRTraceResumeRecording: void. value must be NULL. If
+** recording for the Trace Facility has been previously been
+** suspended, this causes recording to resume. Recording resumes
+** with the next in-memory buffer segment that would be written
+** if trace recording had not been suspended. If recording is
+** not currently suspended, the call has no effect.
+**
+** PRTraceLockHandles: void. value must be NULL. Locks the
+** trace handle lock. While the trace handle lock is held,
+** calls to PR_CreateTrace() will block until the lock is
+** released.
+**
+** PRTraceUnlockHandles: void. value must be NULL. Unlocks the
+** trace handle lock.
+**
+** OUTPUTS:
+** The operation of the Trace Facility may be changed.
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#define PR_SET_TRACE_OPTION(command,value)\
+ PR_SetTraceOption((command),(value))
+
+PR_EXTERN(void)
+ PR_SetTraceOption(
+ PRTraceOption command, /* One of the enumerated values */
+ void *value /* command value or NULL */
+);
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_GetTraceOption() -- Retrieve settings from the Trace Facility
+**
+** DESCRIPTION:
+** PR_GetTraceOption() retrieves the current setting of the
+** Trace Facility control depending on command.
+**
+**
+** PRTraceBufSize: unsigned long: the size of the trace buffer,
+** in bytes.
+**
+**
+** INPUTS:
+** command: one of the enumerated values in PRTraceOptions
+** valid for PR_GetTraceOption().
+**
+** OUTPUTS:
+** dependent on command.
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#define PR_GET_TRACE_OPTION(command,value)\
+ PR_GetTraceOption((command),(value))
+
+PR_EXTERN(void)
+ PR_GetTraceOption(
+ PRTraceOption command, /* One of the enumerated values */
+ void *value /* command value or NULL */
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_GetTraceHandleFromName() -- Retrieve an existing
+** handle by name.
+**
+** DESCRIPTION:
+** PR_GetTraceHandleFromName() retreives an existing tracehandle
+** using the name specified by qName and rName.
+**
+** INPUTS:
+** qName: pointer to string. QName for this trace handle.
+**
+** rName: pointer to string. RName for this trace handle.
+**
+**
+** OUTPUTS: returned.
+**
+** RETURNS:
+** PRTraceHandle associated with qName and rName or NULL when
+** there is no match.
+**
+** RESTRICTIONS:
+**
+*/
+#define PR_GET_TRACE_HANDLE_FROM_NAME(handle,qName,rName)\
+ (handle) = PR_GetTraceHandleFromName((qName),(rName))
+
+PR_EXTERN(PRTraceHandle)
+ PR_GetTraceHandleFromName(
+ const char *qName, /* QName search argument */
+ const char *rName /* RName search argument */
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_GetTraceNameFromHandle() -- Retreive trace name
+** by bandle.
+**
+** DESCRIPTION:
+** PR_GetTraceNameFromHandle() retreives the existing qName,
+** rName, and description for the referenced trace handle.
+**
+** INPUTS: handle: PRTraceHandle.
+**
+** OUTPUTS: pointers to the Trace Facility's copy of qName,
+** rName and description. ... Don't mess with these values.
+** They're mine.
+**
+** RETURNS: void
+**
+** RESTRICTIONS:
+**
+*/
+#define PR_GET_TRACE_NAME_FROM_HANDLE(handle,qName,rName,description)\
+ PR_GetTraceNameFromHandle((handle),(qName),(rName),(description))
+
+PR_EXTERN(void)
+ PR_GetTraceNameFromHandle(
+ PRTraceHandle handle, /* handle as search argument */
+ const char **qName, /* pointer to associated QName */
+ const char **rName, /* pointer to associated RName */
+ const char **description /* pointer to associated description */
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_FindNextTraceQname() -- Retrieive a QName handle
+** iterator.
+**
+** DESCRIPTION:
+** PR_FindNextTraceQname() retreives the first or next trace
+** QName handle, depending on the value of handle, from the trace
+** database. The PRTraceHandle returned can be used as an
+** iterator to traverse the QName handles in the Trace database.
+**
+** INPUTS:
+** handle: When NULL, PR_FindNextQname() returns the first QName
+** handle. When a handle is a valid PRTraceHandle previously
+** retreived using PR_FindNextQname() the next QName handle is
+** retreived.
+**
+** OUTPUTS: returned.
+**
+** RETURNS:
+** PRTraceHandle or NULL when there are no trace handles.
+**
+** RESTRICTIONS:
+** Iterating thru the trace handles via FindFirst/FindNext
+** should be done under protection of the trace handle lock.
+** See: PR_SetTraceOption( PRLockTraceHandles ).
+**
+*/
+#define PR_FIND_NEXT_TRACE_QNAME(next,handle)\
+ (next) = PR_FindNextTraceQname((handle))
+
+PR_EXTERN(PRTraceHandle)
+ PR_FindNextTraceQname(
+ PRTraceHandle handle
+);
+
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_FindNextTraceRname() -- Retrieive an RName handle
+** iterator.
+**
+** DESCRIPTION:
+** PR_FindNextTraceRname() retreives the first or next trace
+** RName handle, depending on the value of handle, from the trace
+** database. The PRTraceHandle returned can be used as an
+** iterator to traverse the RName handles in the Trace database.
+**
+** INPUTS:
+** rhandle: When NULL, PR_FindNextRname() returns the first
+** RName handle. When a handle is a valid PRTraceHandle
+** previously retreived using PR_FindNextRname() the next RName
+** handle is retreived.
+** qhandle: A valid PRTraceHandle retruned from a previous call
+** to PR_FIND_NEXT_TRACE_QNAME().
+**
+** OUTPUTS: returned.
+**
+** RETURNS:
+** PRTraceHandle or NULL when there are no trace handles.
+**
+** RESTRICTIONS:
+** Iterating thru the trace handles via FindNext should be done
+** under protection of the trace handle lock. See: (
+** PR_SetTraceOption( PRLockTraceHandles ).
+**
+*/
+#define PR_FIND_NEXT_TRACE_RNAME(next,rhandle,qhandle)\
+ (next) = PR_FindNextTraceRname((rhandle),(qhandle))
+
+PR_EXTERN(PRTraceHandle)
+ PR_FindNextTraceRname(
+ PRTraceHandle rhandle,
+ PRTraceHandle qhandle
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_RecordTraceEntries() -- Write trace entries to external media
+**
+** DESCRIPTION:
+** PR_RecordTraceEntries() causes entries in the in-memory trace
+** buffer to be written to external media.
+**
+** When PR_RecordTraceEntries() is called from an application
+** thread, the function appears to block until another thread
+** calls PR_SetTraceOption() with the PRTraceStopRecording
+** option. This suggests that PR_RecordTraceEntries() should be
+** called from a user supplied thread whose only job is to
+** record trace entries.
+**
+** The environment variable NSPR_TRACE_LOG controls the operation
+** of this function. When NSPR_TRACE_LOG is not defined in the
+** environment, no recording of trace entries occurs. When
+** NSPR_TRACE_LOG is defined, the value of its definition must be
+** the filename of the file to receive the trace entry buffer.
+**
+** PR_RecordTraceEntries() attempts to record the in-memory
+** buffer to a file, subject to the setting of the environment
+** variable NSPR_TRACE_LOG. It is possible because of system
+** load, the thread priority of the recording thread, number of
+** active trace records being written over time, and other
+** variables that some trace records can be lost. ... In other
+** words: don't bet the farm on getting everything.
+**
+** INPUTS: none
+**
+** OUTPUTS: none
+**
+** RETURNS: PR_STATUS
+** PR_SUCCESS no errors were found.
+** PR_FAILURE errors were found.
+**
+** RESTRICTIONS:
+** Only one thread can call PR_RecordTraceEntries() within a
+** process.
+**
+** On error, PR_RecordTraceEntries() may return prematurely.
+**
+*/
+#define PR_RECORD_TRACE_ENTRIES()\
+ PR_RecordTraceEntries()
+
+PR_EXTERN(void)
+ PR_RecordTraceEntries(
+ void
+);
+
+/* -----------------------------------------------------------------------
+** FUNCTION: PR_GetTraceEntries() -- Retreive trace entries from
+** the Trace Facility
+**
+** DESCRIPTION:
+** PR_GetTraceEntries() retreives trace entries from the Trace
+** Facility. Up to count trace entries are copied from the Trace
+** Facility into buffer. Only those trace entries that have not
+** been copied via a previous call to PR_GetTraceEntries() are
+** copied. The actual number copied is placed in the PRInt32
+** variable pointed to by found.
+**
+** If more than count trace entries have entered the Trace
+** Facility since the last call to PR_GetTraceEntries()
+** a lost data condition is returned. In this case, the most
+** recent count trace entries are copied into buffer and found is
+** set to count.
+**
+** INPUTS:
+** count. The number of trace entries to be copied into buffer.
+**
+**
+** OUTPUTS:
+** buffer. An array of PRTraceEntries. The buffer is supplied
+** by the caller.
+**
+** found: 32bit signed integer. The number of PRTraceEntries
+** actually copied. found is always less than or equal to count.
+**
+** RETURNS:
+** zero when there is no lost data.
+** non-zero when some PRTraceEntries have been lost.
+**
+** RESTRICTIONS:
+** This is a real performance pig. The copy out operation is bad
+** enough, but depending on then frequency of calls to the
+** function, serious performance impact to the operating
+** application may be realized. ... YMMV.
+**
+*/
+#define PR_GET_TRACE_ENTRIES(buffer,count,found)\
+ PR_GetTraceEntries((buffer),(count),(found))
+
+
+PR_EXTERN(PRIntn)
+ PR_GetTraceEntries(
+ PRTraceEntry *buffer, /* where to write output */
+ PRInt32 count, /* number to get */
+ PRInt32 *found /* number you got */
+);
+
+#else /* !(defined (DEBUG) || defined (FORCE_NSPR_TRACE)) */
+/*
+** Define the Trace Facility macros as No-Ops for when the trace
+** facility is to be compiled-out of the application.
+**
+*/
+#define PR_DEFINE_TRACE(name) PRTraceHandle name
+#define PR_INIT_TRACE_HANDLE(handle,value)
+#define PR_CREATE_TRACE(handle,qName,rName,description)
+#define PR_DESTROY_TRACE(handle)
+#define PR_TRACE(handle,ud0,ud1,ud2,ud3,ud4,ud5,ud6,ud7)
+#define PR_SET_TRACE_OPTION(command,value)
+#define PR_GET_TRACE_OPTION(command,value)
+#define PR_GET_TRACE_HANDLE_FROM_NAME(handle,qName,rName)
+#define PR_GET_TRACE_NAME_FROM_HANDLE(handle,qName,rName,description)
+#define PR_FIND_NEXT_TRACE_QNAME(next,handle)
+#define PR_FIND_NEXT_TRACE_RNAME(next,rhandle,qhandle)
+#define PR_GET_TRACE_ENTRIES(buffer,count,found)
+#define PR_RECORD_TRACE_ENTRIES()
+
+#endif /* !(defined (DEBUG) || defined (FORCE_NSPR_TRACE)) */
+
+PR_END_EXTERN_C
+
+#endif /* prtrace_h___ */
+
diff --git a/pr/include/prtypes.h b/pr/include/prtypes.h
new file mode 100644
index 00000000..8e10743c
--- /dev/null
+++ b/pr/include/prtypes.h
@@ -0,0 +1,391 @@
+/* -*- 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: prtypes.h
+** Description: Definitions of NSPR's basic types
+**
+** Prototypes and macros used to make up for deficiencies in ANSI environments
+** that we have found.
+**
+** Since we do not wrap <stdlib.h> and all the other standard headers, authors
+** of portable code will not know in general that they need these definitions.
+** Instead of requiring these authors to find the dependent uses in their code
+** and take the following steps only in those C files, we take steps once here
+** for all C files.
+**/
+
+#ifndef prtypes_h___
+#define prtypes_h___
+
+#include "prcpucfg.h"
+
+#include <stddef.h>
+
+/***********************************************************************
+** MACROS: PR_EXTERN
+** PR_IMPLEMENT
+** DESCRIPTION:
+** These are only for externally visible routines and globals. For
+** internal routines, just use "extern" for type checking and that
+** will not export internal cross-file or forward-declared symbols.
+** Define a macro for declaring procedures return types. We use this to
+** deal with windoze specific type hackery for DLL definitions. Use
+** PR_EXTERN when the prototype for the method is declared. Use
+** PR_IMPLEMENT for the implementation of the method.
+**
+** Example:
+** in dowhim.h
+** PR_EXTERN( void ) DoWhatIMean( void );
+** in dowhim.c
+** PR_IMPLEMENT( void ) DoWhatIMean( void ) { return; }
+**
+**
+***********************************************************************/
+#if defined(WIN32)
+#define PR_EXTERN(__type) extern _declspec(dllexport) __type
+#define PR_IMPLEMENT(__type) _declspec(dllexport) __type
+#define PR_EXTERN_DATA(__type) extern _declspec(dllexport) __type
+#define PR_IMPLEMENT_DATA(__type) _declspec(dllexport) __type
+
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x
+
+#elif defined(WIN16)
+
+#define PR_CALLBACK_DECL __cdecl
+
+#if defined(_WINDLL)
+#define PR_EXTERN(__type) extern __type _cdecl _export _loadds
+#define PR_IMPLEMENT(__type) __type _cdecl _export _loadds
+#define PR_EXTERN_DATA(__type) extern __type _export
+#define PR_IMPLEMENT_DATA(__type) __type _export
+
+#define PR_CALLBACK __cdecl __loadds
+#define PR_STATIC_CALLBACK(__x) static __x PR_CALLBACK
+
+#else /* this must be .EXE */
+#define PR_EXTERN(__type) extern __type _cdecl _export
+#define PR_IMPLEMENT(__type) __type _cdecl _export
+#define PR_EXTERN_DATA(__type) extern __type _export
+#define PR_IMPLEMENT_DATA(__type) __type _export
+
+#define PR_CALLBACK __cdecl __loadds
+#define PR_STATIC_CALLBACK(__x) __x PR_CALLBACK
+#endif /* _WINDLL */
+
+#elif defined(XP_MAC)
+#define PR_EXTERN(__type) extern __declspec(export) __type
+#define PR_IMPLEMENT(__type) __declspec(export) __type
+#define PR_EXTERN_DATA(__type) extern __declspec(export) __type
+#define PR_IMPLEMENT_DATA(__type) __declspec(export) __type
+
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x
+
+#elif defined(XP_OS2)
+#define PR_EXTERN(__type) extern __type
+#define PR_IMPLEMENT(__type) __type
+#define PR_EXTERN_DATA(__type) extern __type
+#define PR_IMPLEMENT_DATA(__type) __type
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) __x _Optlink
+
+#else /* Unix */
+#define PR_EXTERN(__type) extern __type
+#define PR_IMPLEMENT(__type) __type
+#define PR_EXTERN_DATA(__type) extern __type
+#define PR_IMPLEMENT_DATA(__type) __type
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x
+
+#endif
+
+/***********************************************************************
+** MACROS: PR_BEGIN_MACRO
+** PR_END_MACRO
+** DESCRIPTION:
+** Macro body brackets so that macros with compound statement definitions
+** behave syntactically more like functions when called.
+***********************************************************************/
+#define PR_BEGIN_MACRO do {
+#define PR_END_MACRO } while (0)
+
+/***********************************************************************
+** MACROS: PR_BEGIN_EXTERN_C
+** PR_END_EXTERN_C
+** DESCRIPTION:
+** Macro shorthands for conditional C++ extern block delimiters.
+***********************************************************************/
+#ifdef __cplusplus
+#define PR_BEGIN_EXTERN_C extern "C" {
+#define PR_END_EXTERN_C }
+#else
+#define PR_BEGIN_EXTERN_C
+#define PR_END_EXTERN_C
+#endif
+
+/***********************************************************************
+** MACROS: PR_BIT
+** PR_BITMASK
+** DESCRIPTION:
+** Bit masking macros. XXX n must be <= 31 to be portable
+***********************************************************************/
+#define PR_BIT(n) ((PRUint32)1 << (n))
+#define PR_BITMASK(n) (PR_BIT(n) - 1)
+
+/***********************************************************************
+** MACROS: PR_ROUNDUP
+** PR_MIN
+** PR_MAX
+** DESCRIPTION:
+** Commonly used macros for operations on compatible types.
+***********************************************************************/
+#define PR_ROUNDUP(x,y) ((((x)+((y)-1))/(y))*(y))
+#define PR_MIN(x,y) ((x)<(y)?(x):(y))
+#define PR_MAX(x,y) ((x)>(y)?(x):(y))
+
+PR_BEGIN_EXTERN_C
+
+/************************************************************************
+** TYPES: PRUint8
+** PRInt8
+** DESCRIPTION:
+** The int8 types are known to be 8 bits each. There is no type that
+** is equivalent to a plain "char".
+************************************************************************/
+#if PR_BYTES_PER_BYTE == 1
+typedef unsigned char PRUint8;
+/*
+** Some cfront-based C++ compilers do not like 'signed char' and
+** issue the warning message:
+** warning: "signed" not implemented (ignored)
+** For these compilers, we have to define PRInt8 as plain 'char'.
+** Make sure that plain 'char' is indeed signed under these compilers.
+*/
+#if (defined(HPUX) && defined(__cplusplus) \
+ && !defined(__GNUC__) && __cplusplus < 199707L) \
+ || (defined(SCO) && defined(__cplusplus) \
+ && !defined(__GNUC__) && __cplusplus == 1L)
+typedef char PRInt8;
+#else
+typedef signed char PRInt8;
+#endif
+#else
+#error No suitable type for PRInt8/PRUint8
+#endif
+
+/************************************************************************
+** TYPES: PRUint16
+** PRInt16
+** DESCRIPTION:
+** The int16 types are known to be 16 bits each.
+************************************************************************/
+#if PR_BYTES_PER_SHORT == 2
+typedef unsigned short PRUint16;
+typedef short PRInt16;
+#else
+#error No suitable type for PRInt16/PRUint16
+#endif
+
+/************************************************************************
+** TYPES: PRUint32
+** PRInt32
+** DESCRIPTION:
+** The int32 types are known to be 32 bits each.
+************************************************************************/
+#if PR_BYTES_PER_INT == 4
+typedef unsigned int PRUint32;
+typedef int PRInt32;
+#define PR_INT32(x) x
+#define PR_UINT32(x) x ## U
+#elif PR_BYTES_PER_LONG == 4
+typedef unsigned long PRUint32;
+typedef long PRInt32;
+#define PR_INT32(x) x ## L
+#define PR_UINT32(x) x ## UL
+#else
+#error No suitable type for PRInt32/PRUint32
+#endif
+
+/************************************************************************
+** TYPES: PRUint64
+** PRInt64
+** DESCRIPTION:
+** The int64 types are known to be 64 bits each. Care must be used when
+** declaring variables of type PRUint64 or PRInt64. Different hardware
+** architectures and even different compilers have varying support for
+** 64 bit values. The only guaranteed portability requires the use of
+** the LL_ macros (see prlong.h).
+************************************************************************/
+#ifdef HAVE_LONG_LONG
+#if PR_BYTES_PER_LONG == 8
+typedef long PRInt64;
+typedef unsigned long PRUint64;
+#elif defined(WIN16)
+typedef __int64 PRInt64;
+typedef unsigned __int64 PRUint64;
+#elif defined(WIN32)
+typedef __int64 PRInt64;
+typedef unsigned __int64 PRUint64;
+#else
+typedef long long PRInt64;
+typedef unsigned long long PRUint64;
+#endif /* PR_BYTES_PER_LONG == 8 */
+#else /* !HAVE_LONG_LONG */
+typedef struct {
+#ifdef IS_LITTLE_ENDIAN
+ PRUint32 lo, hi;
+#else
+ PRUint32 hi, lo;
+#endif
+} PRInt64;
+typedef PRInt64 PRUint64;
+#endif /* !HAVE_LONG_LONG */
+
+/************************************************************************
+** TYPES: PRUintn
+** PRIntn
+** DESCRIPTION:
+** The PRIntn types are most appropriate for automatic variables. They are
+** guaranteed to be at least 16 bits, though various architectures may
+** define them to be wider (e.g., 32 or even 64 bits). These types are
+** never valid for fields of a structure.
+************************************************************************/
+#if PR_BYTES_PER_INT >= 2
+typedef int PRIntn;
+typedef unsigned int PRUintn;
+#else
+#error 'sizeof(int)' not sufficient for platform use
+#endif
+
+/************************************************************************
+** TYPES: PRFloat64
+** DESCRIPTION:
+** NSPR's floating point type is always 64 bits.
+************************************************************************/
+typedef double PRFloat64;
+
+/************************************************************************
+** TYPES: PRSize
+** DESCRIPTION:
+** A type for representing the size of objects.
+************************************************************************/
+typedef size_t PRSize;
+
+/************************************************************************
+** TYPES: PRPtrDiff
+** DESCRIPTION:
+** A type for pointer difference. Variables of this type are suitable
+** for storing a pointer or pointer sutraction.
+************************************************************************/
+typedef ptrdiff_t PRPtrdiff;
+
+/************************************************************************
+** TYPES: PRUptrdiff
+** DESCRIPTION:
+** A type for pointer difference. Variables of this type are suitable
+** for storing a pointer or pointer sutraction.
+************************************************************************/
+typedef unsigned long PRUptrdiff;
+
+/************************************************************************
+** TYPES: PRBool
+** DESCRIPTION:
+** Use PRBool for variables and parameter types. Use PR_FALSE and PR_TRUE
+** for clarity of target type in assignments and actual arguments. Use
+** 'if (bool)', 'while (!bool)', '(bool) ? x : y' etc., to test booleans
+** juast as you would C int-valued conditions.
+************************************************************************/
+typedef PRIntn PRBool;
+#define PR_TRUE (PRIntn)1
+#define PR_FALSE (PRIntn)0
+
+/************************************************************************
+** TYPES: PRPackedBool
+** DESCRIPTION:
+** Use PRPackedBOol within structs where bitfields are not desireable
+** but minimum and consistant overhead matters.
+************************************************************************/
+typedef PRUint8 PRPackedBool;
+
+/*
+** Status code used by some routines that have a single point of failure or
+** special status return.
+*/
+typedef enum { PR_FAILURE = -1, PR_SUCCESS = 0 } PRStatus;
+
+#if defined(NO_NSPR_10_SUPPORT)
+#else
+/********* ???????????????? FIX ME ??????????????????????????? *****/
+/********************** Some old definitions until pr=>ds transition is done ***/
+/********************** Also, we are still using NSPR 1.0. GC ******************/
+/*
+** Fundamental NSPR macros, used nearly everywhere.
+*/
+
+#define PR_PUBLIC_API PR_IMPLEMENT
+
+/*
+** Macro body brackets so that macros with compound statement definitions
+** behave syntactically more like functions when called.
+*/
+#define NSPR_BEGIN_MACRO do {
+#define NSPR_END_MACRO } while (0)
+
+/*
+** Macro shorthands for conditional C++ extern block delimiters.
+*/
+#ifdef NSPR_BEGIN_EXTERN_C
+#undef NSPR_BEGIN_EXTERN_C
+#endif
+#ifdef NSPR_END_EXTERN_C
+#undef NSPR_END_EXTERN_C
+#endif
+
+#ifdef __cplusplus
+#define NSPR_BEGIN_EXTERN_C extern "C" {
+#define NSPR_END_EXTERN_C }
+#else
+#define NSPR_BEGIN_EXTERN_C
+#define NSPR_END_EXTERN_C
+#endif
+
+/*
+** A PRWord is an integer that is the same size as a void*
+*/
+typedef long PRWord;
+typedef unsigned long PRUword;
+
+/********* ????????????? End Fix me ?????????????????????????????? *****/
+#endif /* NO_NSPR_10_SUPPORT */
+
+#ifdef XP_MAC
+#include "protypes.h"
+#else
+#include "obsolete/protypes.h"
+#endif
+
+PR_END_EXTERN_C
+
+#endif /* prtypes_h___ */
+
diff --git a/pr/include/prvrsion.h b/pr/include/prvrsion.h
new file mode 100755
index 00000000..a8a0bf3c
--- /dev/null
+++ b/pr/include/prvrsion.h
@@ -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.
+ */
+
+
+/* author: jstewart */
+
+#if defined(_PRVERSION_H)
+#else
+#define _PRVERSION_H
+
+#include "prtypes.h"
+
+PR_BEGIN_EXTERN_C
+
+/* All components participating in the PR version protocol must expose
+ * a structure and a function. The structure is defined below and named
+ * according to the naming conventions outlined further below. The function
+ * is called libVersionPoint and returns a pointer to this structure.
+ */
+
+/* on NT, always pack the structure the same. */
+#ifdef _WIN32
+#pragma pack(push, 8)
+#endif
+
+typedef struct {
+ /*
+ * The first field defines which version of this structure is in use.
+ * At this time, only version 2 is specified. If this value is not
+ * 2, you must read no further into the structure.
+ */
+ PRInt32 version;
+
+ /* for Version 2, this is the body format. */
+ PRInt64 buildTime; /* 64 bits - usecs since midnight, 1/1/1970 */
+ char * buildTimeString;/* a human readable version of the time */
+
+ PRUint8 vMajor; /* Major version of this component */
+ PRUint8 vMinor; /* Minor version of this component */
+ PRUint8 vPatch; /* Patch level of this component */
+
+ PRBool beta; /* true if this is a beta component */
+ PRBool debug; /* true if this is a debug component */
+ PRBool special; /* true if this component is a special build */
+
+ char * filename; /* The original filename */
+ char * description; /* description of this component */
+ char * security; /* level of security in this component */
+ char * copyright; /* The copyright for this file */
+ char * comment; /* free form field for misc usage */
+ char * specialString; /* the special variant for this build */
+} PRVersionDescription;
+
+/* on NT, restore the previous packing */
+#ifdef _WIN32
+#pragma pack(pop)
+#endif
+
+/*
+ * All components must define an entrypoint named libVersionPoint which
+ * is of type versionEntryPointType.
+ */
+PR_EXTERN(const PRVersionDescription *) libVersionPoint(void);
+
+typedef const PRVersionDescription *(*versionEntryPointType)(void);
+
+/*
+ * Where you declare your libVersionPoint, do it like this:
+ * PR_IMPLEMENT(const PRVersionDescription *) libVersionPoint(void) {
+ * fill it in...
+ * }
+ */
+
+/*
+ * NAMING CONVENTION FOR struct
+ *
+ * all components should also expose a static PRVersionDescription
+ * The name of the struct should be calculated as follows:
+ * Take the value of filename. (If filename is not specified, calculate
+ * a short, unique string.) Convert all non-alphanumeric characters
+ * to '_'. To this, prepend "PRVersionDescription_". Thus for libfoo.so,
+ * the symbol name is "PRVersionDescription_libfoo_so".
+ * so the file should have
+ * PRVersionDescription PRVersionDescription_libfoo_so { fill it in };
+ * on NT, this file should be declspec export.
+ */
+
+PR_END_EXTERN_C
+
+#endif /* defined(_PRVERSION_H) */
+
+/* prvrsion.h */
+
diff --git a/pr/include/prwin16.h b/pr/include/prwin16.h
new file mode 100644
index 00000000..2420b235
--- /dev/null
+++ b/pr/include/prwin16.h
@@ -0,0 +1,177 @@
+/* -*- 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 prwin16_h___
+#define prwin16_h___
+
+/*
+** Condition use of this header on platform.
+*/
+#if (defined(XP_PC) && !defined(_WIN32) && !defined(XP_OS2) && defined(MOZILLA_CLIENT)) || defined(WIN16)
+#include <stdio.h>
+
+PR_BEGIN_EXTERN_C
+/*
+** Win16 stdio special case.
+** To get stdio to work for Win16, all calls to printf() and related
+** things must be called from the environment of the .EXE; calls to
+** printf() from the .DLL send output to the bit-bucket.
+**
+** To make sure that PR_fprintf(), and related functions, work correctly,
+** the actual stream I/O to stdout, stderr, stdin must be done in the
+** .EXE. To do this, a hack is placed in _MD_Write() such that the
+** fd for stdio handles results in a call to the .EXE.
+**
+** file w16stdio.c contains the functions that get called from NSPR
+** to do the actual I/O. w16stdio.o must be statically linked with
+** any application needing stdio for Win16.
+**
+** The address of these functions must be made available to the .DLL
+** so he can call back to the .EXE. To do this, function
+** PR_MD_RegisterW16StdioCallbacks() is called from the .EXE.
+** The arguments are the functions defined in w16stdio.c
+** At runtime, MD_Write() calls the registered functions, if any
+** were registered.
+**
+** prinit.h contains a macro PR_STDIO_INIT() that calls the registration
+** function for Win16; For other platforms, the macro is a No-Op.
+**
+** Note that stdio is not operational at all on Win16 GUI applications.
+** This special case exists to provide stdio capability from the NSPR
+** .DLL for command line applications only. NSPR's test cases are
+** almost exclusively command line applications.
+**
+** See also: w16io.c, w16stdio.c
+*/
+typedef PRInt32 (PR_CALLBACK *PRStdinRead)( void *buf, PRInt32 amount);
+typedef PRInt32 (PR_CALLBACK *PRStdoutWrite)( void *buf, PRInt32 amount);
+typedef PRInt32 (PR_CALLBACK *PRStderrWrite)( void *buf, PRInt32 amount);
+
+PR_EXTERN(PRStatus)
+PR_MD_RegisterW16StdioCallbacks(
+ PRStdinRead inReadf, /* i: function pointer for stdin read */
+ PRStdoutWrite outWritef, /* i: function pointer for stdout write */
+ PRStderrWrite errWritef /* i: function pointer for stderr write */
+ );
+
+PR_EXTERN(PRInt32)
+_PL_W16StdioWrite( void *buf, PRInt32 amount );
+
+PR_EXTERN(PRInt32)
+_PL_W16StdioRead( void *buf, PRInt32 amount );
+
+#define PR_STDIO_INIT() PR_MD_RegisterW16StdioCallbacks( \
+ _PL_W16StdioRead, _PL_W16StdioWrite, _PL_W16StdioWrite ); \
+ PR_INIT_CALLBACKS();
+
+/*
+** Win16 hackery.
+**
+*/
+struct PRMethodCallbackStr {
+ int (PR_CALLBACK *auxOutput)(const char *outputString);
+ size_t (PR_CALLBACK *strftime)(char *s, size_t len, const char *fmt, const struct tm *p);
+ void * (PR_CALLBACK *malloc)( size_t size );
+ void * (PR_CALLBACK *calloc)(size_t n, size_t size );
+ void * (PR_CALLBACK *realloc)( void* old_blk, size_t size );
+ void (PR_CALLBACK *free)( void *ptr );
+ void * (PR_CALLBACK *getenv)( const char *name);
+ int (PR_CALLBACK *putenv)( const char *assoc);
+// void * (PR_CALLBACK *perror)( const char *prefix );
+};
+
+PR_EXTERN(void) PR_MDRegisterCallbacks(struct PRMethodCallbackStr *);
+
+int PR_CALLBACK _PL_W16CallBackPuts( const char *outputString );
+size_t PR_CALLBACK _PL_W16CallBackStrftime(
+ char *s,
+ size_t len,
+ const char *fmt,
+ const struct tm *p );
+void * PR_CALLBACK _PL_W16CallBackMalloc( size_t size );
+void * PR_CALLBACK _PL_W16CallBackCalloc( size_t n, size_t size );
+void * PR_CALLBACK _PL_W16CallBackRealloc(
+ void *old_blk,
+ size_t size );
+void PR_CALLBACK _PL_W16CallBackFree( void *ptr );
+void * PR_CALLBACK _PL_W16CallBackGetenv( const char *name );
+int PR_CALLBACK _PL_W16CallBackPutenv( const char *assoc );
+
+/*
+** Hackery!
+**
+** These functions are provided as static link points.
+** This is to satisfy the quick port of Gromit to NSPR 2.0
+** ... Don't do this! ... alas, It may never go away.
+**
+*/
+PR_EXTERN(int) PR_MD_printf(const char *, ...);
+PR_EXTERN(void) PR_MD_exit(int);
+PR_EXTERN(size_t) PR_MD_strftime(char *, size_t, const char *, const struct tm *);
+PR_EXTERN(int) PR_MD_sscanf(const char *, const char *, ...);
+PR_EXTERN(void*) PR_MD_malloc( size_t size );
+PR_EXTERN(void*) PR_MD_calloc( size_t n, size_t size );
+PR_EXTERN(void*) PR_MD_realloc( void* old_blk, size_t size );
+PR_EXTERN(void) PR_MD_free( void *ptr );
+PR_EXTERN(char*) PR_MD_getenv( const char *name );
+PR_EXTERN(int) PR_MD_putenv( const char *assoc );
+PR_EXTERN(int) PR_MD_fprintf(FILE *fPtr, const char *fmt, ...);
+
+#define PR_INIT_CALLBACKS() \
+ { \
+ static struct PRMethodCallbackStr cbf = { \
+ _PL_W16CallBackPuts, \
+ _PL_W16CallBackStrftime, \
+ _PL_W16CallBackMalloc, \
+ _PL_W16CallBackCalloc, \
+ _PL_W16CallBackRealloc, \
+ _PL_W16CallBackFree, \
+ _PL_W16CallBackGetenv, \
+ _PL_W16CallBackPutenv, \
+ }; \
+ PR_MDRegisterCallbacks( &cbf ); \
+ }
+
+
+/*
+** Get the exception context for Win16 MFC applications threads
+*/
+PR_EXTERN(void *) PR_W16GetExceptionContext(void);
+/*
+** Set the exception context for Win16 MFC applications threads
+*/
+PR_EXTERN(void) PR_W16SetExceptionContext(void *context);
+
+PR_END_EXTERN_C
+#else
+/*
+** For platforms other than Win16, define
+** PR_STDIO_INIT() as a No-Op.
+*/
+#define PR_STDIO_INIT()
+#endif /* WIN16 || MOZILLA_CLIENT */
+
+#endif /* prwin16_h___ */
+
+
+
+
+
+
+
+
diff --git a/pr/src/Makefile b/pr/src/Makefile
new file mode 100644
index 00000000..f718e85d
--- /dev/null
+++ b/pr/src/Makefile
@@ -0,0 +1,367 @@
+#
+# 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
+
+DIRS = io linking malloc md memory misc threads
+
+ifeq ($(USE_PTHREADS), 1)
+ DIRS += pthreads
+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
+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 # 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 = -lpthreads -lsvld -lC_r -lC -lc_r -lm /usr/lib/libc.a
+else
+OS_LIBS = -lpthreads -ldl -lC_r -lC -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)
+ifeq ($(USE_PTHREADS), 1)
+OS_LIBS = -lpthread -lrt
+endif
+ifneq ($(OS_RELEASE),V2.0)
+OS_LIBS += -lc_r
+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 ($(USE_PTHREADS), 1)
+ifeq (,$(filter-out B.10.10 B.10.20,$(OS_RELEASE)))
+OS_LIBS = -ldce
+else
+OS_LIBS = -lpthread
+endif
+endif
+ifeq ($(PTHREADS_USER), 1)
+OS_LIBS = -lpthread
+endif
+ifeq ($(basename $(OS_RELEASE)),A.09)
+OS_LIBS += -ldld -L/lib/pa1.1 -lm
+else
+OS_LIBS += -ldld -lm -lc
+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 = \
+ $(OBJDIR)/prvrsion.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prfdcach.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prmwait.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prmapopt.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/priometh.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prlayer.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prlog.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prmmap.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prpolevt.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prprf.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prscanf.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prstdio.$(OBJ_SUFFIX) \
+ threads/$(OBJDIR)/prcmon.$(OBJ_SUFFIX) \
+ threads/$(OBJDIR)/prtpd.$(OBJ_SUFFIX) \
+ linking/$(OBJDIR)/prlink.$(OBJ_SUFFIX) \
+ malloc/$(OBJDIR)/prmalloc.$(OBJ_SUFFIX) \
+ malloc/$(OBJDIR)/prmem.$(OBJ_SUFFIX) \
+ md/$(OBJDIR)/prosdep.$(OBJ_SUFFIX) \
+ memory/$(OBJDIR)/prseg.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/pralarm.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/pratom.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prcountr.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prdtoa.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prenv.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prerror.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prinit.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prinrval.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prlog2.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prlong.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prnetdb.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prolock.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prsystem.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prthinfo.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prtrace.$(OBJ_SUFFIX) \
+ misc/$(OBJDIR)/prtime.$(OBJ_SUFFIX)
+
+ifdef USE_PTHREADS
+OBJS += \
+ pthreads/$(OBJDIR)/ptsynch.$(OBJ_SUFFIX) \
+ pthreads/$(OBJDIR)/ptio.$(OBJ_SUFFIX) \
+ pthreads/$(OBJDIR)/ptthread.$(OBJ_SUFFIX) \
+ pthreads/$(OBJDIR)/ptmisc.$(OBJ_SUFFIX)
+else
+OBJS += \
+ io/$(OBJDIR)/prdir.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prfile.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prio.$(OBJ_SUFFIX) \
+ io/$(OBJDIR)/prsocket.$(OBJ_SUFFIX) \
+ threads/$(OBJDIR)/prcthr.$(OBJ_SUFFIX) \
+ threads/$(OBJDIR)/prdump.$(OBJ_SUFFIX) \
+ threads/$(OBJDIR)/prmon.$(OBJ_SUFFIX) \
+ threads/$(OBJDIR)/prsem.$(OBJ_SUFFIX) \
+ threads/combined/$(OBJDIR)/prucpu.$(OBJ_SUFFIX) \
+ threads/combined/$(OBJDIR)/prucv.$(OBJ_SUFFIX) \
+ threads/combined/$(OBJDIR)/prulock.$(OBJ_SUFFIX) \
+ threads/combined/$(OBJDIR)/prustack.$(OBJ_SUFFIX) \
+ threads/combined/$(OBJDIR)/pruthr.$(OBJ_SUFFIX)
+endif
+
+
+ifeq ($(USE_IPV6), 1)
+OBJS += io/$(OBJDIR)/pripv6.$(OBJ_SUFFIX)
+endif
+
+ifeq ($(OS_ARCH), WINNT)
+ifneq (,$(filter-out WIN16 OS2,$(OS_TARGET)))
+DLLBASE=/BASE:0x30000000
+RES=$(OBJDIR)/nspr.res
+RESNAME=nspr.rc
+ifdef MOZ_DEBUG
+ifdef GLOWCODE
+EXTRA_LIBS += $(GLOWDIR)/glowcode.lib
+endif
+endif
+endif
+
+ifeq ($(OS_TARGET), WIN16)
+OBJS += md/windows/$(OBJDIR)/w16null.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/w16proc.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/w16thred.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/w16fmem.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/w16sock.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/w16mem.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/w16io.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/w16gc.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/w16error.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/w16callb.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/ntinrval.$(OBJ_SUFFIX)
+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.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/w95sock.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/w95thred.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/w95cv.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/ntgc.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/ntmisc.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/ntinrval.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/ntsem.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/win32_errors.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/w32poll.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/w95dllmain.$(OBJ_SUFFIX)
+else
+ifeq ($(OS_TARGET), OS2)
+OBJS += md/os2/$(OBJDIR)/os2io.$(OBJ_SUFFIX) \
+ md/os2/$(OBJDIR)/os2sock.$(OBJ_SUFFIX) \
+ md/os2/$(OBJDIR)/os2thred.$(OBJ_SUFFIX) \
+ md/os2/$(OBJDIR)/os2cv.$(OBJ_SUFFIX) \
+ md/os2/$(OBJDIR)/os2gc.$(OBJ_SUFFIX) \
+ md/os2/$(OBJDIR)/os2misc.$(OBJ_SUFFIX) \
+ md/os2/$(OBJDIR)/os2inrval.$(OBJ_SUFFIX) \
+ md/os2/$(OBJDIR)/os2sem.$(OBJ_SUFFIX) \
+ md/os2/$(OBJDIR)/os2_errors.$(OBJ_SUFFIX) \
+ md/os2/$(OBJDIR)/os2poll.$(OBJ_SUFFIX)
+else
+OBJS += md/windows/$(OBJDIR)/ntdllmn.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/ntio.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/ntgc.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/ntthread.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/ntmisc.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/ntinrval.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/ntsem.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/win32_errors.$(OBJ_SUFFIX) \
+ md/windows/$(OBJDIR)/w32poll.$(OBJ_SUFFIX)
+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
+
+#
+# Version information generation (begin)
+#
+ECHO = echo
+INCLUDES = -I$(DIST)/include
+TINC = $(OBJDIR)/_pr_bld.h
+PROD = libnspr$(MOD_VERSION).$(DLL_SUFFIX)
+NOW = $(MOD_DEPTH)/config/$(OBJDIR)/now
+SH_DATE = $(shell date)
+SH_NOW = $(shell $(NOW))
+
+ifeq ($(OS_ARCH), WINNT)
+ SUF = i64
+else
+ SUF = LL
+endif
+
+$(TINC):
+ @$(MAKE_OBJDIR)
+ @$(ECHO) '#define _BUILD_STRING "$(SH_DATE)"' > $(TINC)
+ @if test ! -z "$(SH_NOW)"; then \
+ $(ECHO) '#define _BUILD_TIME $(SH_NOW)$(SUF)' >> $(TINC); \
+ fi
+ @$(ECHO) '#define _PRODUCTION "$(PROD)"' >> $(TINC)
+
+
+$(OBJDIR)/prvrsion.$(OBJ_SUFFIX): $(TINC)
+ifeq ($(OS_ARCH), WINNT)
+ $(CC) -Fo$@ -c $(CFLAGS) -I$(OBJDIR) prvrsion.c
+else
+ $(CC) -o $@ -c $(CFLAGS) -I$(OBJDIR) prvrsion.c
+endif
+#
+# Version information generation (end)
+#
+
+
+#
+# The Client build wants the shared libraries in $(DIST)/bin,
+# so we also install them there.
+#
+
+export:: $(TARGETS)
+ $(INSTALL) -m 444 $(TARGETS) $(DIST)/lib
+ifdef SHARED_LIBRARY
+ $(INSTALL) -m 444 $(SHARED_LIBRARY) $(DIST)/bin
+endif
+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..a3574793
--- /dev/null
+++ b/pr/src/io/Makefile
@@ -0,0 +1,66 @@
+#
+# 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 = \
+ prfdcach.c \
+ prmwait.c \
+ priometh.c \
+ prmapopt.c \
+ prlayer.c \
+ prlog.c \
+ prmmap.c \
+ prpolevt.c \
+ prprf.c \
+ prscanf.c \
+ prstdio.c \
+ $(NULL)
+
+ifndef USE_PTHREADS
+ CSRCS += \
+ prdir.c \
+ prfile.c \
+ prio.c \
+ prsocket.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/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/prfdcach.c b/pr/src/io/prfdcach.c
new file mode 100644
index 00000000..9c7c8ea7
--- /dev/null
+++ b/pr/src/io/prfdcach.c
@@ -0,0 +1,287 @@
+/* -*- 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 "pratom.h"
+
+#include <string.h>
+
+/*****************************************************************************/
+/*****************************************************************************/
+/************************** File descriptor caching **************************/
+/*****************************************************************************/
+/*****************************************************************************/
+
+/*
+** This code is built into debuggable versions of NSPR to assist in
+** finding misused file descriptors. Since file descritors (PRFileDesc)
+** are identified by a pointer to their structure, they can be the
+** target of dangling references. Furthermore, NSPR caches and tries
+** to aggressively reuse file descriptors, leading to more ambiguity.
+** The following code will allow a debugging client to set environment
+** variables and control the number of file descriptors that will be
+** preserved before they are recycled. The environment variables are
+** NSPR_FD_CACHE_SIZE_LOW and NSPR_FD_CACHE_SIZE_HIGH. The former sets
+** the number of descriptors NSPR will allocate before beginning to
+** recycle. The latter is the maximum number permitted in the cache
+** (exclusive of those in use) at a time.
+*/
+typedef struct _PR_Fd_Cache
+{
+ PRLock *ml;
+ PRIntn count;
+ PRStack *stack;
+ PRFileDesc *head, *tail;
+ PRIntn limit_low, limit_high;
+} _PR_Fd_Cache;
+
+static _PR_Fd_Cache _pr_fd_cache;
+static PRFileDesc **stack2fd = &(((PRFileDesc*)NULL)->higher);
+
+
+/*
+** Get a FileDescriptor from the cache if one exists. If not allocate
+** a new one from the heap.
+*/
+PRFileDesc *_PR_Getfd()
+{
+ PRFileDesc *fd;
+ /*
+ ** $$$
+ ** This may look a little wasteful. We'll see. Right now I want to
+ ** be able to toggle between caching and not at runtime to measure
+ ** the differences. If it isn't too annoying, I'll leave it in.
+ ** $$$$
+ **
+ ** The test is against _pr_fd_cache.limit_high. If that's zero,
+ ** we're not doing the extended cache but going for performance.
+ */
+ if (0 == _pr_fd_cache.limit_high)
+ {
+ PRStackElem *pop;
+ PR_ASSERT(NULL != _pr_fd_cache.stack);
+ pop = PR_StackPop(_pr_fd_cache.stack);
+ if (NULL == pop) goto allocate;
+ fd = (PRFileDesc*)((PRPtrdiff)pop - (PRPtrdiff)stack2fd);
+ }
+ else
+ {
+ do
+ {
+ if (NULL == _pr_fd_cache.head) goto allocate; /* nothing there */
+ if (_pr_fd_cache.count < _pr_fd_cache.limit_low) goto allocate;
+
+ /* we "should" be able to extract an fd from the cache */
+ PR_Lock(_pr_fd_cache.ml); /* need the lock to do this safely */
+ fd = _pr_fd_cache.head; /* protected extraction */
+ if (NULL == fd) /* unexpected, but not fatal */
+ {
+ PR_ASSERT(0 == _pr_fd_cache.count);
+ PR_ASSERT(NULL == _pr_fd_cache.tail);
+ }
+ else
+ {
+ _pr_fd_cache.count -= 1;
+ _pr_fd_cache.head = fd->higher;
+ if (NULL == _pr_fd_cache.head)
+ {
+ PR_ASSERT(0 == _pr_fd_cache.count);
+ _pr_fd_cache.tail = NULL;
+ }
+ PR_ASSERT(_PR_FILEDESC_FREED == fd->secret->state);
+ }
+ PR_Unlock(_pr_fd_cache.ml);
+
+ } while (NULL == fd); /* then go around and allocate a new one */
+ }
+
+finished:
+ fd->dtor = NULL;
+ fd->lower = fd->higher = NULL;
+ fd->identity = PR_NSPR_IO_LAYER;
+ memset(fd->secret, 0, sizeof(PRFilePrivate));
+ return fd;
+
+allocate:
+ fd = PR_NEW(PRFileDesc);
+ if (NULL != fd)
+ {
+ fd->secret = PR_NEW(PRFilePrivate);
+ if (NULL == fd->secret) PR_DELETE(fd);
+ }
+ if (NULL != fd) goto finished;
+ else return NULL;
+
+} /* _PR_Getfd */
+
+/*
+** Return a file descriptor to the cache unless there are too many in
+** there already. If put in cache, clear the fields first.
+*/
+void _PR_Putfd(PRFileDesc *fd)
+{
+ PR_ASSERT(PR_NSPR_IO_LAYER == fd->identity);
+ fd->methods = &_pr_faulty_methods;
+ fd->identity = PR_INVALID_IO_LAYER;
+ fd->secret->state = _PR_FILEDESC_FREED;
+
+ if (0 == _pr_fd_cache.limit_high)
+ {
+ PR_StackPush(_pr_fd_cache.stack, (PRStackElem*)(&fd->higher));
+ }
+ else
+ {
+ if (_pr_fd_cache.count > _pr_fd_cache.limit_high)
+ {
+ PR_Free(fd->secret);
+ PR_Free(fd);
+ }
+ else
+ {
+ PR_Lock(_pr_fd_cache.ml);
+ if (NULL == _pr_fd_cache.tail)
+ {
+ PR_ASSERT(0 == _pr_fd_cache.count);
+ PR_ASSERT(NULL == _pr_fd_cache.head);
+ _pr_fd_cache.head = _pr_fd_cache.tail = fd;
+ }
+ else
+ {
+ PR_ASSERT(NULL == _pr_fd_cache.tail->higher);
+ _pr_fd_cache.tail->higher = fd;
+ _pr_fd_cache.tail = fd; /* new value */
+ }
+ fd->higher = NULL; /* always so */
+ _pr_fd_cache.count += 1; /* count the new entry */
+ PR_Unlock(_pr_fd_cache.ml);
+ }
+ }
+} /* _PR_Putfd */
+
+PR_IMPLEMENT(PRStatus) PR_SetFDCacheSize(PRIntn low, PRIntn high)
+{
+ /*
+ ** This can be called at any time, may adjust the cache sizes,
+ ** turn the caches off, or turn them on. It is not dependent
+ ** on the compilation setting of DEBUG.
+ */
+ if (low > high) low = high; /* sanity check the params */
+
+ PR_Lock(_pr_fd_cache.ml);
+ if (0 == high) /* shutting down or staying down */
+ {
+ if (0 != _pr_fd_cache.limit_high) /* shutting down */
+ {
+ _pr_fd_cache.limit_high = 0; /* stop use */
+ /*
+ ** Hold the lock throughout - nobody's going to want it
+ ** other than another caller to this routine. Just don't
+ ** let that happen.
+ **
+ ** Put all the cached fds onto the new cache.
+ */
+ while (NULL != _pr_fd_cache.head)
+ {
+ PRFileDesc *fd = _pr_fd_cache.head;
+ _pr_fd_cache.head = fd->higher;
+ fd->identity = PR_NSPR_IO_LAYER;
+ _PR_Putfd(fd);
+ }
+ _pr_fd_cache.limit_low = 0;
+ _pr_fd_cache.tail = NULL;
+ _pr_fd_cache.count = 0;
+ }
+ }
+ else /* starting up or just adjusting parameters */
+ {
+ PRBool was_using_cache = (0 != _pr_fd_cache.limit_high);
+ _pr_fd_cache.limit_low = low;
+ _pr_fd_cache.limit_high = high;
+ if (was_using_cache) /* was using stack - feed into cache */
+ {
+ PRStackElem *pop;
+ while (NULL != (pop = PR_StackPop(_pr_fd_cache.stack)))
+ {
+ PRFileDesc *fd = (PRFileDesc*)
+ ((PRPtrdiff)pop - (PRPtrdiff)stack2fd);
+ fd->identity = PR_NSPR_IO_LAYER;
+ _PR_Putfd(fd);
+ }
+ }
+ }
+ PR_Unlock(_pr_fd_cache.ml);
+ return PR_SUCCESS;
+} /* PR_SetFDCacheSize */
+
+void _PR_InitFdCache()
+{
+ /*
+ ** The fd caching is enabled by default for DEBUG builds,
+ ** disabled by default for OPT builds. That default can
+ ** be overridden at runtime using environment variables
+ ** or a super-wiz-bang API.
+ */
+ const char *low = PR_GetEnv("NSPR_FD_CACHE_SIZE_LOW");
+ const char *high = PR_GetEnv("NSPR_FD_CACHE_SIZE_HIGH");
+
+ _pr_fd_cache.limit_low = _pr_fd_cache.limit_high = 0;
+ if (NULL != low) _pr_fd_cache.limit_low = atoi(low);
+ if (NULL != high) _pr_fd_cache.limit_high = atoi(high);
+
+ /*
+ **_low is allowed to be zero, _high is not.
+ ** IF _high is zero, we're not doing the caching.
+ */
+
+#if defined(DEBUG)
+ if (0 == _pr_fd_cache.limit_high)
+ _pr_fd_cache.limit_high = FD_SETSIZE;
+#endif /* defined(DEBUG) */
+
+ if (_pr_fd_cache.limit_high < _pr_fd_cache.limit_low)
+ _pr_fd_cache.limit_high = _pr_fd_cache.limit_low;
+
+ _pr_fd_cache.ml = PR_NewLock();
+ PR_ASSERT(NULL != _pr_fd_cache.ml);
+ _pr_fd_cache.stack = PR_CreateStack("FD");
+ PR_ASSERT(NULL != _pr_fd_cache.stack);
+
+} /* _PR_InitFdCache */
+
+void _PR_CleanupFdCache(void)
+{
+ PRFileDesc *fd, *next;
+ PRStackElem *pop;
+
+ for (fd = _pr_fd_cache.head; fd != NULL; fd = next)
+ {
+ next = fd->higher;
+ PR_DELETE(fd->secret);
+ PR_DELETE(fd);
+ }
+ PR_DestroyLock(_pr_fd_cache.ml);
+ while ((pop = PR_StackPop(_pr_fd_cache.stack)) != NULL)
+ {
+ fd = (PRFileDesc*)((PRPtrdiff)pop - (PRPtrdiff)stack2fd);
+ PR_DELETE(fd->secret);
+ PR_DELETE(fd);
+ }
+ PR_DestroyStack(_pr_fd_cache.stack);
+} /* _PR_CleanupFdCache */
+
+/* prfdcach.c */
diff --git a/pr/src/io/prfile.c b/pr/src/io/prfile.c
new file mode 100644
index 00000000..ece72b0a
--- /dev/null
+++ b/pr/src/io/prfile.c
@@ -0,0 +1,565 @@
+/* -*- 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)
+{
+ if (!fd || !fd->secret
+ || (fd->secret->state != _PR_FILEDESC_OPEN
+ && fd->secret->state != _PR_FILEDESC_CLOSED)) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ if (fd->secret->state == _PR_FILEDESC_OPEN) {
+ if (_PR_MD_CLOSE_FILE(fd->secret->md.osfd) < 0) {
+ return PR_FAILURE;
+ }
+ fd->secret->state = _PR_FILEDESC_CLOSED;
+ }
+ PR_FreeFileDesc(fd);
+ return PR_SUCCESS;
+}
+
+static PRInt16 PR_CALLBACK FilePoll(
+ PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
+{
+ *out_flags = 0;
+ return in_flags;
+} /* FilePoll */
+
+static 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,
+ FilePoll,
+ (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(const 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;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+ 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;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ 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 (!_pr_initialized) _PR_ImplicitInitialization();
+
+ 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..97987b92
--- /dev/null
+++ b/pr/src/io/prio.c
@@ -0,0 +1,112 @@
+/* -*- 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;
+
+void _PR_InitIO(void)
+{
+ const PRIOMethods *methods = PR_GetFileMethods();
+
+ _PR_InitFdCache();
+
+ _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((int) 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, const 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
+ fd = _PR_Getfd();
+ 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_Putfd(fd);
+}
+
+/*
+** 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)
+{
+ 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..e9e4847c
--- /dev/null
+++ b/pr/src/io/priometh.c
@@ -0,0 +1,276 @@
+/* -*- 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"
+
+/*****************************************************************************/
+/************************** Invalid I/O method object ************************/
+/*****************************************************************************/
+PRIOMethods _pr_faulty_methods = {
+ (PRDescType)0,
+ (PRCloseFN)_PR_InvalidStatus,
+ (PRReadFN)_PR_InvalidInt,
+ (PRWriteFN)_PR_InvalidInt,
+ (PRAvailableFN)_PR_InvalidInt,
+ (PRAvailable64FN)_PR_InvalidInt64,
+ (PRFsyncFN)_PR_InvalidStatus,
+ (PRSeekFN)_PR_InvalidInt,
+ (PRSeek64FN)_PR_InvalidInt64,
+ (PRFileInfoFN)_PR_InvalidStatus,
+ (PRFileInfo64FN)_PR_InvalidStatus,
+ (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)_PR_InvalidInt16,
+ (PRAcceptreadFN)_PR_InvalidInt,
+ (PRTransmitfileFN)_PR_InvalidInt,
+ (PRGetsocknameFN)_PR_InvalidStatus,
+ (PRGetpeernameFN)_PR_InvalidStatus,
+ (PRGetsockoptFN)_PR_InvalidStatus,
+ (PRSetsockoptFN)_PR_InvalidStatus,
+ (PRGetsocketoptionFN)_PR_InvalidStatus,
+ (PRSetsocketoptionFN)_PR_InvalidStatus
+};
+
+PRIntn _PR_InvalidInt()
+{
+ PR_ASSERT(!"I/O method is invalid");
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return -1;
+} /* _PR_InvalidInt */
+
+PRInt16 _PR_InvalidInt16()
+{
+ PR_ASSERT(!"I/O method is invalid");
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return -1;
+} /* _PR_InvalidInt */
+
+PRInt64 _PR_InvalidInt64()
+{
+ PRInt64 rv;
+ LL_I2L(rv, -1);
+ PR_ASSERT(!"I/O method is invalid");
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return rv;
+} /* _PR_InvalidInt */
+
+/*
+ * An invalid method that returns PRStatus
+ */
+
+PRStatus _PR_InvalidStatus()
+{
+ PR_ASSERT(!"I/O method is invalid");
+ PR_SetError(PR_INVALID_METHOD_ERROR, 0);
+ return PR_FAILURE;
+} /* _PR_InvalidDesc */
+
+/*
+ * An invalid method that returns a pointer
+ */
+
+PRFileDesc *_PR_InvalidDesc()
+{
+ PR_ASSERT(!"I/O method is invalid");
+ 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..f6e1828f
--- /dev/null
+++ b/pr/src/io/prlayer.c
@@ -0,0 +1,581 @@
+/* -*- 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->lower = fd->higher = NULL;
+
+ 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)
+{
+ PRStatus rv;
+ PRFileDesc *newfd;
+
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ newfd = (fd->lower->methods->accept)(fd->lower, addr, timeout);
+ if (newfd != NULL)
+ {
+ PRFileDesc *newstack = PR_NEW(PRFileDesc);
+ if (NULL != newstack)
+ {
+ *newstack = *fd; /* make a copy of the accepting layer */
+ rv = PR_PushIOLayer(newfd, PR_TOP_IO_LAYER, newstack);
+ if (PR_SUCCESS == rv) return newfd; /* that's it */
+ }
+ PR_Close(newfd); /* we failed for local reasons */
+ }
+ return NULL;
+}
+
+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 in_flags, PRInt16 *out_flags)
+{
+ PR_ASSERT(fd != NULL);
+ PR_ASSERT(fd->lower != NULL);
+
+ return (fd->lower->methods->poll)(fd->lower, in_flags, out_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 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(const PRIOMethods*) PR_GetDefaultIOMethods()
+{
+ return &pl_methods;
+} /* PR_GetDefaultIOMethods */
+
+PR_IMPLEMENT(PRFileDesc*) PR_CreateIOLayerStub(
+ PRDescIdentity ident, const PRIOMethods *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;
+ stack->higher = NULL;
+ }
+ 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..723cb699
--- /dev/null
+++ b/pr/src/io/prlog.c
@@ -0,0 +1,485 @@
+/* -*- 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
+
+/*
+** Coerce Win32 log output to use OutputDebugString() when
+** NSPR_LOG_FILE is set to "WinDebug".
+*/
+#if defined(XP_PC)
+#define WIN32_DEBUG_FILE (FILE*)-2
+#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 */
+
+static void _PR_SetLogModuleLevel( PRLogModuleInfo *lm )
+{
+#ifdef PR_LOGGING
+ char *ev;
+
+ ev = PR_GetEnv("NSPR_LOG_MODULES");
+ if (ev && ev[0]) {
+ char module[64];
+ PRIntn evlen = strlen(ev), pos = 0;
+ while (pos < evlen) {
+ PRIntn level = 1, count = 0, delta = 0;
+ PRLogModuleInfo *lm = logModules;
+ PRBool skip_modcheck;
+
+ 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).
+ */
+ 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;
+ }
+ /*found:*/
+ count = sscanf(&ev[pos], " , %n", &delta);
+ pos += delta;
+ if (count == -1) break;
+ }
+ }
+#endif /* PR_LOGGING */
+} /* end _PR_SetLogModuleLevel() */
+
+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;
+ }
+ _PR_SetLogModuleLevel(lm);
+ return lm;
+}
+
+PR_IMPLEMENT(PRBool) PR_SetLogFile(const char *file)
+{
+#ifdef PR_LOGGING
+#ifdef _PR_USE_STDIO_FOR_LOGGING
+ FILE *newLogFile;
+
+#ifdef XP_PC
+ if ( strcmp( file, "WinDebug") == 0)
+ {
+ logFile = WIN32_DEBUG_FILE;
+ return(PR_TRUE);
+ }
+#endif
+ 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) {
+#ifdef XP_PC
+ if ( logFile == WIN32_DEBUG_FILE)
+ OutputDebugString( line );
+ else
+ _PUT_LOG(logFile, line, nb);
+#else
+ _PUT_LOG(logFile, line, nb);
+#endif
+ } 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..6bd4dddc
--- /dev/null
+++ b/pr/src/io/prmapopt.c
@@ -0,0 +1,423 @@
+/* -*- 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
+
+#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;
+ }
+#ifdef IP_ADD_MEMBERSHIP
+ 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;
+ }
+#endif /* IP_ADD_MEMBERSHIP */
+ 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;
+ }
+#ifdef IP_ADD_MEMBERSHIP
+ 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;
+ }
+#endif /* IP_ADD_MEMBERSHIP */
+ 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 */
+
+/*
+ *********************************************************************
+ *********************************************************************
+ **
+ ** Make sure that the following is at the end of this file,
+ ** because we will be playing with macro redefines.
+ **
+ *********************************************************************
+ *********************************************************************
+ */
+
+/*
+ * 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 */
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..98becef2
--- /dev/null
+++ b/pr/src/io/prmwait.c
@@ -0,0 +1,1460 @@
+/* -*- 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 "pprmwait.h"
+
+#define _MW_REHASH_MAX 11
+
+static PRLock *mw_lock = NULL;
+static _PRGlobalState *mw_state = NULL;
+
+static PRIntervalTime max_polling_interval;
+
+#ifdef WINNT
+
+typedef struct TimerEvent {
+ PRIntervalTime absolute;
+ void (*func)(void *);
+ void *arg;
+ LONG ref_count;
+ PRCList links;
+} TimerEvent;
+
+#define TIMER_EVENT_PTR(_qp) \
+ ((TimerEvent *) ((char *) (_qp) - offsetof(TimerEvent, links)))
+
+struct {
+ PRLock *ml;
+ PRCondVar *new_timer;
+ PRCondVar *cancel_timer;
+ PRThread *manager_thread;
+ PRCList timer_queue;
+} tm_vars;
+
+static PRStatus TimerInit(void);
+static void TimerManager(void *arg);
+static TimerEvent *CreateTimer(PRIntervalTime timeout,
+ void (*func)(void *), void *arg);
+static PRBool CancelTimer(TimerEvent *timer);
+
+static void TimerManager(void *arg)
+{
+ PRIntervalTime now;
+ PRIntervalTime timeout;
+ PRCList *head;
+ TimerEvent *timer;
+
+ PR_Lock(tm_vars.ml);
+ while (1)
+ {
+ if (PR_CLIST_IS_EMPTY(&tm_vars.timer_queue))
+ {
+ PR_WaitCondVar(tm_vars.new_timer, PR_INTERVAL_NO_TIMEOUT);
+ }
+ else
+ {
+ now = PR_IntervalNow();
+ head = PR_LIST_HEAD(&tm_vars.timer_queue);
+ timer = TIMER_EVENT_PTR(head);
+ if ((PRInt32) (now - timer->absolute) >= 0)
+ {
+ PR_REMOVE_LINK(head);
+ /*
+ * make its prev and next point to itself so that
+ * it's obvious that it's not on the timer_queue.
+ */
+ PR_INIT_CLIST(head);
+ PR_ASSERT(2 == timer->ref_count);
+ PR_Unlock(tm_vars.ml);
+ timer->func(timer->arg);
+ PR_Lock(tm_vars.ml);
+ timer->ref_count -= 1;
+ if (0 == timer->ref_count)
+ {
+ PR_NotifyAllCondVar(tm_vars.cancel_timer);
+ }
+ }
+ else
+ {
+ timeout = (PRIntervalTime)(timer->absolute - now);
+ PR_WaitCondVar(tm_vars.new_timer, timeout);
+ }
+ }
+ }
+ PR_Unlock(tm_vars.ml);
+}
+
+static TimerEvent *CreateTimer(
+ PRIntervalTime timeout,
+ void (*func)(void *),
+ void *arg)
+{
+ TimerEvent *timer;
+ PRCList *links, *tail;
+ TimerEvent *elem;
+
+ timer = PR_NEW(TimerEvent);
+ if (NULL == timer)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return timer;
+ }
+ timer->absolute = PR_IntervalNow() + timeout;
+ timer->func = func;
+ timer->arg = arg;
+ timer->ref_count = 2;
+ PR_Lock(tm_vars.ml);
+ tail = links = PR_LIST_TAIL(&tm_vars.timer_queue);
+ while (links->prev != tail)
+ {
+ elem = TIMER_EVENT_PTR(links);
+ if ((PRInt32)(timer->absolute - elem->absolute) >= 0)
+ {
+ break;
+ }
+ links = links->prev;
+ }
+ PR_INSERT_AFTER(&timer->links, links);
+ PR_NotifyCondVar(tm_vars.new_timer);
+ PR_Unlock(tm_vars.ml);
+ return timer;
+}
+
+static PRBool CancelTimer(TimerEvent *timer)
+{
+ PRBool canceled = PR_FALSE;
+
+ PR_Lock(tm_vars.ml);
+ timer->ref_count -= 1;
+ if (timer->links.prev == &timer->links)
+ {
+ while (timer->ref_count == 1)
+ {
+ PR_WaitCondVar(tm_vars.cancel_timer, PR_INTERVAL_NO_TIMEOUT);
+ }
+ }
+ else
+ {
+ PR_REMOVE_LINK(&timer->links);
+ canceled = PR_TRUE;
+ }
+ PR_Unlock(tm_vars.ml);
+ PR_DELETE(timer);
+ return canceled;
+}
+
+static PRStatus TimerInit(void)
+{
+ tm_vars.ml = PR_NewLock();
+ if (NULL == tm_vars.ml)
+ {
+ goto failed;
+ }
+ tm_vars.new_timer = PR_NewCondVar(tm_vars.ml);
+ if (NULL == tm_vars.new_timer)
+ {
+ goto failed;
+ }
+ tm_vars.cancel_timer = PR_NewCondVar(tm_vars.ml);
+ if (NULL == tm_vars.cancel_timer)
+ {
+ goto failed;
+ }
+ tm_vars.manager_thread = PR_CreateThread(
+ PR_SYSTEM_THREAD, TimerManager, NULL, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, 0);
+ if (NULL == tm_vars.manager_thread)
+ {
+ goto failed;
+ }
+ PR_INIT_CLIST(&tm_vars.timer_queue);
+ return PR_SUCCESS;
+
+failed:
+ if (NULL != tm_vars.cancel_timer)
+ {
+ PR_DestroyCondVar(tm_vars.cancel_timer);
+ }
+ if (NULL != tm_vars.new_timer)
+ {
+ PR_DestroyCondVar(tm_vars.new_timer);
+ }
+ if (NULL != tm_vars.ml)
+ {
+ PR_DestroyLock(tm_vars.ml);
+ }
+ return PR_FAILURE;
+}
+
+#endif /* WINNT */
+
+/******************************************************************/
+/******************************************************************/
+/************************ The private portion *********************/
+/******************************************************************/
+/******************************************************************/
+void _PR_InitMW(void)
+{
+#ifdef WINNT
+ /*
+ * We use NT 4's InterlockedCompareExchange() to operate
+ * on PRMWStatus variables.
+ */
+ PR_ASSERT(sizeof(PVOID) == sizeof(PRMWStatus));
+ TimerInit();
+#endif
+ mw_lock = PR_NewLock();
+ PR_ASSERT(NULL != mw_lock);
+ mw_state = PR_NEWZAP(_PRGlobalState);
+ PR_ASSERT(NULL != mw_state);
+ PR_INIT_CLIST(&mw_state->group_list);
+ max_polling_interval = PR_MillisecondsToInterval(MAX_POLLING_INTERVAL);
+} /* _PR_InitMW */
+
+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 _MW_REHASH_MAX 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 = _MW_REHASH_MAX;
+ PRRecvWait **waiter;
+ PRUintn hidx = _MW_HASH(desc->fd, hash->length);
+ PRUintn hoffset = 0;
+
+ while (rehash-- > 0)
+ {
+ 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
+ if (0 == hoffset)
+ {
+ hoffset = _MW_HASH2(desc->fd, hash->length);
+ PR_ASSERT(0 != hoffset);
+ }
+ hidx = (hidx + hoffset) % (hash->length);
+ }
+ return _prmw_rehash;
+} /* MW_AddHashInternal */
+
+static _PR_HashStory MW_ExpandHashInternal(PRWaitGroup *group)
+{
+ PRRecvWait **desc;
+ PRUint32 pidx, length;
+ _PRWaiterHash *newHash, *oldHash = group->waiter;
+ PRBool retry;
+ _PR_HashStory hrv;
+
+ 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(PRInt32));
+
+ /* 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)
+ {
+ break;
+ }
+ }
+ /* table size must be one of the prime numbers */
+ PR_ASSERT(pidx < primes);
+
+ /* if pidx == primes - 1, we can't expand the table any more */
+ while (pidx < primes - 1)
+ {
+ /* next size */
+ ++pidx;
+ length = prime_number[pidx];
+
+ /* allocate the new hash table and fill it in with the old */
+ newHash = (_PRWaiterHash*)PR_CALLOC(
+ sizeof(_PRWaiterHash) + (length * sizeof(PRRecvWait*)));
+ if (NULL == newHash)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return _prmw_error;
+ }
+
+ newHash->length = length;
+ retry = PR_FALSE;
+ for (desc = &oldHash->recv_wait;
+ newHash->count < oldHash->count; ++desc)
+ {
+ PR_ASSERT(desc < &oldHash->recv_wait + oldHash->length);
+ if (NULL != *desc)
+ {
+ hrv = MW_AddHashInternal(*desc, newHash);
+ PR_ASSERT(_prmw_error != hrv);
+ if (_prmw_success != hrv)
+ {
+ PR_DELETE(newHash);
+ retry = PR_TRUE;
+ break;
+ }
+ }
+ }
+ if (retry) continue;
+
+ PR_DELETE(group->waiter);
+ group->waiter = newHash;
+ group->p_timestamp += 1;
+ return _prmw_success;
+ }
+
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return _prmw_error; /* we're hosed */
+} /* MW_ExpandHashInternal */
+
+#ifndef WINNT
+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 */
+#endif /* WINNT */
+
+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 = _MW_REHASH_MAX;
+ _PRWaiterHash *hash = group->waiter;
+ PRUintn hidx = _MW_HASH(fd, hash->length);
+ PRUintn hoffset = 0;
+
+ while (rehash-- > 0)
+ {
+ desc = (&hash->recv_wait) + hidx;
+ if ((*desc != NULL) && ((*desc)->fd == fd)) return desc;
+ if (0 == hoffset)
+ {
+ hoffset = _MW_HASH2(fd, hash->length);
+ PR_ASSERT(0 != hoffset);
+ }
+ hidx = (hidx + hoffset) % (hash->length);
+ }
+ return NULL;
+} /* _MW_LookupInternal */
+
+#ifndef WINNT
+static PRStatus _MW_PollInternal(PRWaitGroup *group)
+{
+ PRRecvWait **waiter;
+ PRStatus rv = PR_FAILURE;
+ PRUintn count, count_ready;
+ PRIntervalTime polling_interval;
+
+ group->poller = PR_GetCurrentThread();
+
+ while (PR_TRUE)
+ {
+ PRIntervalTime now, since_last_poll;
+ PRPollDesc *poll_list;
+
+ while (0 == group->waiter->count)
+ {
+ PRStatus st;
+ st = PR_WaitCondVar(group->new_business, PR_INTERVAL_NO_TIMEOUT);
+ if (_prmw_running != group->state)
+ {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ goto aborted;
+ }
+ if (_MW_ABORTED(st)) goto aborted;
+ }
+
+ /*
+ ** 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;
+ PRPollDesc *old_polling_list = group->polling_list;
+
+ PR_Unlock(group->ml);
+ poll_list = (PRPollDesc*)PR_CALLOC(new_size);
+ if (NULL == poll_list)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ PR_Lock(group->ml);
+ goto failed_alloc;
+ }
+ if (NULL != old_polling_list)
+ PR_DELETE(old_polling_list);
+ PR_Lock(group->ml);
+ if (_prmw_running != group->state)
+ {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ goto aborted;
+ }
+ 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;
+
+ waiter = &group->waiter->recv_wait;
+ poll_list = group->polling_list;
+ for (count = 0; count < group->waiter->count; ++waiter)
+ {
+ PR_ASSERT(waiter < &group->waiter->recv_wait
+ + group->waiter->length);
+ if (NULL != *waiter) /* a live one! */
+ {
+ if ((PR_INTERVAL_NO_TIMEOUT != (*waiter)->timeout)
+ && (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;
+ }
+ PR_ASSERT(poll_list < group->polling_list
+ + group->polling_count);
+ 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 there are no more threads waiting for completion,
+ ** we need to return.
+ */
+ if ((!PR_CLIST_IS_EMPTY(&group->io_ready))
+ && (1 == group->waiting_threads)) break;
+
+ if (0 == count) continue; /* wait for new business */
+
+ group->last_poll = now;
+
+ PR_Unlock(group->ml);
+
+ count_ready = PR_Poll(group->polling_list, count, polling_interval);
+
+ PR_Lock(group->ml);
+
+ if (_prmw_running != group->state)
+ {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ goto aborted;
+ }
+ if (-1 == count_ready)
+ {
+ goto failed_poll; /* that's a shame */
+ }
+ else if (0 < count_ready)
+ {
+ for (poll_list = group->polling_list; count > 0;
+ poll_list++, count--)
+ {
+ PR_ASSERT(
+ poll_list < group->polling_list + group->polling_count);
+ if (poll_list->out_flags != 0)
+ {
+ waiter = _MW_LookupInternal(group, poll_list->fd);
+ /*
+ ** If 'waiter' is NULL, that means the wait receive
+ ** descriptor has been canceled.
+ */
+ 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 ((!PR_CLIST_IS_EMPTY(&group->io_ready))
+ && (1 == group->waiting_threads)) break;
+ }
+
+ rv = PR_SUCCESS;
+
+aborted:
+failed_poll:
+failed_alloc:
+ group->poller = NULL; /* we were that, not we ain't */
+ if ((_prmw_running == group->state) && (group->waiting_threads > 1))
+ {
+ /* Wake up one thread to become the new poller. */
+ PR_NotifyCondVar(group->io_complete);
+ }
+ return rv; /* we return with the lock held */
+} /* _MW_PollInternal */
+#endif /* !WINNT */
+
+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))
+ {
+ rv = group->state = _prmw_stopped;
+ PR_NotifyCondVar(group->mw_manage);
+ }
+ return rv;
+} /* MW_TestForShutdownInternal */
+
+#ifndef WINNT
+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 */
+#endif
+
+#ifdef WINNT
+static void NT_TimeProc(void *arg)
+{
+ _MDOverlapped *overlapped = (_MDOverlapped *)arg;
+ PRRecvWait *desc = overlapped->data.mw.desc;
+ PRFileDesc *bottom;
+
+ if (InterlockedCompareExchange((PVOID *)&desc->outcome,
+ (PVOID)PR_MW_TIMEOUT, (PVOID)PR_MW_PENDING) != (PVOID)PR_MW_PENDING)
+ {
+ /* This wait recv descriptor has already completed. */
+ return;
+ }
+
+ /* close the osfd to abort the outstanding async io request */
+ /* $$$$
+ ** Little late to be checking if NSPR's on the bottom of stack,
+ ** but if we don't check, we can't assert that the private data
+ ** is what we think it is.
+ ** $$$$
+ */
+ bottom = PR_GetIdentitiesLayer(desc->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom);
+ if (NULL != bottom) /* now what!?!?! */
+ {
+ bottom->secret->state = _PR_FILEDESC_CLOSED;
+ if (closesocket(bottom->secret->md.osfd) == SOCKET_ERROR)
+ {
+ fprintf(stderr, "closesocket failed: %d\n", WSAGetLastError());
+ PR_ASSERT(!"What shall I do?");
+ }
+ }
+ return;
+} /* NT_TimeProc */
+
+static PRStatus NT_HashRemove(PRWaitGroup *group, PRFileDesc *fd)
+{
+ PRRecvWait **waiter;
+
+ _PR_MD_LOCK(&group->mdlock);
+ waiter = _MW_LookupInternal(group, fd);
+ if (NULL != waiter)
+ {
+ group->waiter->count -= 1;
+ *waiter = NULL;
+ }
+ _PR_MD_UNLOCK(&group->mdlock);
+ return (NULL != waiter) ? PR_SUCCESS : PR_FAILURE;
+}
+
+PRStatus NT_HashRemoveInternal(PRWaitGroup *group, PRFileDesc *fd)
+{
+ PRRecvWait **waiter;
+
+ waiter = _MW_LookupInternal(group, fd);
+ if (NULL != waiter)
+ {
+ group->waiter->count -= 1;
+ *waiter = NULL;
+ }
+ return (NULL != waiter) ? PR_SUCCESS : PR_FAILURE;
+}
+#endif /* WINNT */
+
+/******************************************************************/
+/******************************************************************/
+/********************** The public API portion ********************/
+/******************************************************************/
+/******************************************************************/
+PR_IMPLEMENT(PRStatus) PR_AddWaitFileDesc(
+ PRWaitGroup *group, PRRecvWait *desc)
+{
+ _PR_HashStory hrv;
+ PRStatus rv = PR_FAILURE;
+#ifdef WINNT
+ _MDOverlapped *overlapped;
+ HANDLE hFile;
+ BOOL bResult;
+ DWORD dwError;
+ PRFileDesc *bottom;
+#endif
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+ if ((NULL == group) && (NULL == (group = MW_Init2())))
+ {
+ return rv;
+ }
+
+ 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);
+ PR_Unlock(group->ml);
+ return rv;
+ }
+
+#ifdef WINNT
+ _PR_MD_LOCK(&group->mdlock);
+#endif
+
+ /*
+ ** 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);
+
+#ifdef WINNT
+ _PR_MD_UNLOCK(&group->mdlock);
+#endif
+
+ PR_NotifyCondVar(group->new_business); /* tell the world */
+ rv = (_prmw_success == hrv) ? PR_SUCCESS : PR_FAILURE;
+ PR_Unlock(group->ml);
+
+#ifdef WINNT
+ overlapped = PR_NEWZAP(_MDOverlapped);
+ if (NULL == overlapped)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ NT_HashRemove(group, desc->fd);
+ return rv;
+ }
+ overlapped->ioModel = _MD_MultiWaitIO;
+ overlapped->data.mw.desc = desc;
+ overlapped->data.mw.group = group;
+ if (desc->timeout != PR_INTERVAL_NO_TIMEOUT)
+ {
+ overlapped->data.mw.timer = CreateTimer(
+ desc->timeout,
+ NT_TimeProc,
+ overlapped);
+ if (0 == overlapped->data.mw.timer)
+ {
+ NT_HashRemove(group, desc->fd);
+ PR_DELETE(overlapped);
+ /*
+ * XXX It appears that a maximum of 16 timer events can
+ * be outstanding. GetLastError() returns 0 when I try it.
+ */
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, GetLastError());
+ return PR_FAILURE;
+ }
+ }
+
+ /* Reach to the bottom layer to get the OS fd */
+ bottom = PR_GetIdentitiesLayer(desc->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom);
+ if (NULL == bottom)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ hFile = (HANDLE)bottom->secret->md.osfd;
+ if (!bottom->secret->md.io_model_committed)
+ {
+ PRInt32 st;
+ st = _md_Associate(hFile);
+ PR_ASSERT(0 != st);
+ bottom->secret->md.io_model_committed = PR_TRUE;
+ }
+ bResult = ReadFile(hFile,
+ desc->buffer.start,
+ (DWORD)desc->buffer.length,
+ NULL,
+ &overlapped->overlapped);
+ if (FALSE == bResult && (dwError = GetLastError()) != ERROR_IO_PENDING)
+ {
+ if (desc->timeout != PR_INTERVAL_NO_TIMEOUT)
+ {
+ if (InterlockedCompareExchange((PVOID *)&desc->outcome,
+ (PVOID)PR_MW_FAILURE, (PVOID)PR_MW_PENDING)
+ == (PVOID)PR_MW_PENDING)
+ {
+ CancelTimer(overlapped->data.mw.timer);
+ }
+ NT_HashRemove(group, desc->fd);
+ PR_DELETE(overlapped);
+ }
+ _PR_MD_MAP_READ_ERROR(dwError);
+ rv = PR_FAILURE;
+ }
+#endif
+
+ return rv;
+} /* PR_AddWaitFileDesc */
+
+PR_IMPLEMENT(PRRecvWait*) PR_WaitRecvReady(PRWaitGroup *group)
+{
+ PRCList *io_ready = NULL;
+#ifdef WINNT
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ _MDOverlapped *overlapped;
+#endif
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+ 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 */
+
+#ifdef WINNT
+ _PR_MD_LOCK(&group->mdlock);
+ while (PR_CLIST_IS_EMPTY(&group->io_ready))
+ {
+ _PR_THREAD_LOCK(me);
+ me->state = _PR_IO_WAIT;
+ PR_APPEND_LINK(&me->waitQLinks, &group->wait_list);
+ if (!_PR_IS_NATIVE_THREAD(me))
+ {
+ _PR_SLEEPQ_LOCK(me->cpu);
+ _PR_ADD_SLEEPQ(me, PR_INTERVAL_NO_TIMEOUT);
+ _PR_SLEEPQ_UNLOCK(me->cpu);
+ }
+ _PR_THREAD_UNLOCK(me);
+ _PR_MD_UNLOCK(&group->mdlock);
+ PR_Unlock(group->ml);
+ _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
+ me->state = _PR_RUNNING;
+ PR_Lock(group->ml);
+ _PR_MD_LOCK(&group->mdlock);
+ if (_PR_PENDING_INTERRUPT(me)) {
+ PR_REMOVE_LINK(&me->waitQLinks);
+ _PR_MD_UNLOCK(&group->mdlock);
+ me->flags &= ~_PR_INTERRUPT;
+ me->io_suspended = PR_FALSE;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ goto aborted;
+ }
+ }
+ io_ready = PR_LIST_HEAD(&group->io_ready);
+ PR_ASSERT(io_ready != NULL);
+ PR_REMOVE_LINK(io_ready);
+ _PR_MD_UNLOCK(&group->mdlock);
+ overlapped = (_MDOverlapped *)
+ ((char *)io_ready - offsetof(_MDOverlapped, data));
+ io_ready = &overlapped->data.mw.desc->internal;
+#else
+ 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))
+ {
+ /*
+ ** 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;
+ }
+ else
+ {
+ /*
+ ** There are four reasons a thread can be awakened from
+ ** a wait on the io_complete condition variable.
+ ** 1. Some I/O has completed, i.e., the io_ready list
+ ** is nonempty.
+ ** 2. The wait group is canceled.
+ ** 3. The thread is interrupted.
+ ** 4. The current polling thread has to leave and needs
+ ** a replacement.
+ ** The logic to find a new polling thread is made more
+ ** complicated by all the other possible events.
+ ** I tried my best to write the logic clearly, but
+ ** it is still full of if's with continue and goto.
+ */
+ PRStatus st;
+ while (PR_CLIST_IS_EMPTY(&group->io_ready))
+ {
+ st = PR_WaitCondVar(group->io_complete, PR_INTERVAL_NO_TIMEOUT);
+ if (_prmw_running != group->state)
+ {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ goto aborted;
+ }
+ if (_MW_ABORTED(st) || (NULL == group->poller)) break;
+ }
+
+ /*
+ ** The thread is interrupted and has to leave. It might
+ ** have also been awakened to process ready i/o or be the
+ ** new poller. To be safe, if either condition is true,
+ ** we awaken another thread to take its place.
+ */
+ if (_MW_ABORTED(st))
+ {
+ if ((NULL == group->poller
+ || !PR_CLIST_IS_EMPTY(&group->io_ready))
+ && group->waiting_threads > 1)
+ PR_NotifyCondVar(group->io_complete);
+ goto aborted;
+ }
+
+ /*
+ ** A new poller is needed, but can I be the new poller?
+ ** If there is no i/o ready, sure. But if there is any
+ ** i/o ready, it has a higher priority. I want to
+ ** process the ready i/o first and wake up another
+ ** thread to be the new poller.
+ */
+ if (NULL == group->poller)
+ {
+ if (PR_CLIST_IS_EMPTY(&group->io_ready))
+ continue;
+ if (group->waiting_threads > 1)
+ PR_NotifyCondVar(group->io_complete);
+ }
+ }
+ PR_ASSERT(!PR_CLIST_IS_EMPTY(&group->io_ready));
+ }
+ io_ready = PR_LIST_HEAD(&group->io_ready);
+ PR_NotifyCondVar(group->io_taken);
+ PR_ASSERT(io_ready != NULL);
+ PR_REMOVE_LINK(io_ready);
+ } while (NULL == io_ready);
+
+failed_poll:
+
+#endif
+
+aborted:
+
+ group->waiting_threads -= 1;
+invalid_state:
+ (void)MW_TestForShutdownInternal(group);
+ PR_Unlock(group->ml);
+
+failed_init:
+ if (NULL != io_ready)
+ {
+ /* If the operation failed, record the reason why */
+ switch (((PRRecvWait*)io_ready)->outcome)
+ {
+ case PR_MW_PENDING:
+ PR_ASSERT(0);
+ break;
+ case PR_MW_SUCCESS:
+#ifndef WINNT
+ _MW_InitialRecv(io_ready);
+#endif
+ break;
+#ifdef WINNT
+ case PR_MW_FAILURE:
+ _PR_MD_MAP_READ_ERROR(overlapped->data.mw.error);
+ break;
+#endif
+ 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;
+ }
+#ifdef WINNT
+ if (NULL != overlapped->data.mw.timer)
+ {
+ PR_ASSERT(PR_INTERVAL_NO_TIMEOUT
+ != overlapped->data.mw.desc->timeout);
+ CancelTimer(overlapped->data.mw.timer);
+ }
+ else
+ {
+ PR_ASSERT(PR_INTERVAL_NO_TIMEOUT
+ == overlapped->data.mw.desc->timeout);
+ }
+ PR_DELETE(overlapped);
+#endif
+ }
+ return (PRRecvWait*)io_ready;
+} /* PR_WaitRecvReady */
+
+PR_IMPLEMENT(PRStatus) PR_CancelWaitFileDesc(PRWaitGroup *group, PRRecvWait *desc)
+{
+#if !defined(WINNT)
+ PRRecvWait **recv_wait;
+#endif
+ PRStatus rv = PR_SUCCESS;
+ 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;
+ }
+
+#ifdef WINNT
+ if (InterlockedCompareExchange((PVOID *)&desc->outcome,
+ (PVOID)PR_MW_INTERRUPT, (PVOID)PR_MW_PENDING) == (PVOID)PR_MW_PENDING)
+ {
+ PRFileDesc *bottom = PR_GetIdentitiesLayer(desc->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom);
+ if (NULL == bottom)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ goto invalid_arg;
+ }
+ bottom->secret->state = _PR_FILEDESC_CLOSED;
+#if 0
+ fprintf(stderr, "cancel wait recv: closing socket\n");
+#endif
+ if (closesocket(bottom->secret->md.osfd) == SOCKET_ERROR)
+ {
+ fprintf(stderr, "closesocket failed: %d\n", WSAGetLastError());
+ exit(1);
+ }
+ }
+#else
+ 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:
+#endif
+stopping:
+invalid_arg:
+ PR_Unlock(group->ml);
+ return rv;
+} /* PR_CancelWaitFileDesc */
+
+PR_IMPLEMENT(PRRecvWait*) PR_CancelWaitGroup(PRWaitGroup *group)
+{
+ PRRecvWait **desc;
+ PRRecvWait *recv_wait = NULL;
+#ifdef WINNT
+ _MDOverlapped *overlapped;
+ PRRecvWait **end;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+#endif
+
+ 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 */
+ else
+ {
+ PR_NotifyAllCondVar(group->new_business);
+ PR_NotifyAllCondVar(group->io_complete);
+ }
+ while (_prmw_stopped != group->state)
+ (void)PR_WaitCondVar(group->mw_manage, PR_INTERVAL_NO_TIMEOUT);
+ }
+
+#ifdef WINNT
+ _PR_MD_LOCK(&group->mdlock);
+#endif
+ /* make all the existing descriptors look done/interrupted */
+#ifdef WINNT
+ end = &group->waiter->recv_wait + group->waiter->length;
+ for (desc = &group->waiter->recv_wait; desc < end; ++desc)
+ {
+ if (NULL != *desc)
+ {
+ if (InterlockedCompareExchange((PVOID *)&(*desc)->outcome,
+ (PVOID)PR_MW_INTERRUPT, (PVOID)PR_MW_PENDING)
+ == (PVOID)PR_MW_PENDING)
+ {
+ PRFileDesc *bottom = PR_GetIdentitiesLayer(
+ (*desc)->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom);
+ if (NULL == bottom)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ goto invalid_arg;
+ }
+ bottom->secret->state = _PR_FILEDESC_CLOSED;
+#if 0
+ fprintf(stderr, "cancel wait group: closing socket\n");
+#endif
+ if (closesocket(bottom->secret->md.osfd) == SOCKET_ERROR)
+ {
+ fprintf(stderr, "closesocket failed: %d\n",
+ WSAGetLastError());
+ exit(1);
+ }
+ }
+ }
+ }
+ while (group->waiter->count > 0)
+ {
+ _PR_THREAD_LOCK(me);
+ me->state = _PR_IO_WAIT;
+ PR_APPEND_LINK(&me->waitQLinks, &group->wait_list);
+ if (!_PR_IS_NATIVE_THREAD(me))
+ {
+ _PR_SLEEPQ_LOCK(me->cpu);
+ _PR_ADD_SLEEPQ(me, PR_INTERVAL_NO_TIMEOUT);
+ _PR_SLEEPQ_UNLOCK(me->cpu);
+ }
+ _PR_THREAD_UNLOCK(me);
+ _PR_MD_UNLOCK(&group->mdlock);
+ PR_Unlock(group->ml);
+ _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
+ me->state = _PR_RUNNING;
+ PR_Lock(group->ml);
+ _PR_MD_LOCK(&group->mdlock);
+ }
+#else
+ for (desc = &group->waiter->recv_wait; group->waiter->count > 0; ++desc)
+ {
+ PR_ASSERT(desc < &group->waiter->recv_wait + group->waiter->length);
+ if (NULL != *desc)
+ _MW_DoneInternal(group, desc, PR_MW_INTERRUPT);
+ }
+#endif
+
+ /* 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);
+#ifdef WINNT
+ overlapped = (_MDOverlapped *)
+ ((char *)head - offsetof(_MDOverlapped, data));
+ head = &overlapped->data.mw.desc->internal;
+ if (NULL != overlapped->data.mw.timer)
+ {
+ PR_ASSERT(PR_INTERVAL_NO_TIMEOUT
+ != overlapped->data.mw.desc->timeout);
+ CancelTimer(overlapped->data.mw.timer);
+ }
+ else
+ {
+ PR_ASSERT(PR_INTERVAL_NO_TIMEOUT
+ == overlapped->data.mw.desc->timeout);
+ }
+ PR_DELETE(overlapped);
+#endif
+ recv_wait = (PRRecvWait*)head;
+ }
+#ifdef WINNT
+invalid_arg:
+ _PR_MD_UNLOCK(&group->mdlock);
+#endif
+ PR_Unlock(group->ml);
+
+ return recv_wait;
+} /* PR_CancelWaitGroup */
+
+PR_IMPLEMENT(PRWaitGroup*) PR_CreateWaitGroup(PRInt32 size /* ignored */)
+{
+ PRWaitGroup *wg;
+
+ if (NULL == (wg = PR_NEWZAP(PRWaitGroup)))
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ 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)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ goto failed_waiter;
+ }
+ wg->waiter->count = 0;
+ wg->waiter->length = _PR_DEFAULT_HASH_LENGTH;
+
+#ifdef WINNT
+ _PR_MD_NEW_LOCK(&wg->mdlock);
+ PR_INIT_CLIST(&wg->wait_list);
+#endif /* WINNT */
+
+ 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_complete);
+failed_cvar1:
+ PR_DestroyCondVar(wg->io_taken);
+failed_cvar0:
+ PR_DestroyLock(wg->ml);
+failed_lock:
+ PR_DELETE(wg);
+ wg = NULL;
+
+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)
+ {
+ PR_Lock(group->ml);
+ if ((group->waiting_threads == 0)
+ && (group->waiter->count == 0)
+ && PR_CLIST_IS_EMPTY(&group->io_ready))
+ {
+ group->state = _prmw_stopped;
+ }
+ else
+ {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ rv = PR_FAILURE;
+ }
+ PR_Unlock(group->ml);
+ if (PR_FAILURE == rv) return rv;
+
+ PR_Lock(mw_lock);
+ PR_REMOVE_LINK(&group->group_link);
+ PR_Unlock(mw_lock);
+
+#ifdef WINNT
+ /*
+ * XXX make sure wait_list is empty and waiter is empty.
+ * These must be checked while holding mdlock.
+ */
+ _PR_MD_FREE_LOCK(&group->mdlock);
+#endif
+
+ PR_DELETE(group->waiter);
+ PR_DELETE(group->polling_list);
+ PR_DestroyCondVar(group->mw_manage);
+ 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);
+ }
+ else
+ {
+ /* The default wait group is not created yet. */
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ rv = PR_FAILURE;
+ }
+ return rv;
+} /* PR_DestroyWaitGroup */
+
+/**********************************************************************
+***********************************************************************
+******************** Wait group enumerations **************************
+***********************************************************************
+**********************************************************************/
+
+PR_IMPLEMENT(PRMWaitEnumerator*) PR_CreateMWaitEnumerator(PRWaitGroup *group)
+{
+ PRMWaitEnumerator *enumerator = PR_NEWZAP(PRMWaitEnumerator);
+ if (NULL == enumerator) PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ else
+ {
+ enumerator->group = group;
+ enumerator->seal = _PR_ENUM_SEALED;
+ }
+ return enumerator;
+} /* PR_CreateMWaitEnumerator */
+
+PR_IMPLEMENT(PRStatus) PR_DestroyMWaitEnumerator(PRMWaitEnumerator* enumerator)
+{
+ PR_ASSERT(NULL != enumerator);
+ PR_ASSERT(_PR_ENUM_SEALED == enumerator->seal);
+ if ((NULL == enumerator) || (_PR_ENUM_SEALED != enumerator->seal))
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ enumerator->seal = _PR_ENUM_UNSEALED;
+ PR_Free(enumerator);
+ return PR_SUCCESS;
+} /* PR_DestroyMWaitEnumerator */
+
+PR_IMPLEMENT(PRRecvWait*) PR_EnumerateWaitGroup(
+ PRMWaitEnumerator *enumerator, const PRRecvWait *previous)
+{
+ PRRecvWait *result = NULL;
+
+ /* entry point sanity checking */
+ PR_ASSERT(NULL != enumerator);
+ PR_ASSERT(_PR_ENUM_SEALED == enumerator->seal);
+ if ((NULL == enumerator)
+ || (_PR_ENUM_SEALED != enumerator->seal)) goto bad_argument;
+
+ /* beginning of enumeration */
+ if (NULL == previous)
+ {
+ if (NULL == enumerator->group)
+ {
+ enumerator->group = mw_state->group;
+ if (NULL == enumerator->group)
+ {
+ PR_SetError(PR_GROUP_EMPTY_ERROR, 0);
+ return NULL;
+ }
+ }
+ enumerator->waiter = &enumerator->group->waiter->recv_wait;
+ enumerator->p_timestamp = enumerator->group->p_timestamp;
+ enumerator->thread = PR_GetCurrentThread();
+ enumerator->index = 0;
+ }
+ /* continuing an enumeration */
+ else
+ {
+ PRThread *me = PR_GetCurrentThread();
+ PR_ASSERT(me == enumerator->thread);
+ if (me != enumerator->thread) goto bad_argument;
+
+ /* need to restart the enumeration */
+ if (enumerator->p_timestamp != enumerator->group->p_timestamp)
+ return PR_EnumerateWaitGroup(enumerator, NULL);
+ }
+
+ /* actually progress the enumeration */
+#if defined(WINNT)
+ _PR_MD_LOCK(&enumerator->group->mdlock);
+#else
+ PR_Lock(enumerator->group->ml);
+#endif
+ while (enumerator->index++ < enumerator->group->waiter->length)
+ {
+ if (NULL != (result = *(enumerator->waiter)++)) break;
+ }
+#if defined(WINNT)
+ _PR_MD_UNLOCK(&enumerator->group->mdlock);
+#else
+ PR_Unlock(enumerator->group->ml);
+#endif
+
+ return result; /* what we live for */
+
+bad_argument:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL; /* probably ambiguous */
+} /* PR_EnumerateWaitGroup */
+
+/* prmwait.c */
diff --git a/pr/src/io/prpolevt.c b/pr/src/io/prpolevt.c
new file mode 100644
index 00000000..a1ca3760
--- /dev/null
+++ b/pr/src/io/prpolevt.c
@@ -0,0 +1,121 @@
+/* -*- 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.
+ */
+
+/*
+ *********************************************************************
+ *
+ * Pollable events
+ *
+ *********************************************************************
+ */
+
+#include "primpl.h"
+
+typedef struct MyFilePrivate {
+ PRFilePrivate copy;
+ PRFileDesc *writeEnd;
+ PRFilePrivate *oldSecret;
+} MyFilePrivate;
+
+#ifndef XP_UNIX
+#define USE_TCP_SOCKETPAIR
+#endif
+
+PR_IMPLEMENT(PRFileDesc *) PR_NewPollableEvent(void)
+{
+ PRFileDesc *fd[2]; /* fd[0] is the read end; fd[1] is the write end */
+ MyFilePrivate *secret;
+
+ secret = PR_NEW(MyFilePrivate);
+ if (secret == NULL) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ goto errorExit;
+ }
+
+#ifndef USE_TCP_SOCKETPAIR
+ if (PR_CreatePipe(&fd[0], &fd[1]) == PR_FAILURE) {
+ goto errorExit;
+ }
+#else
+ if (PR_NewTCPSocketPair(fd) == PR_FAILURE) {
+ goto errorExit;
+ }
+#endif
+
+ secret->copy = *fd[0]->secret;
+ secret->oldSecret = fd[0]->secret;
+ secret->writeEnd = fd[1];
+ fd[0]->secret = (PRFilePrivate *) secret;
+
+ return fd[0];
+
+errorExit:
+ PR_DELETE(secret);
+ return NULL;
+}
+
+PR_IMPLEMENT(PRStatus) PR_DestroyPollableEvent(PRFileDesc *event)
+{
+ MyFilePrivate *secret;
+
+ secret = (MyFilePrivate *) event->secret;
+ event->secret = secret->oldSecret;
+ PR_Close(event);
+ PR_Close(secret->writeEnd);
+ PR_DELETE(secret);
+ return PR_SUCCESS;
+}
+
+static const char magicChar = '\x38';
+
+PR_IMPLEMENT(PRStatus) PR_SetPollableEvent(PRFileDesc *event)
+{
+ MyFilePrivate *secret;
+
+ secret = (MyFilePrivate *) event->secret;
+ if (PR_Write(secret->writeEnd, &magicChar, 1) != 1) {
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus) PR_WaitForPollableEvent(PRFileDesc *event)
+{
+ char buf[1024];
+ PRInt32 nBytes;
+#ifdef DEBUG
+ PRIntn i;
+#endif
+
+ nBytes = PR_Read(event, buf, sizeof(buf));
+ if (nBytes == -1) {
+ return PR_FAILURE;
+ }
+
+#ifdef DEBUG
+ /*
+ * Make sure people do not write to the pollable event fd
+ * directly.
+ */
+ for (i = 0; i < nBytes; i++) {
+ PR_ASSERT(buf[i] == magicChar);
+ }
+#endif
+
+ return PR_SUCCESS;
+}
diff --git a/pr/src/io/prprf.c b/pr/src/io/prprf.c
new file mode 100644
index 00000000..ee6fc44d
--- /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(LINUX) && defined(__powerpc__)) || defined(WIN16)
+#define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0]
+#else
+#define VARARGS_ASSIGN(foo, bar) (foo) = (bar)
+#endif
+
+/*
+** 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..96530bf6
--- /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 *) = (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..22dcd721
--- /dev/null
+++ b/pr/src/io/prsocket.c
@@ -0,0 +1,1605 @@
+/* -*- 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>
+
+/************************************************************************/
+
+/* These two functions are only used in assertions. */
+#if defined(DEBUG)
+
+static PRBool IsValidNetAddr(const PRNetAddr *addr)
+{
+ if ((addr != NULL)
+#ifdef XP_UNIX
+ && (addr->raw.family != AF_UNIX)
+#endif
+#ifdef _PR_INET6
+ && (addr->raw.family != AF_INET6)
+#endif
+ && (addr->raw.family != AF_INET)) {
+ return PR_FALSE;
+ }
+ return PR_TRUE;
+}
+
+static PRBool IsValidNetAddrLen(const PRNetAddr *addr, PRInt32 addr_len)
+{
+ /*
+ * The definition of the length of a Unix domain socket address
+ * is not uniform, so we don't check it.
+ */
+ if ((addr != NULL)
+#ifdef XP_UNIX
+ && (addr->raw.family != AF_UNIX)
+#endif
+ && (PR_NETADDR_SIZE(addr) != addr_len)) {
+ return PR_FALSE;
+ }
+ return PR_TRUE;
+}
+
+#endif /* DEBUG */
+
+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;
+#define LOCAL_MAXIOV 8
+ PRIOVec local_iov[LOCAL_MAXIOV];
+ int tmp_out;
+ int index, iov_cnt;
+ int count=0, sz = 0; /* 'count' is the return value. */
+
+ 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;
+ }
+
+ /*
+ * Assume the first writev will succeed. Copy iov's only on
+ * failure.
+ */
+ tmp_iov = iov;
+ for (index = 0; index < iov_size; index++)
+ sz += 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 */
+
+ if (tmp_iov == iov) {
+ /*
+ * The first writev failed so we
+ * must copy iov's around.
+ * Avoid calloc/free if there
+ * are few enough iov's.
+ */
+ if (iov_size - index <= LOCAL_MAXIOV)
+ tmp_iov = local_iov;
+ else if ((tmp_iov = (PRIOVec *) PR_CALLOC((iov_size - index) *
+ sizeof *tmp_iov)) == NULL) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+ }
+
+ /* 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 != iov && tmp_iov != local_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;
+
+ 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 defined(WIN32)
+ /*
+ * The sleep circumvents a bug in Win32 WinSock.
+ * See Microsoft Knowledge Base article ID: Q165989.
+ */
+ Sleep(0);
+#endif /* WIN32 */
+
+ if (pd->out_flags & PR_POLL_EXCEPT) {
+ int 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) {
+ int 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(IsValidNetAddr(addr) == PR_TRUE);
+ PR_ASSERT(IsValidNetAddrLen(addr, al) == PR_TRUE);
+
+ 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;
+
+ PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
+
+#ifdef XP_UNIX
+ if (addr->raw.family == AF_UNIX) {
+ /* Disallow relative pathnames */
+ if (addr->local.path[0] != '/') {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ } else {
+#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
+
+#ifdef XP_UNIX
+ }
+#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)
+{
+ if (!fd || !fd->secret
+ || (fd->secret->state != _PR_FILEDESC_OPEN
+ && fd->secret->state != _PR_FILEDESC_CLOSED)) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ if (fd->secret->state == _PR_FILEDESC_OPEN) {
+ if (_PR_MD_CLOSE_SOCKET(fd->secret->md.osfd) < 0) {
+ return PR_FAILURE;
+ }
+ fd->secret->state = _PR_FILEDESC_CLOSED;
+ }
+
+ PR_FreeFileDesc(fd);
+ 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;
+ }
+
+ PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
+
+ 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;
+ }
+ /* The socket must be in blocking mode. */
+ if (sd->secret->nonblocking) {
+ PR_SetError(PR_INVALID_ARGUMENT_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;
+ }
+ /* The socket must be in blocking mode. */
+ if (sd->secret->nonblocking) {
+ PR_SetError(PR_INVALID_ARGUMENT_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.
+ */
+ 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(IsValidNetAddr(addr) == PR_TRUE);
+ PR_ASSERT(IsValidNetAddrLen(addr, addrlen) == PR_TRUE);
+ 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(IsValidNetAddr(addr) == PR_TRUE);
+ PR_ASSERT(IsValidNetAddrLen(addr, addrlen) == PR_TRUE);
+ 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, (char*)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, (const char*)optval, optlen);
+ }
+ }
+ return rv;
+}
+
+static PRInt16 PR_CALLBACK SocketPoll(
+ PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
+{
+ *out_flags = 0;
+ return in_flags;
+} /* SocketPoll */
+
+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,
+ SocketPoll,
+ 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,
+ SocketPoll,
+ (PRAcceptreadFN)_PR_InvalidInt,
+ (PRTransmitfileFN)_PR_InvalidInt,
+ SocketGetName,
+ SocketGetPeerName,
+ SocketGetSockOpt,
+ SocketSetSockOpt,
+ _PR_SocketGetSocketOption,
+ _PR_SocketSetSocketOption
+};
+
+PR_IMPLEMENT(const PRIOMethods*) PR_GetTCPMethods()
+{
+ return &tcpMethods;
+}
+
+PR_IMPLEMENT(const 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
+#if defined(XP_UNIX)
+ && AF_UNIX != 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];
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ 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;
+#else /* XP_UNIX */
+ PRFileDesc *listenSock;
+ PRNetAddr selfAddr;
+ PRUint16 port;
+
+ f[0] = f[1] = NULL;
+ listenSock = PR_NewTCPSocket();
+ if (listenSock == NULL) {
+ goto failed;
+ }
+ PR_InitializeNetAddr(PR_IpAddrAny, 0, &selfAddr);
+ if (PR_Bind(listenSock, &selfAddr) == PR_FAILURE) {
+ goto failed;
+ }
+ if (PR_GetSockName(listenSock, &selfAddr) == PR_FAILURE) {
+ goto failed;
+ }
+ port = ntohs(selfAddr.inet.port);
+ if (PR_Listen(listenSock, 5) == PR_FAILURE) {
+ goto failed;
+ }
+ f[0] = PR_NewTCPSocket();
+ if (f[0] == NULL) {
+ goto failed;
+ }
+ PR_InitializeNetAddr(PR_IpAddrLoopback, port, &selfAddr);
+
+ /*
+ * Only a thread is used to do the connect and accept.
+ * I am relying on the fact that PR_Connect returns
+ * successfully as soon as the connect request is put
+ * into the listen queue (but before PR_Accept is called).
+ * This is the behavior of the BSD socket code. If
+ * connect does not return until accept is called, we
+ * will need to create another thread to call connect.
+ */
+ if (PR_Connect(f[0], &selfAddr, PR_INTERVAL_NO_TIMEOUT)
+ == PR_FAILURE) {
+ goto failed;
+ }
+ f[1] = PR_Accept(listenSock, NULL, PR_INTERVAL_NO_TIMEOUT);
+ if (f[1] == NULL) {
+ goto failed;
+ }
+ PR_Close(listenSock);
+ return PR_SUCCESS;
+
+failed:
+ if (listenSock) {
+ PR_Close(listenSock);
+ }
+ if (f[0]) {
+ PR_Close(f[0]);
+ }
+ return PR_FAILURE;
+#endif /* XP_UNIX */
+}
+
+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 = (char*)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 + 2 * 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 = -1;
+ PRNetAddr remote;
+ PRFileDesc *accepted = NULL;
+
+ /* The socket must be in blocking mode. */
+ if (sd->secret->nonblocking)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return rv;
+ }
+
+ /*
+ ** The timeout does not apply to the accept portion of the
+ ** operation - it waits indefinitely.
+ */
+ accepted = PR_Accept(sd, &remote, PR_INTERVAL_NO_TIMEOUT);
+ if (NULL == accepted) return rv;
+
+ rv = PR_Recv(accepted, buf, amount, 0, timeout);
+ if (rv >= 0)
+ {
+ /* copy the new info out where caller can see it */
+ PRPtrdiff aligned = (PRPtrdiff)buf + amount + sizeof(void*) - 1;
+ *raddr = (PRNetAddr*)(aligned & ~(sizeof(void*) - 1));
+ memcpy(*raddr, &remote, PR_NETADDR_SIZE(&remote));
+ *nd = accepted;
+ return rv;
+ }
+
+ PR_Close(accepted);
+ return rv;
+}
+
+/*
+** 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 = (PRPollDesc*)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 = (PRPollDesc*)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..ff06b33d
--- /dev/null
+++ b/pr/src/linking/prlink.c
@@ -0,0 +1,973 @@
+/* -*- 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>
+#elif defined(RHAPSODY)
+#include <mach-o/dyld.h>
+#endif
+
+/* Define this on systems which don't have it (AIX) */
+#ifndef RTLD_LAZY
+#define RTLD_LAZY RTLD_NOW
+#endif
+#endif /* XP_UNIX */
+
+/*
+ * On these platforms, symbols have a leading '_'.
+ */
+#if defined(SUNOS4) || defined(RHAPSODY) || defined(WIN16)
+#define NEED_LEADING_UNDERSCORE
+#endif
+
+#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;
+#elif defined(RHAPSODY)
+ NSModule 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)
+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 %d", 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 USE_DLFCN
+ error = dlerror(); /* $$$ That'll be wrong some of the time - AOF */
+#elif defined(HAVE_STRERROR)
+ error = strerror(oserr); /* this should be okay */
+#else
+ error = errno_string(oserr);
+#endif
+ 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 */
+#elif defined(RHAPSODY)
+ h = NULL; /* XXXX toshok */
+#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 || defined RHAPSODY
+ {
+ char *home;
+ char *local;
+ char *p=NULL;
+ char * mozilla_home=NULL;
+ 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 */
+ }
+
+ mozilla_home = getenv("MOZILLA_HOME");
+ if (mozilla_home && mozilla_home[0]) {
+ len += strlen(mozilla_home) + 5 ; /* +5 for initial : and trailing "/lib" */
+ }
+
+ 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);
+ }
+ if (mozilla_home && mozilla_home[0]) {
+ strcat(p, ":");
+ strcat(p, mozilla_home);
+ strcat(p, "/lib");
+ }
+ strcat(p, local);
+ } /* if (p) */
+ ev = p;
+ PR_LOG(_pr_io_lm, PR_LOG_NOTICE, ("linker path '%s'", ev));
+
+ printf("linker_path = %s\n", 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);
+#elif defined(RHAPSODY)
+ NSObjectFileImage ofi;
+ NSModule h = NULL;
+ if (NSCreateObjectFileImageFromFile(name, &ofi)
+ == NSObjectFileImageSuccess) {
+ h = NSLinkModule(ofi, name, TRUE);
+ }
+#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);
+#elif defined(RHAPSODY)
+ result = NSUnLinkModule(lib->dlh, FALSE);
+#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;
+ }
+#elif defined(RHAPSODY)
+ f = NSAddressOfSymbol(NSLookupAndBindSymbol(name));
+#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(NEED_LEADING_UNDERSCORE)
+ char *name;
+#else
+ const char *name;
+#endif
+ /*
+ ** Mangle the raw symbol name in any way that is platform specific.
+ */
+#if defined(NEED_LEADING_UNDERSCORE)
+ /* 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;
+#else
+ name = raw_name;
+#endif
+
+ PR_EnterMonitor(pr_linker_lock);
+ PR_ASSERT(lib != NULL);
+ f = pr_FindSymbolInLib(lib, name);
+
+#if defined(NEED_LEADING_UNDERSCORE)
+ 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(NEED_LEADING_UNDERSCORE)
+ char *name;
+#else
+ const char *name;
+#endif
+ PRLibrary* lm;
+
+ /*
+ ** Mangle the raw symbol name in any way that is platform specific.
+ */
+#if defined(NEED_LEADING_UNDERSCORE)
+ /* 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;
+#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(NEED_LEADING_UNDERSCORE)
+ 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=NULL;
+ PRLibrary* result = NULL;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ /* 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..61f50023
--- /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%ld\n",malloc_maxsize);
+ fprintf(fd,"Pagesize\t%ld\n",malloc_pagesize);
+ fprintf(fd,"Pageshift\t%ld\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 **young, **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 :-) */
+ young = (struct pginfo**) map_pages(i,0);
+ if (!young)
+ return 0;
+
+ /* Copy the old stuff */
+ memset(young, 0, i * malloc_pagesize);
+ memcpy(young, 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 = young;
+
+ /* Mark the pages */
+ index = ((u_long)young >> 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 = (struct pgfree*)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 = (char*)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 = (struct pgfree*)_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((char*)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..c3fdcebc
--- /dev/null
+++ b/pr/src/malloc/prmem.c
@@ -0,0 +1,254 @@
+/* -*- 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;
+
+#ifdef IRIX
+#define _PR_Lock_Malloc() { \
+ PRIntn _is; \
+ if(PR_TRUE == _PR_malloc_initialised) { \
+ if (_PR_MD_GET_ATTACHED_THREAD() && \
+ !_PR_IS_NATIVE_THREAD( \
+ _PR_MD_GET_ATTACHED_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_GET_ATTACHED_THREAD() && \
+ !_PR_IS_NATIVE_THREAD( \
+ _PR_MD_GET_ATTACHED_THREAD())) \
+ _PR_INTSON(_is); \
+ } \
+ }
+#else /* IRIX */
+#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 /* IRIX */
+#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/MANIFEST b/pr/src/md/mac/MANIFEST
new file mode 100644
index 00000000..c51cbd5a
--- /dev/null
+++ b/pr/src/md/mac/MANIFEST
@@ -0,0 +1,7 @@
+#
+# This is a list of local files which get copied to the mozilla:dist directory
+#
+
+MacErrorHandling.h
+macsocket.h
+prcpucfg.h
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..24d9f29f
--- /dev/null
+++ b/pr/src/md/mac/macio.c
@@ -0,0 +1,1875 @@
+/* -*- 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_MD_SET_INTSOFF(1);
+
+ thread->md.osErrCode = noErr;
+ DoneWaitingOnThisThread(thread);
+
+ _PR_MD_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;
+ HParamBlockRec 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 = PBHOpenSync(&pb);
+ if (err == noErr)
+ return pb.ioParam.ioRefNum;
+ else if ((err != fnfErr) || ((oflag & O_CREAT) == 0))
+ goto ErrorExit;
+
+ err = PBHCreateSync(&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, PRSeekWhence 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.
+
+ const char *src;
+ char *tgt;
+ OSErr err = noErr;
+
+ PR_ASSERT(unixPath != nil);
+ if (nil == unixPath) {
+ err = paramErr;
+ goto exit;
+ }
+
+ // If unixPath is a zero-length string, we copy ":" into
+ // macPath, so we need a minimum of two bytes to handle
+ // the case of ":".
+ *macPath = malloc(strlen(unixPath) + 2); // Will be enough extra space.
+ require_action (*macPath != NULL, exit, err = memFullErr;);
+
+ src = unixPath;
+ tgt = *macPath;
+
+ if (PL_strchr(src, PR_DIRECTORY_SEPARATOR) == src) // If weÕre dealing with an absolute
+ src++; // path, skip the separator
+ else
+ *(tgt++) = PR_PATH_SEPARATOR;
+
+ if (PL_strstr(src, UNIX_THIS_DIRECTORY_STR) == src) // If it starts with /
+ src += 2; // skip it.
+
+ while (*src)
+ { // deal with the rest of the path
+ if (PL_strstr(src, UNIX_PARENT_DIRECTORY_STR) == src) { // Going up?
+ *(tgt++) = PR_PATH_SEPARATOR; // simply add an extra colon.
+ src +=3;
+ }
+ else if (*src == PR_DIRECTORY_SEPARATOR) { // Change the separator
+ *(tgt++) = PR_PATH_SEPARATOR;
+ src++;
+ }
+ else
+ *(tgt++) = *(src++);
+ }
+
+ *tgt = NULL; // make sure itÕs null terminated.
+
+exit:
+ return err;
+}
+
+
+#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, "/usr/local/netscape/", (tempLen = strlen("/usr/local/netscape/")))) {
+
+ unixPath += tempLen;
+
+ if (!strncmp(unixPath, "RequiredGuts/", (tempLen = strlen("RequiredGuts/"))))
+ {
+ unixPath += tempLen;
+ err = FindGutsFolder(&foundSpec);
+ }
+ else if (!strncmp(unixPath, "bin/", (tempLen = strlen("bin/"))))
+ {
+ unixPath += tempLen;
+ err = FindNetscapeFolder(&foundSpec);
+ }
+ else if (*unixPath == '\0')
+ {
+ // it's /usr/local/netscape
+ err = FindGutsFolder(&foundSpec);
+ }
+
+ }
+
+ else {
+ // This is a root relative directory, weÕll just convert the whole thing.
+ err = CreateMacPathFromUnixPath(unixPath, macPath);
+ goto Exit_ConvertUnixPathToMacPath;
+ }
+
+
+
+ // WeÕre dealing with a special folder
+ if (err == noErr)
+ {
+ Handle hPathStr;
+ // Get the path to the root-relative directory
+ err = FSpGetFullPath(&foundSpec, &pathBufferSize, &hPathStr); // NewHandle's hPathStr
+
+ if (noErr == err)
+ {
+ // convert handle to c-string
+ // add one for NULL termination
+ // pathBufferSize is now one greater than the length of the string
+ pathBufferSize++;
+
+ *macPath = (char*) malloc(sizeof(char) * pathBufferSize);
+ (*macPath)[pathBufferSize - 1] = '\0';
+ BlockMoveData(*hPathStr, *macPath, pathBufferSize - 1);
+
+ DisposeHandle(hPathStr);
+ }
+ }
+
+ if (err == noErr)
+ {
+ UInt32 unixPathLeft;
+ UInt32 macPathLen;
+
+ unixPathLeft = strlen(unixPath);
+ macPathLen = strlen(*macPath);
+
+
+ // copy over the remaining file name, converting
+ if (pathBufferSize - 1 < macPathLen + unixPathLeft)
+ {
+ // need to grow string
+ *macPath = realloc(*macPath, macPathLen + unixPathLeft + 1);
+ err = (*macPath == NULL ? memFullErr : noErr);
+ }
+
+ if (err == noErr)
+ {
+ // carefully remove the '/''s out of the unix path. If we see an "escaped" /
+ // we will leave it in there, otherwise we take it out and replace it with a :
+ // we have to do this before we convert to a mac-path, so we can tell what is
+ // really a path separator and what is in the name of a file or directory
+ // Make sure that all of the /Õs are :Õs in the final pathname
+ // effectively we do a
+ // strcat(*macPath, unixPath); while replace all occurrences of / with : in unixPath
+ char* dp;
+ const char* sp;
+
+ sp = unixPath;
+ dp = *macPath + macPathLen;
+
+ for (;*sp != '\0'; sp++, dp++)
+ {
+ if (*sp == PR_DIRECTORY_SEPARATOR)
+ {
+ // if we can look at the previous character
+ if (sp > unixPath)
+ {
+ // check to see if previous character is an escape
+ if (sp[-1] == '\\')
+ {
+ // leave it in, and cycle
+ continue;
+ }
+ else
+ {
+ *dp = PR_PATH_SEPARATOR;
+ }
+ }
+ else
+ *dp = PR_PATH_SEPARATOR;
+ }
+ else
+ {
+ // just copy;
+ *dp = *sp;
+ }
+ }
+
+ *dp = '\0'; // NULL terminate *macPath
+ }
+#if DEBUG
+ // we used to check here, now we check above, we leave this in
+ // the debug build to make sure we didn't screw up
+ // Make sure that all of the /Õs are :Õs in the final pathname
+ for (temp = *macPath + strlen(*macPath) - strlen(unixPath); *temp != '\0'; temp++) {
+
+ if (*temp == PR_DIRECTORY_SEPARATOR)
+ {
+ DebugStr("\pFound a slash");
+ *temp = PR_PATH_SEPARATOR;
+ }
+ }
+#endif
+ }
+ }
+
+
+Exit_ConvertUnixPathToMacPath:
+
+ return err;
+}
+
+// Hey! Before you delete this "hack" you should look at how it's being
+// used by sun-java/netscape/applet/appletStubs.c.
+PR_IMPLEMENT (OSErr)
+ConvertMacPathToUnixPath(const char *macPath, char **unixPath)
+{
+ // *** HACK ***
+ // Get minimal version working
+
+ char *unixPathPtr;
+
+ *unixPath = malloc(strlen(macPath) + 2); // Add one for the front slash, one for null
+ if (*unixPath == NULL)
+ return (memFullErr);
+
+ unixPathPtr = *unixPath;
+
+ *unixPathPtr++ = PR_DIRECTORY_SEPARATOR;
+
+ do {
+ // Translate all colons to slashes
+ if (*macPath == PR_PATH_SEPARATOR)
+ *unixPathPtr = PR_DIRECTORY_SEPARATOR;
+ else
+ *unixPathPtr = *macPath;
+
+ unixPathPtr++;
+ macPath++;
+ } while (*macPath != NULL);
+
+ // Terminate the string
+ *unixPathPtr = '\0';
+
+ return (noErr);
+}
+
+OSErr
+ConvertUnixPathToFSSpec(const char *unixPath, FSSpec *fileSpec)
+{
+ char* macPath;
+ OSErr convertError;
+ int len;
+
+ convertError = ConvertUnixPathToMacPath(unixPath, &macPath);
+ if (convertError != noErr)
+ return convertError;
+
+ len = strlen(macPath);
+
+ if (*macPath == PR_PATH_SEPARATOR)
+ {
+ if (len < sizeof(Str255))
+ {
+ short vRefNum;
+ long dirID;
+ Str255 pascalMacPath;
+
+ convertError = HGetVol(NULL, &vRefNum, &dirID);
+ if (convertError == noErr)
+ {
+ PStrFromCStr(macPath, pascalMacPath);
+ convertError = FSMakeFSSpec(vRefNum, dirID, pascalMacPath, fileSpec);
+ }
+ }
+ else
+ convertError = paramErr;
+ }
+ else
+ {
+ convertError = FSpLocationFromFullPath(len, macPath, fileSpec);
+ if (convertError == fnfErr)
+ {
+ CInfoPBRec pb;
+ Str255 pascalMacPath;
+ OSErr err;
+
+ PStrFromCStr(macPath, pascalMacPath);
+ /*
+ FSpLocationFromFullPath does not work for directories unless there is
+ a ":" at the end. We will make sure of an existence of a directory.
+ If so, the returned fileSpec is valid from FSpLocationFromFullPath eventhough
+ it returned an error.
+ */
+ pb.hFileInfo.ioNamePtr = pascalMacPath;
+ pb.hFileInfo.ioVRefNum = 0;
+ pb.hFileInfo.ioDirID = 0;
+ pb.hFileInfo.ioFDirIndex = 0;
+
+ err = PBGetCatInfoSync(&pb);
+ if (err == noErr)
+ convertError = noErr;
+ }
+ }
+
+ free(macPath);
+
+ return (convertError);
+}
+
+
+FILE *_OS_FOPEN(const char *filename, const char *mode)
+{
+ OSErr err = noErr;
+ char *macFileName = NULL;
+ FILE *result;
+
+ err = ConvertUnixPathToMacPath(filename, &macFileName);
+ if (err != noErr)
+ goto ErrorExit;
+
+ result = fopen(macFileName, mode);
+
+ PR_DELETE(macFileName);
+
+ return result;
+
+ErrorExit:
+ _PR_MD_CURRENT_THREAD()->md.osErrCode = err;
+ _MD_SetError(err);
+ return NULL;
+}
+
+#else
+
+short GetVolumeRefNumFromName(const char *cTgtVolName)
+{
+ OSErr err;
+ Str32 pVolName;
+ char *cVolName = NULL;
+ HParamBlockRec hPB;
+ short refNum = 0;
+
+ hPB.volumeParam.ioVolIndex = 0;
+ hPB.volumeParam.ioNamePtr = pVolName;
+ do {
+ hPB.volumeParam.ioVolIndex++;
+ err = PBHGetVInfoSync(&hPB);
+ CStrFromPStr(pVolName, &cVolName);
+ if (strcmp(cTgtVolName, cVolName) == 0) {
+ refNum = hPB.volumeParam.ioVRefNum;
+ PR_DELETE(cVolName);
+ break;
+ }
+ PR_DELETE(cVolName);
+ } while (err == noErr);
+
+ return refNum;
+}
+
+
+
+static OSErr GetFullPath(short vRefNum, long dirID, char **fullPath, int *strSize)
+{
+ Str255 pascalDirName;
+ char cDirName[256];
+ char *tmpPath = NULL; // needed since sprintf isnÕt safe
+ CInfoPBRec myPB;
+ OSErr err = noErr;
+
+
+ // get the full path of the temp folder.
+ *strSize = 256;
+ *fullPath = NULL;
+ *fullPath = malloc(*strSize); // How big should this thing be?
+ require_action (*fullPath != NULL, errorExit, err = memFullErr;);
+
+ tmpPath = malloc(*strSize);
+ require_action (tmpPath != NULL, errorExit, err = memFullErr;);
+
+ strcpy(*fullPath, ""); // Clear C result
+ strcpy(tmpPath, "");
+ pascalDirName[0] = 0; // Clear Pascal intermediate string
+
+ myPB.dirInfo.ioNamePtr = &pascalDirName[0];
+ myPB.dirInfo.ioVRefNum = vRefNum;
+ myPB.dirInfo.ioDrParID = dirID;
+ myPB.dirInfo.ioFDirIndex = -1; // Getting info about
+
+ do {
+ myPB.dirInfo.ioDrDirID = myPB.dirInfo.ioDrParID;
+
+ err = PBGetCatInfoSync(&myPB);
+ require(err == noErr, errorExit);
+
+ // Move the name into C domain
+ memcpy(&cDirName, &pascalDirName, 256);
+ p2cstr((unsigned char *)&cDirName); // Changes in place!
+
+ if ((strlen(cDirName) + strlen(*fullPath)) > *strSize) {
+ // We need to grow the string, do it in 256 byte chunks
+ (*strSize) += 256;
+ *fullPath = PR_REALLOC(*fullPath, *strSize);
+ require_action (*fullPath != NULL, errorExit, err = memFullErr;);
+
+ tmpPath = PR_REALLOC(tmpPath, *strSize);
+ require_action (tmpPath != NULL, errorExit, err = memFullErr;);
+ }
+ sprintf(tmpPath, "%s:%s", cDirName, *fullPath);
+ strcpy(*fullPath, tmpPath);
+ } while (myPB.dirInfo.ioDrDirID != fsRtDirID);
+
+ PR_DELETE(tmpPath);
+
+ return noErr;
+
+
+errorExit:
+ PR_DELETE(*fullPath);
+ PR_DELETE(tmpPath);
+
+ return err;
+
+}
+
+static OSErr CreateMacPathFromUnixPath(const char *unixPath, char **macPath)
+{
+ // Given a Unix style path with '/' directory separators, this allocates
+ // a path with Mac style directory separators in the path.
+ //
+ // It does not do any special directory translation; use ConvertUnixPathToMacPath
+ // for that.
+
+ const char *src;
+ char *tgt;
+ OSErr err = noErr;
+
+ PR_ASSERT(unixPath != nil);
+ if (nil == unixPath) {
+ err = paramErr;
+ goto exit;
+ }
+
+ // If unixPath is a zero-length string, we copy ":" into
+ // macPath, so we need a minimum of two bytes to handle
+ // the case of ":".
+ *macPath = malloc(strlen(unixPath) + 2); // Will be enough extra space.
+ require_action (*macPath != NULL, exit, err = memFullErr;);
+
+ src = unixPath;
+ tgt = *macPath;
+
+ if (PL_strchr(src, PR_DIRECTORY_SEPARATOR) == src) // If weÕre dealing with an absolute
+ src++; // path, skip the separator
+ else
+ *(tgt++) = PR_PATH_SEPARATOR;
+
+ if (PL_strstr(src, UNIX_THIS_DIRECTORY_STR) == src) // If it starts with ./
+ src += 2; // skip it.
+
+ while (*src)
+ { // deal with the rest of the path
+ if (PL_strstr(src, UNIX_PARENT_DIRECTORY_STR) == src) { // Going up?
+ *(tgt++) = PR_PATH_SEPARATOR; // simply add an extra colon.
+ src +=3;
+ }
+ else if (*src == PR_DIRECTORY_SEPARATOR) { // Change the separator
+ *(tgt++) = PR_PATH_SEPARATOR;
+ src++;
+ }
+ else
+ *(tgt++) = *(src++);
+ }
+
+ *tgt = NULL; // make sure itÕs null terminated.
+
+exit:
+ return err;
+}
+
+static OSErr ConvertUnixPathToMacPath(const char *unixPath, char **macPath)
+{
+ OSErr err = noErr;
+
+
+ //
+ // Convert UNIX style path names (with . and / separators) into a Macintosh
+ // style path (with :).
+ //
+ // There are also a couple of special paths that need to be dealt with
+ // by translating them to the appropriate Mac special folders. These include:
+ //
+ // /usr/tmp/file => {TempFolder}file
+ //
+ // The file conversions we need to do are as follows:
+ //
+ // file => file
+ // dir/file => :dir:file
+ // ./file => file
+ // ../file => ::file
+ // ../dir/file => ::dir:file
+ // /file => ::BootDrive:file
+ // /dir/file => ::BootDrive:dir:file
+
+
+ if (*unixPath != PR_DIRECTORY_SEPARATOR) { // Not root relative, just convert it.
+ err = CreateMacPathFromUnixPath(unixPath, macPath);
+ }
+
+ else {
+ // WeÕre root-relative. This is either a special Unix directory, or a
+ // full path (which weÕll support on the Mac since they might be generated).
+ // This is not condoning the use of full-paths on the Macintosh for file
+ // specification.
+
+ short foundVRefNum;
+ long foundDirID;
+ int pathBufferSize;
+ char *temp;
+ char isNetscapeDir = false;
+
+ // Are we dealing with the temp folder?
+ if (strncmp(unixPath, "/usr/tmp", strlen("/usr/tmp")) == 0){
+ unixPath += 8;
+ if (*unixPath == PR_DIRECTORY_SEPARATOR)
+ unixPath++; // Skip the slash
+ err = FindFolder(kOnSystemDisk, kTemporaryFolderType, kCreateFolder, // Create if needed
+ &foundVRefNum, &foundDirID);
+ }
+
+ if (strncmp(unixPath, "/tmp", strlen("/tmp")) == 0) {
+ unixPath += 4; // Skip the slash
+ if (*unixPath == PR_DIRECTORY_SEPARATOR)
+ unixPath++; // Skip the slash
+ err = FindFolder(kOnSystemDisk, kTemporaryFolderType, kCreateFolder, // Create if needed
+ &foundVRefNum, &foundDirID);
+ }
+
+ else if (strncmp(unixPath, "/usr", strlen("/usr")) == 0) {
+
+ int usrNetscapePathLen;
+
+ usrNetscapePathLen = strlen("/usr/local/netscape/");
+
+ if (strncmp(unixPath, "/usr/local/netscape/", usrNetscapePathLen) == 0) {
+ unixPath += usrNetscapePathLen;
+// err = FindPreferencesFolder(&foundVRefNum, &foundDirID);
+ err = paramErr;
+ isNetscapeDir = true;
+ }
+
+ else {
+ dprintf("Unable to translate Unix file path %s to Mac path\n", unixPath);
+ err = -1;
+ goto Exit_ConvertUnixPathToMacPath;
+ }
+
+ }
+
+ else {
+ // This is a root relative directory, weÕll just convert the whole thing.
+ err = CreateMacPathFromUnixPath(unixPath, macPath);
+ goto Exit_ConvertUnixPathToMacPath;
+ }
+
+ // WeÕre dealing with a special folder
+ if (err == noErr)
+ // Get the path to the root-relative directory
+ err = GetFullPath(foundVRefNum, foundDirID, macPath, &pathBufferSize); // mallocs macPath
+
+ if (err == noErr){
+
+ // copy over the remaining file name, converting
+ if (pathBufferSize < (strlen(*macPath) + strlen(unixPath))) {
+ // need to grow string
+ *macPath = PR_REALLOC(*macPath, (strlen(*macPath) + strlen(unixPath) +
+ (isNetscapeDir ? strlen("Netscape Ä:") : 0)));
+ err = (*macPath == NULL ? memFullErr : noErr);
+ }
+
+ if (isNetscapeDir)
+ strcat(*macPath, "Netscape Ä:");
+
+ if (err == noErr)
+ strcat(*macPath, unixPath);
+
+ // Make sure that all of the /Õs are :Õs in the final pathname
+
+ for (temp = *macPath + strlen(*macPath) - strlen(unixPath); *temp != '\0'; temp++) {
+ if (*temp == PR_DIRECTORY_SEPARATOR)
+ *temp = PR_PATH_SEPARATOR;
+ }
+
+ }
+ }
+
+
+Exit_ConvertUnixPathToMacPath:
+
+ return err;
+}
+
+OSErr
+ConvertUnixPathToFSSpec(const char *unixPath, FSSpec *fileSpec)
+{
+ char* macPath;
+ OSErr convertError;
+ int len;
+
+ convertError = ConvertUnixPathToMacPath(unixPath, &macPath);
+ if (convertError != noErr)
+ return convertError;
+
+ len = strlen(macPath);
+
+ if (*macPath == PR_PATH_SEPARATOR)
+ {
+ if (len < sizeof(Str255))
+ {
+ short vRefNum;
+ long dirID;
+ Str255 pascalMacPath;
+
+ convertError = HGetVol(NULL, &vRefNum, &dirID);
+ if (convertError == noErr)
+ {
+ PStrFromCStr(macPath, pascalMacPath);
+ convertError = FSMakeFSSpec(vRefNum, dirID, pascalMacPath, fileSpec);
+ }
+ }
+ else
+ convertError = paramErr;
+ }
+ else
+ {
+ convertError = FSpLocationFromFullPath(len, macPath, fileSpec);
+ }
+
+ free(macPath);
+
+ return (convertError);
+}
+
+
+#endif
+
+/*
+ **********************************************************************
+ *
+ * Memory-mapped files are not implementable on the Mac.
+ *
+ **********************************************************************
+ */
+
+PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size)
+{
+#pragma unused (fmap, size)
+
+ PR_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..93d3b100
--- /dev/null
+++ b/pr/src/md/mac/macsockclient.c
@@ -0,0 +1,543 @@
+/* -*- 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)
+{
+ PRInt32 osfd;
+ fd_set rd, wt, ex;
+ PRFileDesc *bottom;
+ PRPollDesc *pd, *epd;
+ PRInt32 ready, maxfd = -1;
+ PRInt16 in_flags, out_flags;
+ struct timeval tv, *tvp = NULL;
+
+ FD_ZERO(&rd);
+ FD_ZERO(&wt);
+ FD_ZERO(&ex);
+
+ for (pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ if (NULL == pd->fd) continue;
+
+ in_flags = (bottom->methods->poll)(pd->fd, pd->in_flags, &pd->out_flags);
+ if (0 != (in_flags & pd->out_flags)) ready += 1;
+ else
+ {
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom);
+ 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)
+ {
+ PRInt32 ticksPerSecond = PR_TicksPerSecond();
+ tv.tv_sec = timeout / ticksPerSecond;
+ tv.tv_usec = timeout - (ticksPerSecond * tv.tv_sec);
+ tv.tv_usec = (PR_USEC_PER_SEC * tv.tv_usec) / ticksPerSecond;
+ tvp = &tv;
+ }
+
+ ready = select(maxfd + 1, &rd, &wt, &ex, tvp);
+
+ if (ready > 0)
+ {
+ ready = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ out_flags = 0;
+ in_flags = pd->in_flags;
+
+ if ((NULL == pd->fd) || (0 == in_flags)) continue;
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ osfd = bottom->secret->md.osfd;
+
+ if (FD_ISSET(osfd, &ex)) out_flags |= PR_POLL_EXCEPT;
+ else if (FD_ISSET(osfd, &rd) || FD_ISSET(osfd, &wt))
+ out_flags = pd->in_flags & (PR_POLL_READ | PR_POLL_WRITE);
+ pd->out_flags = out_flags;
+ if (out_flags) ready++;
+ }
+ /*
+ 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(ready > 0);*/
+ }
+ else PR_ASSERT(ready == 0);
+
+ return ready;
+}
+
+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..4af516cc
--- /dev/null
+++ b/pr/src/md/mac/macsockotpt.c
@@ -0,0 +1,1585 @@
+/* -*- 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);
+ break;
+
+ default:
+ PR_ASSERT(0);
+ break;
+ }
+}
+
+
+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;
+ }
+
+ if (opCode != kSTREAM_SEND && opCode != kSTREAM_RECEIVE) {
+ err = kEINVALErr;
+ goto ErrorExit;
+ }
+
+ while (bytesLeft > 0) {
+
+ PrepareThreadForAsyncIO(me, endpoint, osfd);
+
+ if (opCode == kSTREAM_SEND)
+ result = OTSnd(endpoint, buf, bytesLeft, NULL);
+ else
+ result = OTRcv(endpoint, buf, bytesLeft, NULL);
+
+ 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 */
+ me->io_pending = PR_FALSE;
+ 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) {
+ me->io_pending = PR_FALSE;
+ err = result;
+ goto ErrorExit;
+ } else if (fd->secret->nonblocking) {
+ me->io_pending = PR_FALSE;
+ err = result;
+ goto ErrorExit;
+ }
+ WaitOnThisThread(me, timeout);
+ me->io_pending = PR_FALSE;
+ 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;
+ }
+
+ if (opCode != kDGRAM_SEND && opCode != kDGRAM_RECEIVE) {
+ err = kEINVALErr;
+ 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
+ err = OTRcvUData(endpoint, &dgram, NULL);
+
+ 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);
+ me->io_pending = PR_FALSE;
+ 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)
+{
+ PRInt32 ready = 0;
+ PRPollDesc *pd, *epd;
+ PRIntervalTime sleepTime, timein;
+
+ sleepTime = PR_MillisecondsToInterval(5UL);
+ if (PR_INTERVAL_NO_TIMEOUT != timeout)
+ {
+ if (sleepTime > timeout) sleepTime = timeout;
+ timein = PR_IntervalNow();
+ }
+
+ do
+ {
+ for (pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ PRInt16 in_flags_read = 0, in_flags_write = 0;
+ PRInt16 out_flags_read = 0, out_flags_write = 0;
+
+ if (NULL == pd->fd || pd->in_flags == 0) continue;
+
+ if (pd->in_flags & PR_POLL_READ)
+ {
+ in_flags_read = (pd->fd->methods->poll)(
+ pd->fd, pd->in_flags & ~PR_POLL_WRITE, &out_flags_read);
+ }
+ if (pd->in_flags & PR_POLL_WRITE)
+ {
+ in_flags_write = (pd->fd->methods->poll)(
+ pd->fd, pd->in_flags & ~PR_POLL_READ, &out_flags_write);
+ }
+ if ((0 != (in_flags_read & out_flags_read))
+ || (0 != (in_flags_write & out_flags_write)))
+ {
+ ready += 1; /* some layer has buffer input */
+ pd->out_flags = out_flags_read | out_flags_write;
+ }
+ else
+ {
+ PRFileDesc *bottom;
+ EndpointRef endpoint;
+ PRBool readReady, writeReady, exceptReady;
+
+ pd->out_flags = 0; /* pre-condition */
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom);
+ if ((NULL != bottom)
+ && (_PR_FILEDESC_OPEN == bottom->secret->state))
+ {
+ endpoint = (EndpointRef)bottom->secret->md.osfd;
+
+ if (GetState(endpoint,
+ &readReady, &writeReady, &exceptReady))
+ {
+ if (readReady)
+ {
+ if (in_flags_read & PR_POLL_READ)
+ pd->out_flags |= PR_POLL_READ;
+ if (in_flags_write & PR_POLL_READ)
+ pd->out_flags |= PR_POLL_WRITE;
+ }
+ if (writeReady)
+ {
+ if (in_flags_read & PR_POLL_WRITE)
+ pd->out_flags |= PR_POLL_READ;
+ if (in_flags_write & PR_POLL_WRITE)
+ pd->out_flags |= PR_POLL_WRITE;
+ }
+ if (exceptReady && (pd->in_flags & PR_POLL_EXCEPT))
+ {
+ pd->out_flags |= PR_POLL_EXCEPT;
+ }
+ if (0 != pd->out_flags) ready++;
+ }
+ }
+ else
+ {
+ ready += 1; /* this will cause an abrupt return */
+ pd->out_flags = PR_POLL_NVAL; /* bogii */
+ }
+ }
+ }
+
+ if (ready > 0) return ready;
+
+ (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) {
+ me->io_pending = PR_FALSE;
+ me->md.osErrCode = err;
+ goto ErrorExit;
+ }
+
+ WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT);
+
+ if (me->md.osErrCode != 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:
+ 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;
+}
+
+
+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..26008768
--- /dev/null
+++ b/pr/src/md/mac/macthr.c
@@ -0,0 +1,380 @@
+/* -*- 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->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_MD_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_MD_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..1b5d2e7a
--- /dev/null
+++ b/pr/src/md/mac/mdmac.c
@@ -0,0 +1,840 @@
+/* -*- 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;
+ Str255 buffer;
+
+ va_start(ap, format);
+ buffer[0] = PR_vsnprintf((char *)buffer + 1, sizeof(buffer) - 1, format, ap);
+ va_end(ap);
+
+ DebugStr(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..7ff5fd27
--- /dev/null
+++ b/pr/src/md/os2/Makefile
@@ -0,0 +1,50 @@
+#
+# 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)
+
+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..95cedff4
--- /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 */
+ DosReleaseMutexSem(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);
+ DosReleaseMutexSem(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);
+
+ _MD_LOCK(lock);
+
+ 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 {
+ DosReleaseMutexSem(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..625df4c9
--- /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)(OS2TID, 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..b08f2155
--- /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;
+
+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;
+}
+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;
+ }
+}
+
+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() */
+
+PRStatus
+_PR_MD_TLOCKFILE(PRInt32 f)
+{
+ return _PR_MD_LOCKFILE(f);
+} /* end _PR_MD_TLOCKFILE() */
+
+
+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..153d595c
--- /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"
+
+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 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;
+}
+
+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..90195c64
--- /dev/null
+++ b/pr/src/md/os2/os2poll.c
@@ -0,0 +1,258 @@
+/* -*- 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)
+{
+ PRInt32 osfd;
+ int maxfd = -1;
+ fd_set rd, wt, ex;
+ PRFileDesc *bottom;
+ PRPollDesc *pd, *epd;
+ PRInt32 ready, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ struct timeval tv, *tvp = NULL;
+
+ /*
+ * 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;
+
+ if (_PR_PENDING_INTERRUPT(me))
+ {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+
+ /*
+ ** Is it an empty set? If so, just sleep for the timeout and return
+ */
+ if (0 == npds)
+ {
+ PR_Sleep(timeout);
+ return 0;
+ }
+
+ FD_ZERO(&rd);
+ FD_ZERO(&wt);
+ FD_ZERO(&ex);
+
+ ready = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ PRInt16 in_flags_read = 0, in_flags_write = 0;
+ PRInt16 out_flags_read = 0, out_flags_write = 0;
+
+ if ((NULL != pd->fd) && (0 != pd->in_flags))
+ {
+ if (pd->in_flags & PR_POLL_READ)
+ {
+ in_flags_read = (pd->fd->methods->poll)(
+ pd->fd, (PRInt16)(pd->in_flags & ~PR_POLL_WRITE),
+ &out_flags_read);
+ }
+ if (pd->in_flags & PR_POLL_WRITE)
+ {
+ in_flags_write = (pd->fd->methods->poll)(
+ pd->fd, (PRInt16)(pd->in_flags & ~PR_POLL_READ),
+ &out_flags_write);
+ }
+ if ((0 != (in_flags_read & out_flags_read))
+ || (0 != (in_flags_write & out_flags_write)))
+ {
+ /* this one's ready right now (buffered input) */
+ if (0 == ready)
+ {
+ /*
+ * We will have to return without calling the
+ * system poll/select function. So zero the
+ * out_flags fields of all the poll descriptors
+ * before this one.
+ */
+ PRPollDesc *prev;
+ for (prev = pds; prev < pd; prev++)
+ {
+ prev->out_flags = 0;
+ }
+ }
+ ready += 1;
+ pd->out_flags = out_flags_read | out_flags_write;
+ }
+ else
+ {
+ pd->out_flags = 0; /* pre-condition */
+ /* make sure this is an NSPR supported stack */
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom); /* what to do about that? */
+ if ((NULL != bottom)
+ && (_PR_FILEDESC_OPEN == bottom->secret->state))
+ {
+ if (0 == ready)
+ {
+ osfd = bottom->secret->md.osfd;
+ if (osfd > maxfd) maxfd = osfd;
+ if (in_flags_read & PR_POLL_READ)
+ {
+ pd->out_flags |= _PR_POLL_READ_SYS_READ;
+ FD_SET(osfd, &rd);
+ }
+ if (in_flags_read & PR_POLL_WRITE)
+ {
+ pd->out_flags |= _PR_POLL_READ_SYS_WRITE;
+ FD_SET(osfd, &wt);
+ }
+ if (in_flags_write & PR_POLL_READ)
+ {
+ pd->out_flags |= _PR_POLL_WRITE_SYS_READ;
+ FD_SET(osfd, &rd);
+ }
+ if (in_flags_write & PR_POLL_WRITE)
+ {
+ pd->out_flags |= _PR_POLL_WRITE_SYS_WRITE;
+ FD_SET(osfd, &wt);
+ }
+ if (pd->in_flags & PR_POLL_EXCEPT) FD_SET(osfd, &ex);
+ }
+ }
+ else
+ {
+ if (0 == ready)
+ {
+ PRPollDesc *prev;
+ for (prev = pds; prev < pd; prev++)
+ {
+ prev->out_flags = 0;
+ }
+ }
+ ready += 1; /* this will cause an abrupt return */
+ pd->out_flags = PR_POLL_NVAL; /* bogii */
+ }
+ }
+ }
+ }
+
+ if (0 != ready) return ready; /* no need to block */
+
+ remaining = timeout;
+ start = PR_IntervalNow();
+
+retry:
+ if (timeout != PR_INTERVAL_NO_TIMEOUT)
+ {
+ PRInt32 ticksPerSecond = PR_TicksPerSecond();
+ tv.tv_sec = remaining / ticksPerSecond;
+ tv.tv_usec = remaining - (ticksPerSecond * tv.tv_sec);
+ tv.tv_usec = (PR_USEC_PER_SEC * tv.tv_usec) / ticksPerSecond;
+ tvp = &tv;
+ }
+
+ ready = _MD_SELECT(maxfd + 1, &rd, &wt, &ex, tvp);
+ if (ready == -1 && errno == EINTR)
+ {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) goto retry;
+ else
+ {
+ elapsed = (PRIntervalTime) (PR_IntervalNow() - start);
+ if (elapsed > timeout) ready = 0; /* timed out */
+ else
+ {
+ remaining = timeout - elapsed;
+ goto retry;
+ }
+ }
+ }
+
+ if (ready > 0)
+ {
+ ready = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ PRInt16 out_flags = 0;
+ if ((NULL != pd->fd) && (0 != pd->in_flags))
+ {
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom);
+
+ osfd = bottom->secret->md.osfd;
+
+ if (FD_ISSET(osfd, &rd))
+ {
+ if (pd->out_flags & _PR_POLL_READ_SYS_READ)
+ out_flags |= PR_POLL_READ;
+ if (pd->out_flags & _PR_POLL_WRITE_SYS_READ)
+ out_flags |= PR_POLL_WRITE;
+ }
+ if (FD_ISSET(osfd, &wt))
+ {
+ if (pd->out_flags & _PR_POLL_READ_SYS_WRITE)
+ out_flags |= PR_POLL_READ;
+ if (pd->out_flags & _PR_POLL_WRITE_SYS_WRITE)
+ out_flags |= PR_POLL_WRITE;
+ }
+ if (FD_ISSET(osfd, &ex)) out_flags |= PR_POLL_EXCEPT;
+ }
+ pd->out_flags = out_flags;
+ if (out_flags) ready++;
+ }
+ PR_ASSERT(ready > 0);
+ }
+ else if (ready < 0)
+ {
+ err = _MD_ERRNO();
+ if (err == EBADF)
+ {
+ /* Find the bad fds */
+ ready = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ int optval;
+ int optlen = sizeof(optval);
+ pd->out_flags = 0;
+ if ((NULL == pd->fd) || (pd->in_flags == 0)) continue;
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ 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;
+ ready++;
+ }
+ }
+ }
+ PR_ASSERT(ready > 0);
+ }
+ else
+ {
+ PR_ASSERT(err != EINTR); /* should have been handled above */
+ _PR_MD_MAP_SELECT_ERROR(err);
+ }
+ }
+ return ready;
+ }
+
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..3be132f5
--- /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)(OS2TID, 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 = (OS2TID) _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..326b3536
--- /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"
+
+#ifdef XP_UNIX
+#include <unistd.h>
+#endif
+#ifdef SUNOS4
+#include "md/sunos4.h"
+#endif
+#ifdef _WIN32
+#include <windows.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 || defined NETBSD || defined OPENBSD || defined RHAPSODY
+ _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..7d36371c
--- /dev/null
+++ b/pr/src/md/unix/Makefile
@@ -0,0 +1,265 @@
+#
+# 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 \
+ uxpoll.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)
+
+NETBSD_CSRCS = \
+ netbsd.c \
+ $(NULL)
+
+OPENBSD_CSRCS = \
+ openbsd.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)
+
+RHAPSODY_CSRCS = \
+ rhapsody.c \
+ $(NULL)
+
+NEC_CSRCS = \
+ nec.c \
+ $(NULL)
+
+SONY_CSRCS = \
+ sony.c \
+ $(NULL)
+
+NCR_CSRCS = \
+ ncr.c \
+ $(NULL)
+
+SCOOS_CSRCS = \
+ scoos.c \
+ $(NULL)
+
+DGUX_CSRCS = \
+ dgux.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),NetBSD)
+CSRCS += $(NETBSD_CSRCS)
+endif
+ifeq ($(OS_ARCH),OpenBSD)
+CSRCS += $(OPENBSD_CSRCS)
+endif
+ifeq ($(OS_ARCH),BSD_OS)
+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),SINIX)
+CSRCS += $(RELIANTUNIX_CSRCS)
+endif
+ifeq ($(OS_ARCH),Rhapsody)
+CSRCS += $(RHAPSODY_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),SCOOS)
+CSRCS += $(SCOOS_CSRCS)
+endif
+ifeq ($(OS_ARCH),DGUX)
+CSRCS += $(DGUX_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 ($(CPU_ARCH),x86)
+ ASFILES = os_$(OS_ARCH)_x86.s
+ else
+ ifneq ($(OS_RELEASE),4.1.3_U1)
+ ifneq ($(LOCAL_THREADS_ONLY),1)
+ ASFILES = os_$(OS_ARCH).s
+ endif
+ endif
+ endif
+endif
+
+ifeq ($(OS_ARCH), SINIX)
+ ASFILES = os_ReliantUNIX.s
+endif
+
+ifeq ($(OS_ARCH)$(OS_RELEASE),BSD_OS2.1)
+ ASFILES = os_BSD_386_2.s
+endif
+
+ifeq ($(OS_ARCH), IRIX)
+ ASFILES = os_Irix.s
+endif
+
+TARGETS = $(OBJS)
+
+ifeq ($(OS_ARCH),AIX)
+ifneq ($(OS_RELEASE),4.2)
+ifneq ($(USE_PTHREADS), 1)
+#TARGETS += $(OBJDIR)/aixwrap.$(OBJ_SUFFIX)
+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=.$(OBJ_SUFFIX)))
+ TARGETS += $(ULTRASPARC_ASOBJS) $(SHARED_LIBRARY)
+ RELEASE_LIBS = $(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.$(OBJ_SUFFIX) $(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)
+ /usr/ccs/bin/as -o $@ -K PIC -P -D_ASM -D__STDC__=0 -xarch=v8plus $<
+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..524346e8
--- /dev/null
+++ b/pr/src/md/unix/aix.c
@@ -0,0 +1,301 @@
+/* -*- 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/atomic_op.h>
+
+PRInt32 _AIX_AtomicSet(PRInt32 *val, PRInt32 newval)
+{
+ PRIntn oldval;
+ boolean_t stored;
+ oldval = fetch_and_add((atomic_p)val, 0);
+ do
+ {
+ stored = compare_and_swap((atomic_p)val, &oldval, newval);
+ } while (!stored);
+ return oldval;
+} /* _AIX_AtomicSet */
+
+#if defined(AIX_TIMERS)
+
+#include <sys/time.h>
+
+static PRUint32 _aix_baseline_epoch;
+
+static void _MD_AixIntervalInit(void)
+{
+ timebasestruct_t real_time;
+ read_real_time(&real_time, TIMEBASE_SZ);
+ (void)time_base_to_time(&real_time, TIMEBASE_SZ);
+ _aix_baseline_epoch = real_time.tb_high;
+} /* _MD_AixIntervalInit */
+
+PRIntervalTime _MD_AixGetInterval(void)
+{
+ PRIntn rv;
+ PRUint64 temp;
+ timebasestruct_t real_time;
+ read_real_time(&real_time, TIMEBASE_SZ);
+ (void)time_base_to_time(&real_time, TIMEBASE_SZ);
+ /* tb_high is in seconds, tb_low in 10(-9)seconds */
+ temp = 1000000000ULL * (PRUint64)(real_time.tb_high - _aix_baseline_epoch);
+ temp += (PRUint64)real_time.tb_low; /* everything's 10(-9) seconds */
+ temp >>= 16; /* now it's something way different */
+ return (PRIntervalTime)temp;
+} /* _MD_AixGetInterval */
+
+PRIntervalTime _MD_AixIntervalPerSec(void)
+{
+ return 1000000000ULL >> 16; /* that's 15258, I think */
+} /* _MD_AixIntervalPerSec */
+
+#endif /* defined(AIX_TIMERS) */
+
+#if !defined(PTHREADS_USER)
+
+#if defined(_PR_PTHREADS)
+
+/*
+ * AIX 4.3 has sched_yield(). AIX 4.2 has pthread_yield().
+ * So we look up the appropriate function pointer at run time.
+ */
+
+#include <dlfcn.h>
+
+int (*_PT_aix_yield_fcn)() = NULL;
+
+void _MD_EarlyInit(void)
+{
+ void *main_app_handle;
+
+ main_app_handle = dlopen(NULL, RTLD_NOW);
+ PR_ASSERT(NULL != main_app_handle);
+
+ _PT_aix_yield_fcn = (int(*)())dlsym(main_app_handle, "sched_yield");
+ if (!_PT_aix_yield_fcn) {
+ _PT_aix_yield_fcn = (int(*)())dlsym(main_app_handle,"pthread_yield");
+ PR_ASSERT(NULL != _PT_aix_yield_fcn);
+ }
+ dlclose(main_app_handle);
+
+#if defined(AIX_TIMERS)
+ _MD_AixIntervalInit();
+#endif
+}
+
+#else /* _PR_PTHREADS */
+
+void _MD_EarlyInit(void)
+{
+#if defined(AIX_TIMERS)
+ _MD_AixIntervalInit();
+#endif
+}
+
+#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
+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 */
+
+/*
+ * NSPR 2.0 overrides the system select() and poll() functions.
+ * On AIX 4.2, we use dlopen("/unix", RTLD_NOW) and dlsym() to get
+ * at the original system select() and poll() functions.
+ */
+
+#if !defined(AIX4_1)
+
+#include <sys/select.h>
+#include <sys/poll.h>
+#include <dlfcn.h>
+
+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) {
+ void *aix_handle;
+
+ aix_handle = dlopen("/unix", RTLD_NOW);
+ if (!aix_handle) {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return -1;
+ }
+ aix_select_fcn = (int(*)())dlsym(aix_handle,"select");
+ dlclose(aix_handle);
+ 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) {
+ void *aix_handle;
+
+ aix_handle = dlopen("/unix", RTLD_NOW);
+ if (!aix_handle) {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return -1;
+ }
+ aix_poll_fcn = (int(*)())dlsym(aix_handle,"poll");
+ dlclose(aix_handle);
+ 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 /* !defined(AIX4_1) */
+
+#ifdef _PR_HAVE_ATOMIC_CAS
+
+#include "pratom.h"
+
+#define _PR_AIX_ATOMIC_LOCK -1
+
+PR_IMPLEMENT(void)
+PR_StackPush(PRStack *stack, PRStackElem *stack_elem)
+{
+PRStackElem *addr;
+boolean_t locked = TRUE;
+
+ do {
+ while ((addr = stack->prstk_head.prstk_elem_next) ==
+ (PRStackElem *)_PR_AIX_ATOMIC_LOCK)
+ ;
+ locked = _check_lock((atomic_p) &stack->prstk_head.prstk_elem_next,
+ (int) addr, _PR_AIX_ATOMIC_LOCK);
+ } while (locked == TRUE);
+ stack_elem->prstk_elem_next = addr;
+ _clear_lock((atomic_p)&stack->prstk_head.prstk_elem_next, (int)stack_elem);
+ return;
+}
+
+PR_IMPLEMENT(PRStackElem *)
+PR_StackPop(PRStack *stack)
+{
+PRStackElem *element;
+boolean_t locked = TRUE;
+
+ do {
+ while ((element = stack->prstk_head.prstk_elem_next) ==
+ (PRStackElem *) _PR_AIX_ATOMIC_LOCK)
+ ;
+ locked = _check_lock((atomic_p) &stack->prstk_head.prstk_elem_next,
+ (int)element, _PR_AIX_ATOMIC_LOCK);
+ } while (locked == TRUE);
+
+ if (element == NULL) {
+ _clear_lock((atomic_p) &stack->prstk_head.prstk_elem_next, NULL);
+ } else {
+ _clear_lock((atomic_p) &stack->prstk_head.prstk_elem_next,
+ (int) element->prstk_elem_next);
+ }
+ return element;
+}
+
+#endif /* _PR_HAVE_ATOMIC_CAS */
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/darwin.c b/pr/src/md/unix/darwin.c
new file mode 100644
index 00000000..01a73650
--- /dev/null
+++ b/pr/src/md/unix/darwin.c
@@ -0,0 +1,99 @@
+/* -*- 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 !defined(_PR_PTHREADS)
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+#else
+ *np = 0;
+ return NULL;
+#endif
+}
+
+#if !defined(_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 Rhapsody */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for Rhapsody.");
+}
+
+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 Rhapsody.");
+ return PR_FAILURE;
+}
+#endif /* ! _PR_PTHREADS */
+
+/*
+** Whoops, we don't have a syscall stub for this
+*/
+int mprotect (caddr_t addr, size_t size, int prot)
+{
+ return -1;
+}
+
+/* rhapsody.c */
+
diff --git a/pr/src/md/unix/dgux.c b/pr/src/md/unix/dgux.c
new file mode 100644
index 00000000..5750792d
--- /dev/null
+++ b/pr/src/md/unix/dgux.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"
+
+/*
+ * using only NSPR threads here
+ *
+ * Copied from the UnixWare implementation. Should be kept in sync
+ * with ../../../include/md/_dgux.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);
+}
+
+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 DG/UX */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for DG/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 DG/UX.");
+}
+
diff --git a/pr/src/md/unix/freebsd.c b/pr/src/md/unix/freebsd.c
new file mode 100644
index 00000000..baaf0619
--- /dev/null
+++ b/pr/src/md/unix/freebsd.c
@@ -0,0 +1,100 @@
+/* -*- 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 FreeBSD */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for FreeBSD.");
+}
+
+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 FreeBSD.");
+ return PR_FAILURE;
+}
+#endif /* ! _PR_PTHREADS */
diff --git a/pr/src/md/unix/hpux.c b/pr/src/md/unix/hpux.c
new file mode 100644
index 00000000..e0328bb9
--- /dev/null
+++ b/pr/src/md/unix/hpux.c
@@ -0,0 +1,242 @@
+/* -*- 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 <setjmp.h>
+
+#if defined(HPUX_LW_TIMER)
+
+#include <machine/inline.h>
+#include <machine/clock.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/pstat.h>
+
+int __lw_get_thread_times(int which, int64_t *sample, int64_t *time);
+
+static double msecond_per_itick;
+
+void _PR_HPUX_LW_IntervalInit(void)
+{
+ struct pst_processor psp;
+ int iticksperclktick, clk_tck;
+ int rv;
+
+ rv = pstat_getprocessor(&psp, sizeof(psp), 1, 0);
+ PR_ASSERT(rv != -1);
+
+ iticksperclktick = psp.psp_iticksperclktick;
+ clk_tck = sysconf(_SC_CLK_TCK);
+ msecond_per_itick = (1000.0)/(double)(iticksperclktick * clk_tck);
+}
+
+PRIntervalTime _PR_HPUX_LW_GetInterval(void)
+{
+ int64_t time, sample;
+
+ __lw_get_thread_times(1, &sample, &time);
+ /*
+ * Division is slower than float multiplication.
+ * return (time / iticks_per_msecond);
+ */
+ return (time * msecond_per_itick);
+}
+#endif /* HPUX_LW_TIMER */
+
+#if !defined(PTHREADS_USER)
+
+void _MD_EarlyInit(void)
+{
+#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 */
+
+/*
+ * 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..2432838d
--- /dev/null
+++ b/pr/src/md/unix/irix.c
@@ -0,0 +1,1657 @@
+/* -*- 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>
+#include <dlfcn.h>
+
+static void _MD_IrixIntervalInit(void);
+
+#if defined(_PR_PTHREADS)
+/*
+ * for compatibility with classic nspr
+ */
+void _PR_IRIX_CHILD_PROCESS()
+{
+}
+#else /* defined(_PR_PTHREADS) */
+
+static void irix_detach_sproc(void);
+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;
+
+PRCList sproc_list = PR_INIT_STATIC_CLIST(&sproc_list);
+ulock_t sproc_list_lock;
+
+typedef struct sproc_data {
+ void (*entry) (void *, size_t);
+ unsigned inh;
+ void *arg;
+ caddr_t sp;
+ size_t len;
+ int *pid;
+ int creator_pid;
+} sproc_data;
+
+typedef struct sproc_params {
+ PRCList links;
+ sproc_data sd;
+} sproc_params;
+
+#define SPROC_PARAMS_PTR(qp) \
+ ((sproc_params *) ((char*) (qp) - offsetof(sproc_params , links)))
+
+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;
+PRInt32 _pr_irix_process_exit_code = 0; /* exit code for PR_ProcessExit */
+PRInt32 _pr_irix_process_exit = 0; /* process exiting due to call to
+ PR_ProcessExit */
+
+int _pr_irix_primoridal_cpu_fd[2] = { -1, -1 };
+static void (*libc_exit)(int) = NULL;
+static void *libc_handle = NULL;
+
+#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 = _MD_GET_ATTACHED_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 = _MD_GET_ATTACHED_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 = _MD_GET_ATTACHED_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 = _MD_GET_ATTACHED_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 = _MD_GET_ATTACHED_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() == get_pid())
+ return _MD_THIS_THREAD();
+ else
+ return 0;
+}
+
+/*
+ * _MD_get_current_thread
+ * Return the thread pointer of the current thread (attaching it if
+ * necessary)
+ */
+PRThread *_MD_get_current_thread(void)
+{
+PRThread *me;
+
+ me = _MD_GET_ATTACHED_THREAD();
+ if (NULL == me) {
+ me = _PRI_AttachThread(
+ PR_USER_THREAD, PR_PRIORITY_NORMAL, NULL, 0);
+ }
+ PR_ASSERT(me != NULL);
+ return(me);
+}
+
+/*
+ * irix_detach_sproc
+ * auto-detach a sproc when it exits
+ */
+void irix_detach_sproc(void)
+{
+PRThread *me;
+
+ me = _MD_GET_ATTACHED_THREAD();
+ if ((me != NULL) && (me->flags & _PR_ATTACHED)) {
+ _PRI_DetachThread();
+ }
+}
+
+
+PRStatus _MD_NEW_LOCK(struct _MDLock *lockp)
+{
+ PRStatus rv;
+ PRIntn is;
+ PRThread *me = _MD_GET_ATTACHED_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);
+ _nspr_irix_lock_cnt++;
+
+ arena_list_lock = usnewlock(_pr_usArena);
+ _nspr_irix_lock_cnt++;
+
+ sproc_list_lock = usnewlock(_pr_usArena);
+ _nspr_irix_lock_cnt++;
+
+ _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()
+{
+extern PRUint32 _pr_global_threads;
+
+ PR_ASSERT(_PR_MD_CURRENT_CPU() == _pr_primordialCPU);
+ PR_ASSERT(_pr_numCPU == 1);
+ PR_ASSERT(_pr_global_threads == 0);
+ /*
+ * save the new pid
+ */
+ _pr_primordialCPU->md.id = getpid();
+ _MD_SET_SPROC_PID(getpid());
+}
+
+static PRStatus pr_cvar_wait_sem(PRThread *thread, PRIntervalTime timeout)
+{
+ int rv;
+
+#ifdef _PR_USE_POLL
+ struct pollfd pfd;
+ int msecs;
+
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ msecs = -1;
+ else
+ msecs = PR_IntervalToMilliseconds(timeout);
+#else
+ struct timeval tv, *tvp;
+ fd_set 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;
+ }
+ FD_ZERO(&rd);
+ FD_SET(thread->md.cvar_pollsemfd, &rd);
+#endif
+
+ /*
+ * 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) {
+#ifdef _PR_USE_POLL
+ pfd.events = POLLIN;
+ pfd.fd = thread->md.cvar_pollsemfd;
+ rv = _MD_POLL(&pfd, 1, msecs);
+#else
+ rv = _MD_SELECT(thread->md.cvar_pollsemfd + 1, &rd, NULL,NULL,tvp);
+#endif
+ 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();
+ 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;
+}
+
+void create_sproc (void (*entry) (void *, size_t), unsigned inh,
+ void *arg, caddr_t sp, size_t len, int *pid)
+{
+sproc_params sparams;
+char data;
+int rv;
+PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (!_PR_IS_NATIVE_THREAD(me) && (_PR_MD_CURRENT_CPU()->id == 0)) {
+ *pid = sprocsp(entry, /* startup func */
+ inh, /* attribute flags */
+ arg, /* thread param */
+ sp, /* stack address */
+ len); /* stack size */
+ } else {
+ sparams.sd.entry = entry;
+ sparams.sd.inh = inh;
+ sparams.sd.arg = arg;
+ sparams.sd.sp = sp;
+ sparams.sd.len = len;
+ sparams.sd.pid = pid;
+ sparams.sd.creator_pid = getpid();
+ _PR_LOCK(sproc_list_lock);
+ PR_APPEND_LINK(&sparams.links, &sproc_list);
+ rv = write(_pr_irix_primoridal_cpu_fd[1], &data, 1);
+ PR_ASSERT(rv == 1);
+ _PR_UNLOCK(sproc_list_lock);
+ blockproc(getpid());
+ }
+}
+
+/*
+ * _PR_MD_WAKEUP_PRIMORDIAL_CPU
+ *
+ * wakeup cpu 0
+ */
+
+void _PR_MD_WAKEUP_PRIMORDIAL_CPU()
+{
+char data = '0';
+int rv;
+
+ rv = write(_pr_irix_primoridal_cpu_fd[1], &data, 1);
+ PR_ASSERT(rv == 1);
+}
+
+/*
+ * _PR_MD_primordial_cpu
+ *
+ * process events that need to executed by the primordial cpu on each
+ * iteration through the idle loop
+ */
+
+void _PR_MD_primordial_cpu()
+{
+PRCList *qp;
+sproc_params *sp;
+int pid;
+
+ _PR_LOCK(sproc_list_lock);
+ while ((qp = sproc_list.next) != &sproc_list) {
+ sp = SPROC_PARAMS_PTR(qp);
+ PR_REMOVE_LINK(&sp->links);
+ pid = sp->sd.creator_pid;
+ (*(sp->sd.pid)) = sprocsp(sp->sd.entry, /* startup func */
+ sp->sd.inh, /* attribute flags */
+ sp->sd.arg, /* thread param */
+ sp->sd.sp, /* stack address */
+ sp->sd.len); /* stack size */
+ unblockproc(pid);
+ }
+ _PR_UNLOCK(sproc_list_lock);
+}
+
+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;
+
+ thread->md.cvar_pollsemfd = -1;
+ if (new_poll_sem(&thread->md,0) == PR_FAILURE) {
+ if (!_PR_IS_NATIVE_THREAD(me))
+ _PR_FAST_INTSON(is);
+ return 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 (!_PR_IS_NATIVE_THREAD(me))
+ _PR_FAST_INTSON(is);
+ return PR_FAILURE;
+ }
+
+ create_sproc(spentry, /* startup func */
+ PR_SALL, /* attribute flags */
+ (void *)thread, /* thread param */
+ NULL, /* stack address */
+ stackSize, &pid); /* stack size */
+ if (pid > 0) {
+ _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 {
+ close(thread->md.cvar_pollsemfd);
+ thread->md.cvar_pollsemfd = -1;
+ free_poll_sem(&thread->md);
+ thread->md.cvar_pollsem = NULL;
+ _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_InitAttachedThread(PRThread *thread, PRBool wakeup_parent)
+{
+ PRStatus rv = PR_SUCCESS;
+
+ if (thread->flags & _PR_GLOBAL_SCOPE) {
+ if (new_poll_sem(&thread->md,0) == PR_FAILURE) {
+ return 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);
+ return PR_FAILURE;
+ }
+ if (_MD_InitThread(thread, PR_FALSE) == PR_FAILURE) {
+ close(thread->md.cvar_pollsemfd);
+ thread->md.cvar_pollsemfd = -1;
+ free_poll_sem(&thread->md);
+ thread->md.cvar_pollsem = NULL;
+ return PR_FAILURE;
+ }
+ }
+ return rv;
+}
+
+PRStatus
+_MD_InitThread(PRThread *thread, PRBool wakeup_parent)
+{
+ struct sigaction sigact;
+ PRStatus rv = PR_SUCCESS;
+
+ if (thread->flags & _PR_GLOBAL_SCOPE) {
+ thread->md.id = getpid();
+ 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
+ */
+ 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);
+
+
+ /*
+ * 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 and for reaping sprocs
+ */
+ sigact.sa_handler = sigchld_handler;
+ sigact.sa_flags = SA_RESTART;
+ sigact.sa_mask = ints_off;
+ sigaction(SIGCLD, &sigact, NULL);
+ }
+ return rv;
+}
+
+/*
+ * PR_Cleanup should be executed on the primordial sproc; migrate the thread
+ * to the primordial cpu
+ */
+
+void _PR_MD_PRE_CLEANUP(PRThread *me)
+{
+PRIntn is;
+_PRCPU *cpu = _pr_primordialCPU;
+
+ PR_ASSERT(cpu);
+
+ me->flags |= _PR_BOUND_THREAD;
+
+ if (me->cpu->id != 0) {
+ _PR_INTSOFF(is);
+ _PR_RUNQ_LOCK(cpu);
+ me->cpu = cpu;
+ me->state = _PR_RUNNABLE;
+ _PR_ADD_RUNQ(me, cpu, me->priority);
+ _PR_RUNQ_UNLOCK(cpu);
+ _MD_Wakeup_CPUs();
+
+ _PR_MD_SWITCH_CONTEXT(me);
+
+ _PR_FAST_INTSON(is);
+ PR_ASSERT(me->cpu->id == 0);
+ }
+}
+
+/*
+ * process exiting
+ */
+PR_EXTERN(void ) _MD_exit(PRIntn status)
+{
+PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ /*
+ * the exit code of the process is the exit code of the primordial
+ * sproc
+ */
+ if (!_PR_IS_NATIVE_THREAD(me) && (_PR_MD_CURRENT_CPU()->id == 0)) {
+ /*
+ * primordial sproc case: call _exit directly
+ * Cause SIGKILL to be sent to other sprocs
+ */
+ prctl(PR_SETEXITSIG, SIGKILL);
+ _exit(status);
+ } else {
+ int rv;
+ char data;
+ sigset_t set;
+
+ /*
+ * non-primordial sproc case: cause the primordial sproc, cpu 0,
+ * to wakeup and call _exit
+ */
+ _pr_irix_process_exit = 1;
+ _pr_irix_process_exit_code = status;
+ rv = write(_pr_irix_primoridal_cpu_fd[1], &data, 1);
+ PR_ASSERT(rv == 1);
+ /*
+ * block all signals and wait for SIGKILL to terminate this sproc
+ */
+ sigfillset(&set);
+ sigsuspend(&set);
+ /*
+ * this code doesn't (shouldn't) execute
+ */
+ prctl(PR_SETEXITSIG, SIGKILL);
+ _exit(status);
+ }
+}
+
+/*
+ * Override the exit() function in libc to cause the process to exit
+ * when the primodial/main nspr thread calls exit. Calls to exit by any
+ * other thread simply result in a call to the exit function in libc.
+ * The exit code of the process is the exit code of the primordial
+ * sproc.
+ */
+
+void exit(int status)
+{
+PRThread *me, *thr;
+PRCList *qp;
+
+ if (!_pr_initialized) {
+ if (!libc_exit) {
+
+ if (!libc_handle)
+ libc_handle = dlopen("libc.so",RTLD_NOW);
+ if (libc_handle)
+ libc_exit = (void (*)(int)) dlsym(libc_handle, "exit");
+ }
+ if (libc_exit)
+ (*libc_exit)(status);
+ else
+ _exit(status);
+ }
+
+ me = _PR_MD_CURRENT_THREAD();
+
+ if (me == NULL) /* detached thread */
+ (*libc_exit)(status);
+
+ PR_ASSERT(_PR_IS_NATIVE_THREAD(me) ||
+ (_PR_MD_CURRENT_CPU())->id == me->cpu->id);
+
+ if (me->flags & _PR_PRIMORDIAL) {
+
+ me->flags |= _PR_BOUND_THREAD;
+
+ PR_ASSERT((_PR_MD_CURRENT_CPU())->id == me->cpu->id);
+ if (me->cpu->id != 0) {
+ _PRCPU *cpu = _pr_primordialCPU;
+ PRIntn is;
+
+ _PR_INTSOFF(is);
+ _PR_RUNQ_LOCK(cpu);
+ me->cpu = cpu;
+ me->state = _PR_RUNNABLE;
+ _PR_ADD_RUNQ(me, cpu, me->priority);
+ _PR_RUNQ_UNLOCK(cpu);
+ _MD_Wakeup_CPUs();
+
+ _PR_MD_SWITCH_CONTEXT(me);
+
+ _PR_FAST_INTSON(is);
+ }
+
+ PR_ASSERT((_PR_MD_CURRENT_CPU())->id == 0);
+
+ if (prctl(PR_GETNSHARE) > 1) {
+#define SPROC_EXIT_WAIT_TIME 5
+ int sleep_cnt = SPROC_EXIT_WAIT_TIME;
+
+ /*
+ * sprocs still running; caue cpus and recycled global threads
+ * to exit
+ */
+ _pr_irix_exit_now = 1;
+ if (_pr_numCPU > 1) {
+ _MD_Wakeup_CPUs();
+ }
+ _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;
+ }
+ }
+
+ while (sleep_cnt-- > 0) {
+ if (waitpid(0, NULL, WNOHANG) >= 0)
+ sleep(1);
+ else
+ break;
+ }
+ prctl(PR_SETEXITSIG, SIGKILL);
+ }
+ (*libc_exit)(status);
+ } else {
+ /*
+ * non-primordial thread; simply call exit in libc.
+ */
+ (*libc_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];
+#ifndef _PR_USE_POLL
+ FD_SET(_pr_md_pipefd[0], &_PR_FD_READ_SET(cpu));
+#endif
+ }
+}
+
+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)
+{
+ PR_ASSERT((thread->flags & _PR_GLOBAL_SCOPE) &&
+ (thread->flags & _PR_GCABLE_THREAD));
+ (void)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 = (struct pda_stat*)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"
+};
+
+void _PR_List_Threads()
+{
+ PRThread *thr;
+ void *handle;
+ struct _PRCPU *cpu;
+ PRCList *qp;
+ int len, 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;
+ extern int __ateachexit(void (*func)(void));
+
+ 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 = (char*)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);
+ }
+ _MD_SET_SPROC_PID(getpid());
+ close(fd);
+ __ateachexit(irix_detach_sproc);
+#endif
+ _MD_IrixIntervalInit();
+} /* _MD_EarlyInit */
+
+void _MD_IrixInit()
+{
+#if !defined(_PR_PTHREADS)
+ struct sigaction sigact;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ int rv;
+
+#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
+ */
+ /*
+ * 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;
+
+ rv = pipe(_pr_irix_primoridal_cpu_fd);
+ PR_ASSERT(rv == 0);
+#ifndef _PR_USE_POLL
+ _PR_IOQ_MAX_OSFD(me->cpu) = _pr_irix_primoridal_cpu_fd[0];
+ FD_SET(_pr_irix_primoridal_cpu_fd[0], &_PR_FD_READ_SET(me->cpu));
+#endif
+
+ libc_handle = dlopen("libc.so",RTLD_NOW);
+ PR_ASSERT(libc_handle != NULL);
+ libc_exit = (void (*)(int)) dlsym(libc_handle, "exit");
+ PR_ASSERT(libc_exit != NULL);
+ /* dlclose(libc_handle); */
+
+#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 PRIntn 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);
+
+static void _MD_IrixIntervalInit()
+{
+ /*
+ * 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));
+ }
+
+} /* _MD_IrixIntervalInit */
+
+PRIntervalTime _MD_IrixIntervalPerSec()
+{
+ return pr_clock_granularity;
+}
+
+PRIntervalTime _MD_IrixGetInterval()
+{
+ 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_IrixGetInterval */
+
diff --git a/pr/src/md/unix/linux.c b/pr/src/md/unix/linux.c
new file mode 100644
index 00000000..ac880597
--- /dev/null
+++ b/pr/src/md/unix/linux.c
@@ -0,0 +1,104 @@
+/* -*- 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)
+{
+ /*
+ * set the pointers to the stack-pointer and frame-pointer words in the
+ * context structure; this is for debugging use.
+ */
+ thread->md.sp = _MD_GET_SP_PTR(thread);
+ thread->md.fp = _MD_GET_FP_PTR(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 Linux */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for Linux.");
+}
+
+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 Linux.");
+ return PR_FAILURE;
+}
+#endif /* ! _PR_PTHREADS */
diff --git a/pr/src/md/unix/ncr.c b/pr/src/md/unix/ncr.c
new file mode 100644
index 00000000..a22a3b96
--- /dev/null
+++ b/pr/src/md/unix/ncr.c
@@ -0,0 +1,376 @@
+/* -*- 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_ADD(PRInt32 *ptr, PRInt32 val)
+{
+ flockfile(_uw_semf);
+ (*ptr) += 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/netbsd.c b/pr/src/md/unix/netbsd.c
new file mode 100644
index 00000000..973ac1b8
--- /dev/null
+++ b/pr/src/md/unix/netbsd.c
@@ -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.
+ */
+
+#include "primpl.h"
+
+#include <signal.h>
+#include <poll.h>
+#include <sys/syscall.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 NetBSD */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for NetBSD.");
+}
+
+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 NetBSD.");
+ return PR_FAILURE;
+}
+#endif /* ! _PR_PTHREADS */
diff --git a/pr/src/md/unix/objs.mk b/pr/src/md/unix/objs.mk
new file mode 100644
index 00000000..1d321324
--- /dev/null
+++ b/pr/src/md/unix/objs.mk
@@ -0,0 +1,211 @@
+#
+# 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 \
+ uxpoll.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)
+
+NETBSD_CSRCS = \
+ netbsd.c \
+ $(NULL)
+
+OPENBSD_CSRCS = \
+ openbsd.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)
+
+RHAPSODY_CSRCS = \
+ rhapsody.c \
+ $(NULL)
+
+NEC_CSRCS = \
+ nec.c \
+ $(NULL)
+
+SONY_CSRCS = \
+ sony.c \
+ $(NULL)
+
+NCR_CSRCS = \
+ ncr.c \
+ $(NULL)
+
+SCOOS_CSRCS = \
+ scoos.c \
+ $(NULL)
+
+DGUX_CSRCS = \
+ dgux.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),NetBSD)
+CSRCS += $(NETBSD_CSRCS)
+endif
+ifeq ($(OS_ARCH),OpenBSD)
+CSRCS += $(OPENBSD_CSRCS)
+endif
+ifeq ($(OS_ARCH),BSD_OS)
+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),SINIX)
+CSRCS += $(RELIANTUNIX_CSRCS)
+endif
+ifeq ($(OS_ARCH),Rhapsody)
+CSRCS += $(RHAPSODY_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),SCOOS)
+CSRCS += $(SCOOS_CSRCS)
+endif
+ifeq ($(OS_ARCH),DGUX)
+CSRCS += $(DGUX_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 ($(CPU_ARCH),x86)
+ ASFILES = os_$(OS_ARCH)_x86.s
+ else
+ ifneq ($(OS_RELEASE),4.1.3_U1)
+ ifneq ($(LOCAL_THREADS_ONLY),1)
+ ASFILES = os_$(OS_ARCH).s
+ endif
+ endif
+ endif
+endif
+
+ifeq ($(OS_ARCH), SINIX)
+ ASFILES = os_ReliantUNIX.s
+endif
+
+ifeq ($(OS_ARCH), IRIX)
+ ASFILES = os_Irix.s
+endif
+
+ifeq ($(OS_ARCH)$(OS_RELEASE),BSD_OS2.1)
+ ASFILES = os_BSD_386_2.s
+endif
+
+OBJS += $(addprefix md/unix/$(OBJDIR)/,$(CSRCS:.c=.$(OBJ_SUFFIX))) \
+ $(addprefix md/unix/$(OBJDIR)/,$(ASFILES:.s=.$(OBJ_SUFFIX)))
+
diff --git a/pr/src/md/unix/openbsd.c b/pr/src/md/unix/openbsd.c
new file mode 100644
index 00000000..1ad32d9f
--- /dev/null
+++ b/pr/src/md/unix/openbsd.c
@@ -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.
+ */
+
+#include "primpl.h"
+
+#include <signal.h>
+#include <poll.h>
+#include <sys/syscall.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 OpenBSD */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for OpenBSD.");
+}
+
+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 OpenBSD.");
+ return PR_FAILURE;
+}
+#endif /* ! _PR_PTHREADS */
diff --git a/pr/src/md/unix/os_AIX.s b/pr/src/md/unix/os_AIX.s
new file mode 100644
index 00000000..48a1f5b1
--- /dev/null
+++ b/pr/src/md/unix/os_AIX.s
@@ -0,0 +1,103 @@
+# -*- 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..a47c4a4e
--- /dev/null
+++ b/pr/src/md/unix/os_BSD_386_2.s
@@ -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.
+ */
+
+/*
+ * 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_Irix.s b/pr/src/md/unix/os_Irix.s
new file mode 100644
index 00000000..5d92fb67
--- /dev/null
+++ b/pr/src/md/unix/os_Irix.s
@@ -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.
+ */
+
+/*
+ * Atomically add a new element to the top of the stack
+ *
+ * usage : PR_StackPush(listp, elementp);
+ * -----------------------
+ */
+
+#include "md/_irix.h"
+#ifdef _PR_HAVE_ATOMIC_CAS
+
+#include <sys/asm.h>
+#include <sys/regdef.h>
+
+LEAF(PR_StackPush)
+
+retry_push:
+.set noreorder
+ lw v0,0(a0)
+ li t1,1
+ beq v0,t1,retry_push
+ move t0,a1
+
+ ll v0,0(a0)
+ beq v0,t1,retry_push
+ nop
+ sc t1,0(a0)
+ beq t1,0,retry_push
+ nop
+ sw v0,0(a1)
+ sync
+ sw t0,0(a0)
+ jr ra
+ nop
+
+END(PR_StackPush)
+
+/*
+ *
+ * Atomically remove the element at the top of the stack
+ *
+ * usage : elemep = PR_StackPop(listp);
+ *
+ */
+
+LEAF(PR_StackPop)
+retry_pop:
+.set noreorder
+
+
+ lw v0,0(a0)
+ li t1,1
+ beq v0,0,done
+ beq v0,t1,retry_pop
+ nop
+
+ ll v0,0(a0)
+ beq v0,0,done
+ beq v0,t1,retry_pop
+ nop
+ sc t1,0(a0)
+ beq t1,0,retry_pop
+ nop
+ lw t0,0(v0)
+ sw t0,0(a0)
+done:
+ jr ra
+ nop
+
+END(PR_StackPop)
+
+#endif /* _PR_HAVE_ATOMIC_CAS */
diff --git a/pr/src/md/unix/os_ReliantUNIX.s b/pr/src/md/unix/os_ReliantUNIX.s
new file mode 100644
index 00000000..86cb2f06
--- /dev/null
+++ b/pr/src/md/unix/os_ReliantUNIX.s
@@ -0,0 +1,109 @@
+/* -*- 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..bbad267c
--- /dev/null
+++ b/pr/src/md/unix/os_SunOS.s
@@ -0,0 +1,134 @@
+/* -*- 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
+
+! ======================================================================
+!
+! Atomically add a new element to the top of the stack
+!
+! usage : PR_StackPush(listp, elementp);
+!
+! -----------------------
+! Note on REGISTER USAGE:
+! as this is a LEAF procedure, a new stack frame is not created.
+!
+! So, the registers used are:
+! %o0 [input] - the address of the stack
+! %o1 [input] - the address of the element to be added to the stack
+! -----------------------
+
+ .section ".text"
+ .global PR_StackPush
+
+PR_StackPush:
+
+pulock: ld [%o0],%o3 !
+ cmp %o3,-1 ! check if stack is locked
+ be pulock ! loop, if locked
+ mov -1,%o3 ! use delay-slot
+ swap [%o0],%o3 ! atomically lock the stack and get
+ ! the pointer to stack head
+ cmp %o3,-1 ! check, if the stack is locked
+ be pulock ! loop, if so
+ nop
+ st %o3,[%o1]
+ retl ! return back to the caller
+ st %o1,[%o0] !
+
+ .size PR_StackPush,(.-PR_StackPush)
+
+
+! end
+! ======================================================================
+
+! ======================================================================
+!
+! Atomically remove the element at the top of the stack
+!
+! usage : elemep = PR_StackPop(listp);
+!
+! -----------------------
+! Note on REGISTER USAGE:
+! as this is a LEAF procedure, a new stack frame is not created.
+!
+! So, the registers used are:
+! %o0 [input] - the address of the stack
+! %o1 [input] - work register (top element)
+! -----------------------
+
+ .section ".text"
+ .global PR_StackPop
+
+PR_StackPop:
+
+polock: ld [%o0],%o1 !
+ cmp %o1,-1 ! check if stack is locked
+ be polock ! loop, if locked
+ mov -1,%o1 ! use delay-slot
+ swap [%o0],%o1 ! atomically lock the stack and get
+ ! the pointer to stack head
+ cmp %o1,-1 ! check, if the stack is locked
+ be polock ! loop, if so
+ nop
+ tst %o1 ! test for empty stack
+ be,a empty ! is empty
+ st %g0,[%o0]
+ ld [%o1], %o2 ! load the second element
+ st %o2,[%o0] ! set stack head to second
+ st %g0,[%o1] ! reset the next pointer; for
+ ! debugging
+empty:
+ retl ! return back to the caller
+ mov %o1, %o0 ! return the first element
+
+ .size PR_StackPop,(.-PR_StackPop)
+
+! end
+! ======================================================================
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..8dd70636
--- /dev/null
+++ b/pr/src/md/unix/os_SunOS_ultrasparc.s
@@ -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.
+!
+!
+! 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
+!
+! ======================================================================
+!
+
+! ======================================================================
+!
+! Perform the sequence a = a + b atomically with respect to other
+! fetch-and-adds to location a in a wait-free fashion.
+!
+! usage : newval = PR_AtomicAdd(address, val)
+! return: the value after addition
+!
+ ENTRY(PR_AtomicAdd) ! standard assembler/ELF prologue
+
+retryAA:
+ ld [%o0], %o2 ! set o2 to the current value
+ add %o2, %o1, %o3 ! calc the new value
+ mov %o3, %o4 ! 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 retryAA ! if not, try again
+ nop ! empty out the branch pipeline
+ retl ! return back to the caller
+ mov %o4, %o0 ! set the return code to the new value
+
+ SET_SIZE(PR_AtomicAdd) ! 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..5b02adb2
--- /dev/null
+++ b/pr/src/md/unix/osf1.c
@@ -0,0 +1,145 @@
+/* -*- 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 */
+
+#ifdef _PR_HAVE_ATOMIC_CAS
+
+#include <c_asm.h>
+
+#define _PR_OSF_ATOMIC_LOCK 1
+
+void
+PR_StackPush(PRStack *stack, PRStackElem *stack_elem)
+{
+long locked;
+
+ do {
+ while ((long) stack->prstk_head.prstk_elem_next ==
+ _PR_OSF_ATOMIC_LOCK)
+ ;
+ locked = __ATOMIC_EXCH_QUAD(&stack->prstk_head.prstk_elem_next,
+ _PR_OSF_ATOMIC_LOCK);
+
+ } while (locked == _PR_OSF_ATOMIC_LOCK);
+ stack_elem->prstk_elem_next = (PRStackElem *) locked;
+ /*
+ * memory-barrier instruction
+ */
+ asm("mb");
+ stack->prstk_head.prstk_elem_next = stack_elem;
+}
+
+PRStackElem *
+PR_StackPop(PRStack *stack)
+{
+PRStackElem *element;
+long locked;
+
+ do {
+ while ((long)stack->prstk_head.prstk_elem_next == _PR_OSF_ATOMIC_LOCK)
+ ;
+ locked = __ATOMIC_EXCH_QUAD(&stack->prstk_head.prstk_elem_next,
+ _PR_OSF_ATOMIC_LOCK);
+
+ } while (locked == _PR_OSF_ATOMIC_LOCK);
+
+ element = (PRStackElem *) locked;
+
+ if (element == NULL) {
+ stack->prstk_head.prstk_elem_next = NULL;
+ } else {
+ stack->prstk_head.prstk_elem_next =
+ element->prstk_elem_next;
+ }
+ /*
+ * memory-barrier instruction
+ */
+ asm("mb");
+ return element;
+}
+#endif /* _PR_HAVE_ATOMIC_CAS */
diff --git a/pr/src/md/unix/pthreads_user.c b/pr/src/md/unix/pthreads_user.c
new file mode 100644
index 00000000..08fea163
--- /dev/null
+++ b/pr/src/md/unix/pthreads_user.c
@@ -0,0 +1,461 @@
+/* -*- 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;
+
+ 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 ... */
+ (void)GETTIMEOFDAY(&now);
+ /* 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;
+ int rv;
+ 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;
+ rv = pthread_create(&thread->md.pthread, &attr, start, (void *)thread);
+ if (0 == rv) {
+ _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);
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, rv);
+ 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];
+#ifndef _PR_USE_POLL
+ FD_SET(_pr_md_pipefd[0], &_PR_FD_READ_SET(cpu));
+#endif
+ }
+}
+
+
+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..0bd597d6
--- /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.
+ */
+
+/*
+ * SINIX (ReliantUNIX) 5.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 SINIX */
+/* 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 SINIX.");
+}
+
+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 SINIX.");
+#if defined(SNI) && !defined(__GNUC__)
+ /* make compiler happy */
+ return (PRStatus)NULL;
+#endif
+}
diff --git a/pr/src/md/unix/rhapsody.c b/pr/src/md/unix/rhapsody.c
new file mode 100644
index 00000000..01a73650
--- /dev/null
+++ b/pr/src/md/unix/rhapsody.c
@@ -0,0 +1,99 @@
+/* -*- 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 !defined(_PR_PTHREADS)
+ if (isCurrent) {
+ (void) setjmp(CONTEXT(t));
+ }
+ *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
+ return (PRWord *) CONTEXT(t);
+#else
+ *np = 0;
+ return NULL;
+#endif
+}
+
+#if !defined(_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 Rhapsody */
+void
+_MD_YIELD(void)
+{
+ PR_NOT_REACHED("_MD_YIELD should not be called for Rhapsody.");
+}
+
+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 Rhapsody.");
+ return PR_FAILURE;
+}
+#endif /* ! _PR_PTHREADS */
+
+/*
+** Whoops, we don't have a syscall stub for this
+*/
+int mprotect (caddr_t addr, size_t size, int prot)
+{
+ return -1;
+}
+
+/* rhapsody.c */
+
diff --git a/pr/src/md/unix/scoos.c b/pr/src/md/unix/scoos.c
new file mode 100644
index 00000000..7b812aa0
--- /dev/null
+++ b/pr/src/md/unix/scoos.c
@@ -0,0 +1,162 @@
+/* -*- 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_ADD(PRInt32 *ptr, PRInt32 val)
+{
+ flockfile(_uw_semf);
+ (*ptr) += 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..882cbca4
--- /dev/null
+++ b/pr/src/md/unix/solaris.c
@@ -0,0 +1,835 @@
+/* -*- 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 _FILE_OFFSET_BITS
+
+#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_AtomicAdd(PRInt32 *ptr, PRInt32 val)
+{
+ PRInt32 rv;
+ if (mutex_lock(&_solaris_atomic) != 0)
+ PR_ASSERT(0);
+
+ rv = ((*ptr) += 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);
+
+ /*
+ * Note that we create joinable threads with the THR_DETACHED
+ * flag. The reasons why we don't use thr_join to implement
+ * PR_JoinThread are:
+ * - We use a termination condition variable in the PRThread
+ * structure to implement PR_JoinThread across all classic
+ * nspr implementation strategies.
+ * - The native threads may be recycled by NSPR to run other
+ * new NSPR threads, so the native threads may not terminate
+ * when the corresponding NSPR threads terminate.
+ */
+ flags = 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, NULL);
+ 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..62de2cab
--- /dev/null
+++ b/pr/src/md/unix/unix.c
@@ -0,0 +1,3705 @@
+/* -*- 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 <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>
+
+#ifdef _PR_POLL_AVAILABLE
+#include <poll.h>
+#endif
+
+/* 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) || defined(NCR) || defined(RHAPSODY)
+#define _PRSockLen_t int
+#elif (defined(AIX) && !defined(AIX4_1)) || defined(FREEBSD) \
+ || defined(NETBSD) || defined(OPENBSD) || defined(UNIXWARE) || defined(DGUX)
+#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 PRInt64 minus_one;
+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 = 1;
+
+#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];
+static PRInt32 local_io_wait(PRInt32 osfd, PRInt32 wait_flag,
+ PRIntervalTime timeout);
+
+_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_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, PRAccessHow 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;
+#ifndef _PR_USE_POLL
+fd_set rd;
+#else
+struct pollfd pfd;
+#endif /* _PR_USE_POLL */
+PRInt32 osfd = fd->secret->md.osfd;
+
+#ifndef _PR_USE_POLL
+ FD_ZERO(&rd);
+ FD_SET(osfd, &rd);
+#else
+ pfd.fd = osfd;
+ pfd.events = POLLIN;
+#endif /* _PR_USE_POLL */
+ 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)) {
+ if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_READ,
+ PR_INTERVAL_NO_TIMEOUT)) < 0)
+ goto done;
+ } else {
+#ifndef _PR_USE_POLL
+ while ((rv = _MD_SELECT(osfd + 1, &rd, NULL, NULL, NULL))
+ == -1 && (err = _MD_ERRNO()) == EINTR) {
+ /* retry _MD_SELECT() if it is interrupted */
+ }
+#else /* _PR_USE_POLL */
+ while ((rv = _MD_POLL(&pfd, 1, -1))
+ == -1 && (err = _MD_ERRNO()) == EINTR) {
+ /* retry _MD_POLL() if it is interrupted */
+ }
+#endif /* _PR_USE_POLL */
+ 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);
+ }
+ }
+done:
+ return(rv);
+}
+
+PRInt32 _MD_write(PRFileDesc *fd, const void *buf, PRInt32 amount)
+{
+PRThread *me = _PR_MD_CURRENT_THREAD();
+PRInt32 rv, err;
+#ifndef _PR_USE_POLL
+fd_set wd;
+#else
+struct pollfd pfd;
+#endif /* _PR_USE_POLL */
+PRInt32 osfd = fd->secret->md.osfd;
+
+#ifndef _PR_USE_POLL
+ FD_ZERO(&wd);
+ FD_SET(osfd, &wd);
+#else
+ pfd.fd = osfd;
+ pfd.events = POLLOUT;
+#endif /* _PR_USE_POLL */
+ 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)) {
+ if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE,
+ PR_INTERVAL_NO_TIMEOUT)) < 0)
+ goto done;
+ } else {
+#ifndef _PR_USE_POLL
+ while ((rv = _MD_SELECT(osfd + 1, NULL, &wd, NULL, NULL))
+ == -1 && (err = _MD_ERRNO()) == EINTR) {
+ /* retry _MD_SELECT() if it is interrupted */
+ }
+#else /* _PR_USE_POLL */
+ while ((rv = _MD_POLL(&pfd, 1, -1))
+ == -1 && (err = _MD_ERRNO()) == EINTR) {
+ /* retry _MD_POLL() if it is interrupted */
+ }
+#endif /* _PR_USE_POLL */
+ 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);
+ }
+ }
+done:
+ return(rv);
+}
+
+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
+
+/*
+ * socket_io_wait --
+ *
+ * wait for socket i/o, periodically checking for interrupt
+ *
+ * The first implementation uses select(), for platforms without
+ * poll(). The second (preferred) implementation uses poll().
+ */
+
+#ifndef _PR_USE_POLL
+
+static PRInt32 socket_io_wait(PRInt32 osfd, PRInt32 fd_type,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv = -1;
+ struct timeval tv;
+ 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;
+ FD_ZERO(&rd_wr);
+ do {
+ FD_SET(osfd, &rd_wr);
+ if (fd_type == READ_FD)
+ rv = _MD_SELECT(osfd + 1, &rd_wr, NULL, NULL, &tv);
+ else
+ rv = _MD_SELECT(osfd + 1, NULL, &rd_wr, NULL, &tv);
+ if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) {
+ _PR_MD_MAP_SELECT_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;
+ 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, &tv);
+ else
+ rv = _MD_SELECT(osfd + 1, NULL, &rd_wr, NULL, &tv);
+ /*
+ * we don't consider EINTR a real error
+ */
+ if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) {
+ _PR_MD_MAP_SELECT_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);
+}
+
+#else /* _PR_USE_POLL */
+
+static PRInt32 socket_io_wait(PRInt32 osfd, PRInt32 fd_type,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv = -1;
+ int msecs;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRIntervalTime epoch, now, elapsed, remaining;
+ PRInt32 syserror;
+ struct pollfd pfd;
+
+ 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.
+ */
+ msecs = _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000;
+ pfd.fd = osfd;
+ if (fd_type == READ_FD) {
+ pfd.events = POLLIN;
+ } else {
+ pfd.events = POLLOUT;
+ }
+ do {
+ rv = _MD_POLL(&pfd, 1, msecs);
+ if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) {
+ _PR_MD_MAP_POLL_ERROR(syserror);
+ break;
+ }
+ /*
+ * If POLLERR is set, don't process it; retry the operation
+ */
+ if ((rv == 1) && (pfd.revents & (POLLHUP | POLLNVAL))) {
+ rv = -1;
+ _PR_MD_MAP_POLL_REVENTS_ERROR(pfd.revents);
+ 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;
+ pfd.fd = osfd;
+ if (fd_type == READ_FD) {
+ pfd.events = POLLIN;
+ } else {
+ pfd.events = POLLOUT;
+ }
+ do {
+ /*
+ * We block in _MD_POLL 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.
+ */
+ msecs = PR_IntervalToMilliseconds(remaining);
+ if (msecs > _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000) {
+ msecs = _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000;
+ }
+ rv = _MD_POLL(&pfd, 1, msecs);
+ /*
+ * we don't consider EINTR a real error
+ */
+ if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) {
+ _PR_MD_MAP_POLL_ERROR(syserror);
+ break;
+ }
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ /*
+ * If POLLERR is set, don't process it; retry the operation
+ */
+ if ((rv == 1) && (pfd.revents & (POLLHUP | POLLNVAL))) {
+ rv = -1;
+ _PR_MD_MAP_POLL_REVENTS_ERROR(pfd.revents);
+ break;
+ }
+ /*
+ * We loop again if _MD_POLL 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_POLL timed out, we know how much time
+ * we spent in blocking, so we can avoid a
+ * PR_IntervalNow() call.
+ */
+ if (rv == 0) {
+ now += PR_MillisecondsToInterval(msecs);
+ } 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);
+}
+
+#endif /* _PR_USE_POLL */
+
+static PRInt32 local_io_wait(
+ PRInt32 osfd,
+ PRInt32 wait_flag,
+ PRIntervalTime timeout)
+{
+ _PRUnixPollDesc pd;
+ PRInt32 rv;
+
+ PR_LOG(_pr_io_lm, PR_LOG_MIN,
+ ("waiting to %s on osfd=%d",
+ (wait_flag == _PR_UNIX_POLL_READ) ? "read" : "write",
+ osfd));
+
+ if (timeout == PR_INTERVAL_NO_WAIT) return 0;
+
+ pd.osfd = osfd;
+ pd.in_flags = wait_flag;
+ pd.out_flags = 0;
+
+ rv = _PR_WaitForMultipleFDs(&pd, 1, timeout);
+
+ if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ }
+ 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) {
+#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 ((rv = local_io_wait(osfd,_PR_UNIX_POLL_READ,timeout)) < 0)
+ 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 ((rv = local_io_wait(osfd, _PR_UNIX_POLL_READ, timeout)) < 0)
+ 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 _PR_HAVE_SOCKADDR_LEN
+ if (rv != -1) {
+ /* mask off the first byte of struct sockaddr (the length field) */
+ if (addr) {
+ *((unsigned char *) addr) = 0;
+#ifdef IS_LITTLE_ENDIAN
+ addr->raw.family = ntohs(addr->raw.family);
+#endif
+ }
+ }
+#endif /* _PR_HAVE_SOCKADDR_LEN */
+ 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();
+
+ /*
+ * On pre-2.6 Solaris, send() is much slower than write().
+ * On 2.6 and beyond, with in-kernel sockets, send() and
+ * write() are fairly equivalent in performance.
+ */
+#if defined(SOLARIS)
+ PR_ASSERT(0 == flags);
+ while ((rv = write(osfd,buf,amount)) == -1) {
+#else
+ while ((rv = send(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 ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout)) < 0)
+ 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) {
+ rv = -1;
+ goto done;
+ }
+ } else {
+ if (local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout) < 0) {
+ rv = -1;
+ 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 ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout)) < 0)
+ 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 ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout)) < 0)
+ 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) {
+ rv = -1;
+ goto done;
+ }
+ } else {
+ if (local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout) < 0) {
+ rv = -1;
+ 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 ((rv = local_io_wait(osfd, _PR_UNIX_POLL_READ, timeout)) < 0)
+ 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 _PR_HAVE_SOCKADDR_LEN
+ if (rv != -1) {
+ /* mask off the first byte of struct sockaddr (the length field) */
+ if (addr) {
+ *((unsigned char *) addr) = 0;
+#ifdef IS_LITTLE_ENDIAN
+ addr->raw.family = ntohs(addr->raw.family);
+#endif
+ }
+ }
+#endif /* _PR_HAVE_SOCKADDR_LEN */
+ 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)) {
+
+ if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout)) < 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 _PR_HAVE_SOCKADDR_LEN
+ if (rv == 0) {
+ /* mask off the first byte of struct sockaddr (the length field) */
+ if (addr) {
+ *((unsigned char *) addr) = 0;
+#ifdef IS_LITTLE_ENDIAN
+ addr->raw.family = ntohs(addr->raw.family);
+#endif
+ }
+ }
+#endif /* _PR_HAVE_SOCKADDR_LEN */
+ 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 _PR_HAVE_SOCKADDR_LEN
+ if (rv == 0) {
+ /* mask off the first byte of struct sockaddr (the length field) */
+ if (addr) {
+ *((unsigned char *) addr) = 0;
+#ifdef IS_LITTLE_ENDIAN
+ addr->raw.family = ntohs(addr->raw.family);
+#endif
+ }
+ }
+#endif /* _PR_HAVE_SOCKADDR_LEN */
+ 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;
+}
+
+/************************************************************************/
+#if !defined(_PR_USE_POLL)
+
+/*
+** 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_UNIX_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_UNIX_POLL_READ) {
+ if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
+ }
+ if (in_flags & _PR_UNIX_POLL_WRITE) {
+ if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ }
+ if (in_flags & _PR_UNIX_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];
+ }
+}
+#endif /* !defined(_PR_USE_POLL) */
+
+/************************************************************************/
+
+/*
+** 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 */
+ unsigned long pollfds_size;
+ 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 = _PR_IOQ_OSFD_CNT(me->cpu);
+ PR_ASSERT(npollfds >= 0);
+
+ /*
+ * 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++;
+#ifdef IRIX
+ /*
+ * On Irix, a second pipe is used to cause the primordial cpu to
+ * wakeup and exit, when the process is exiting because of a call
+ * to exit/PR_ProcessExit.
+ */
+ if (me->cpu->id == 0) {
+ npollfds++;
+ }
+#endif
+ }
+
+ /*
+ * if the cpu's pollfd array is not big enough, release it and allocate a new one
+ */
+ if (npollfds > _PR_IOQ_POLLFDS_SIZE(me->cpu)) {
+ if (_PR_IOQ_POLLFDS(me->cpu) != NULL)
+ PR_DELETE(_PR_IOQ_POLLFDS(me->cpu));
+ pollfds_size = PR_MAX(_PR_IOQ_MIN_POLLFDS_SIZE(me->cpu), npollfds);
+ pollfds = (struct pollfd *) PR_MALLOC(pollfds_size * sizeof(struct pollfd));
+ _PR_IOQ_POLLFDS(me->cpu) = pollfds;
+ _PR_IOQ_POLLFDS_SIZE(me->cpu) = pollfds_size;
+ } else {
+ pollfds = _PR_IOQ_POLLFDS(me->cpu);
+ }
+ 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 = POLLIN;
+ pollfdPtr++;
+#ifdef IRIX
+ /*
+ * On Irix, the second element is the exit pipe
+ */
+ if (me->cpu->id == 0) {
+ pollfdPtr->fd = _pr_irix_primoridal_cpu_fd[0];
+ pollfdPtr->events = POLLIN;
+ pollfdPtr++;
+ }
+#endif
+ }
+
+ 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;
+ }
+ }
+ _PR_IOQ_TIMEOUT(me->cpu) = min_timeout;
+#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();
+
+#ifdef IRIX
+ _PR_MD_primordial_cpu();
+#endif
+
+ _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 & POLLIN) && (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++;
+#ifdef IRIX
+ /*
+ * On Irix, check to see if the primordial cpu needs to exit
+ * to cause the process to terminate
+ */
+ if (me->cpu->id == 0) {
+ PR_ASSERT(pollfds[1].fd == _pr_irix_primoridal_cpu_fd[0]);
+ if (pollfdPtr->revents & POLLIN) {
+ if (_pr_irix_process_exit) {
+ /*
+ * process exit due to a call to PR_ProcessExit
+ */
+ prctl(PR_SETEXITSIG, SIGKILL);
+ _exit(_pr_irix_process_exit_code);
+ } else {
+ while ((rv = read(_pr_irix_primoridal_cpu_fd[0],
+ _pr_md_pipebuf, PIPE_BUF)) == PIPE_BUF) {
+ }
+ PR_ASSERT(rv > 0);
+ }
+ }
+ pollfdPtr++;
+ }
+#endif
+ }
+ 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;
+
+ 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);
+ _PR_IOQ_OSFD_CNT(me->cpu) -= pq->npds;
+ PR_ASSERT(_PR_IOQ_OSFD_CNT(me->cpu) >= 0);
+ }
+ }
+ } else if (nfd == -1) {
+ PR_LOG(_pr_io_lm, PR_LOG_MAX, ("poll() failed with errno %d", errno));
+ }
+
+#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_UNIX_POLL_READ) && FD_ISSET(osfd, rp)) {
+ out_flags |= _PR_UNIX_POLL_READ;
+ }
+ if ((in_flags & _PR_UNIX_POLL_WRITE) && FD_ISSET(osfd, wp)) {
+ out_flags |= _PR_UNIX_POLL_WRITE;
+ }
+ if ((in_flags & _PR_UNIX_POLL_EXCEPT) && FD_ISSET(osfd, ep)) {
+ out_flags |= _PR_UNIX_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_UNIX_POLL_READ) {
+ if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
+ }
+ if (in_flags & _PR_UNIX_POLL_WRITE) {
+ if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ }
+ if (in_flags & _PR_UNIX_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];
+#ifdef IRIX
+ if ((me->cpu->id == 0) &&
+ (FD_ISSET(_pr_irix_primoridal_cpu_fd[0], rp))) {
+ if (_pr_irix_process_exit) {
+ /*
+ * process exit due to a call to PR_ProcessExit
+ */
+ prctl(PR_SETEXITSIG, SIGKILL);
+ _exit(_pr_irix_process_exit_code);
+ } else {
+ while ((rv = read(_pr_irix_primoridal_cpu_fd[0],
+ _pr_md_pipebuf, PIPE_BUF)) == PIPE_BUF) {
+ }
+ PR_ASSERT(rv > 0);
+ }
+ }
+ if (me->cpu->id == 0) {
+ if (_PR_IOQ_MAX_OSFD(me->cpu) < _pr_irix_primoridal_cpu_fd[0])
+ _PR_IOQ_MAX_OSFD(me->cpu) = _pr_irix_primoridal_cpu_fd[0];
+ }
+#endif
+ }
+ } 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];
+#ifndef _PR_USE_POLL
+ FD_SET(_pr_md_pipefd[0], &_PR_FD_READ_SET(me->cpu));
+#endif
+
+ 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);
+}
+
+/*
+ * On HP-UX 9, we have to use the sigvector() interface to restart
+ * interrupted system calls, because sigaction() does not have the
+ * SA_RESTART flag.
+ */
+
+#ifdef HPUX9
+static void HPUX9_ClockInterruptHandler(
+ int sig,
+ int code,
+ struct sigcontext *scp)
+{
+ ClockInterruptHandler();
+ scp->sc_syscall_action = SIG_RESTART;
+}
+#endif /* HPUX9 */
+
+/* # of milliseconds per clock tick that we will use */
+#define MSEC_PER_TICK 50
+
+
+void _MD_StartInterrupts()
+{
+ struct itimerval itval;
+ char *eval;
+#ifdef HPUX9
+ struct sigvec vec;
+
+ vec.sv_handler = (void (*)()) HPUX9_ClockInterruptHandler;
+ vec.sv_mask = 0;
+ vec.sv_flags = 0;
+ sigvector(SIGALRM, &vec, 0);
+#else
+ struct sigaction vtact;
+
+ vtact.sa_handler = (void (*)()) ClockInterruptHandler;
+ sigemptyset(&vtact.sa_mask);
+ vtact.sa_flags = SA_RESTART;
+ sigaction(SIGALRM, &vtact, 0);
+#endif /* HPUX9 */
+
+ 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_EnableClockInterrupts()
+{
+ struct itimerval itval;
+ extern PRUintn _pr_numCPU;
+
+ PR_ASSERT(_pr_numCPU == 1);
+ 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);
+}
+
+void _MD_DisableClockInterrupts()
+{
+ struct itimerval itval;
+ extern PRUintn _pr_numCPU;
+
+ PR_ASSERT(_pr_numCPU == 1);
+ 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(O_SYNC)
+ osflags |= O_SYNC;
+#elif defined(O_FSYNC)
+ osflags |= O_FSYNC;
+#else
+#error "Neither O_SYNC nor O_FSYNC is defined on this platform"
+#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 = _md_iovector._open64(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;
+
+#if defined(SOLARIS) || defined(IRIX)
+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 /* SOLARIS, IRIX */
+
+#endif /* !defined(_PR_PTHREADS) */
+
+PRInt32 _MD_lseek(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence)
+{
+ PRInt32 rv, where;
+
+ 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)
+ {
+ PRInt32 syserr = _MD_ERRNO();
+ _PR_MD_MAP_LSEEK_ERROR(syserr);
+ }
+done:
+ return(rv);
+}
+
+PRInt64 _MD_lseek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence)
+{
+ PRInt32 where;
+ PRInt64 rv;
+
+ 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 = minus_one;
+ goto done;
+ }
+ rv = _md_iovector._lseek64(fd->secret->md.osfd, offset, where);
+ if (LL_EQ(rv, minus_one))
+ {
+ PRInt32 syserr = _MD_ERRNO();
+ _PR_MD_MAP_LSEEK_ERROR(syserr);
+ }
+done:
+ return rv;
+} /* _MD_lseek64 */
+
+/*
+** _MD_set_fileinfo_times --
+** Set the modifyTime and creationTime of the PRFileInfo
+** structure using the values in struct stat.
+**
+** _MD_set_fileinfo64_times --
+** Set the modifyTime and creationTime of the PRFileInfo64
+** structure using the values in _MDStat64.
+*/
+
+#if defined(_PR_STAT_HAS_ST_ATIM)
+/*
+** struct stat has st_atim, st_mtim, and st_ctim fields of
+** type timestruc_t.
+*/
+static void _MD_set_fileinfo_times(
+ const struct stat *sb,
+ PRFileInfo *info)
+{
+ info->modifyTime = ((PRTime)sb->st_mtim.tv_sec * PR_USEC_PER_SEC);
+ info->modifyTime += (sb->st_mtim.tv_nsec / 1000);
+ info->creationTime = ((PRTime)sb->st_ctim.tv_sec * PR_USEC_PER_SEC);
+ info->creationTime += (sb->st_ctim.tv_nsec / 1000);
+}
+
+static void _MD_set_fileinfo64_times(
+ const _MDStat64 *sb,
+ PRFileInfo64 *info)
+{
+ info->modifyTime = ((PRTime)sb->st_mtim.tv_sec * PR_USEC_PER_SEC);
+ info->modifyTime += (sb->st_mtim.tv_nsec / 1000);
+ info->creationTime = ((PRTime)sb->st_ctim.tv_sec * PR_USEC_PER_SEC);
+ info->creationTime += (sb->st_ctim.tv_nsec / 1000);
+}
+#elif defined(_PR_STAT_HAS_ST_ATIM_UNION)
+/*
+** The st_atim, st_mtim, and st_ctim fields in struct stat are
+** unions with a st__tim union member of type timestruc_t.
+*/
+static void _MD_set_fileinfo_times(
+ const struct stat *sb,
+ PRFileInfo *info)
+{
+ PRInt64 us, s2us;
+
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(info->modifyTime, sb->st_mtim.st__tim.tv_sec);
+ LL_MUL(info->modifyTime, info->modifyTime, s2us);
+ LL_I2L(us, sb->st_mtim.st__tim.tv_nsec / 1000);
+ LL_ADD(info->modifyTime, info->modifyTime, us);
+ LL_I2L(info->creationTime, sb->st_ctim.st__tim.tv_sec);
+ LL_MUL(info->creationTime, info->creationTime, s2us);
+ LL_I2L(us, sb->st_ctim.st__tim.tv_nsec / 1000);
+ LL_ADD(info->creationTime, info->creationTime, us);
+}
+
+static void _MD_set_fileinfo64_times(
+ const _MDStat64 *sb,
+ PRFileInfo64 *info)
+{
+ PRInt64 us, s2us;
+
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(info->modifyTime, sb->st_mtim.st__tim.tv_sec);
+ LL_MUL(info->modifyTime, info->modifyTime, s2us);
+ LL_I2L(us, sb->st_mtim.st__tim.tv_nsec / 1000);
+ LL_ADD(info->modifyTime, info->modifyTime, us);
+ LL_I2L(info->creationTime, sb->st_ctim.st__tim.tv_sec);
+ LL_MUL(info->creationTime, info->creationTime, s2us);
+ LL_I2L(us, sb->st_ctim.st__tim.tv_nsec / 1000);
+ LL_ADD(info->creationTime, info->creationTime, us);
+}
+#elif defined(_PR_STAT_HAS_ST_ATIMESPEC)
+/*
+** struct stat has st_atimespec, st_mtimespec, and st_ctimespec
+** fields of type struct timespec.
+*/
+#if defined(_PR_TIMESPEC_HAS_TS_SEC)
+static void _MD_set_fileinfo_times(
+ const struct stat *sb,
+ PRFileInfo *info)
+{
+ PRInt64 us, s2us;
+
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(info->modifyTime, sb->st_mtimespec.ts_sec);
+ LL_MUL(info->modifyTime, info->modifyTime, s2us);
+ LL_I2L(us, sb->st_mtimespec.ts_nsec / 1000);
+ LL_ADD(info->modifyTime, info->modifyTime, us);
+ LL_I2L(info->creationTime, sb->st_ctimespec.ts_sec);
+ LL_MUL(info->creationTime, info->creationTime, s2us);
+ LL_I2L(us, sb->st_ctimespec.ts_nsec / 1000);
+ LL_ADD(info->creationTime, info->creationTime, us);
+}
+
+static void _MD_set_fileinfo64_times(
+ const _MDStat64 *sb,
+ PRFileInfo64 *info)
+{
+ PRInt64 us, s2us;
+
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(info->modifyTime, sb->st_mtimespec.ts_sec);
+ LL_MUL(info->modifyTime, info->modifyTime, s2us);
+ LL_I2L(us, sb->st_mtimespec.ts_nsec / 1000);
+ LL_ADD(info->modifyTime, info->modifyTime, us);
+ LL_I2L(info->creationTime, sb->st_ctimespec.ts_sec);
+ LL_MUL(info->creationTime, info->creationTime, s2us);
+ LL_I2L(us, sb->st_ctimespec.ts_nsec / 1000);
+ LL_ADD(info->creationTime, info->creationTime, us);
+}
+#else /* _PR_TIMESPEC_HAS_TS_SEC */
+/*
+** The POSIX timespec structure has tv_sec and tv_nsec.
+*/
+static void _MD_set_fileinfo_times(
+ const struct stat *sb,
+ PRFileInfo *info)
+{
+ PRInt64 us, s2us;
+
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(info->modifyTime, sb->st_mtimespec.tv_sec);
+ LL_MUL(info->modifyTime, info->modifyTime, s2us);
+ LL_I2L(us, sb->st_mtimespec.tv_nsec / 1000);
+ LL_ADD(info->modifyTime, info->modifyTime, us);
+ LL_I2L(info->creationTime, sb->st_ctimespec.tv_sec);
+ LL_MUL(info->creationTime, info->creationTime, s2us);
+ LL_I2L(us, sb->st_ctimespec.tv_nsec / 1000);
+ LL_ADD(info->creationTime, info->creationTime, us);
+}
+
+static void _MD_set_fileinfo64_times(
+ const _MDStat64 *sb,
+ PRFileInfo64 *info)
+{
+ PRInt64 us, s2us;
+
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(info->modifyTime, sb->st_mtimespec.tv_sec);
+ LL_MUL(info->modifyTime, info->modifyTime, s2us);
+ LL_I2L(us, sb->st_mtimespec.tv_nsec / 1000);
+ LL_ADD(info->modifyTime, info->modifyTime, us);
+ LL_I2L(info->creationTime, sb->st_ctimespec.tv_sec);
+ LL_MUL(info->creationTime, info->creationTime, s2us);
+ LL_I2L(us, sb->st_ctimespec.tv_nsec / 1000);
+ LL_ADD(info->creationTime, info->creationTime, us);
+}
+#endif /* _PR_TIMESPEC_HAS_TS_SEC */
+#elif defined(_PR_STAT_HAS_ONLY_ST_ATIME)
+/*
+** struct stat only has st_atime, st_mtime, and st_ctime fields
+** of type time_t.
+*/
+static void _MD_set_fileinfo_times(
+ const struct stat *sb,
+ PRFileInfo *info)
+{
+ PRInt64 s, s2us;
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(s, sb->st_mtime);
+ LL_MUL(s, s, s2us);
+ info->modifyTime = s;
+ LL_I2L(s, sb->st_ctime);
+ LL_MUL(s, s, s2us);
+ info->creationTime = s;
+}
+
+static void _MD_set_fileinfo64_times(
+ const _MDStat64 *sb,
+ PRFileInfo64 *info)
+{
+ PRInt64 s, s2us;
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(s, sb->st_mtime);
+ LL_MUL(s, s, s2us);
+ info->modifyTime = s;
+ LL_I2L(s, sb->st_ctime);
+ LL_MUL(s, s, s2us);
+ info->creationTime = s;
+}
+#else
+#error "I don't know yet"
+#endif
+
+static int _MD_convert_stat_to_fileinfo(
+ const struct stat *sb,
+ PRFileInfo *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;
+
+#if defined(_PR_HAVE_LARGE_OFF_T)
+ if (0x7fffffffL < sb->st_size)
+ {
+ PR_SetError(PR_FILE_TOO_BIG_ERROR, 0);
+ return -1;
+ }
+#endif /* defined(_PR_HAVE_LARGE_OFF_T) */
+ info->size = sb->st_size;
+
+ _MD_set_fileinfo_times(sb, info);
+ return 0;
+} /* _MD_convert_stat_to_fileinfo */
+
+static int _MD_convert_stat64_to_fileinfo64(
+ const _MDStat64 *sb,
+ PRFileInfo64 *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;
+
+ LL_I2L(info->size, sb->st_size);
+
+ _MD_set_fileinfo64_times(sb, info);
+ return 0;
+} /* _MD_convert_stat64_to_fileinfo64 */
+
+PRInt32 _MD_getfileinfo(const char *fn, PRFileInfo *info)
+{
+ PRInt32 rv;
+ struct stat sb;
+
+ rv = stat(fn, &sb);
+ if (rv < 0)
+ _PR_MD_MAP_STAT_ERROR(_MD_ERRNO());
+ else if (NULL != info)
+ rv = _MD_convert_stat_to_fileinfo(&sb, info);
+ return rv;
+}
+
+PRInt32 _MD_getfileinfo64(const char *fn, PRFileInfo64 *info)
+{
+ _MDStat64 sb;
+ PRInt32 rv = _md_iovector._stat64(fn, &sb);
+ if (rv < 0)
+ _PR_MD_MAP_STAT_ERROR(_MD_ERRNO());
+ else if (NULL != info)
+ rv = _MD_convert_stat64_to_fileinfo64(&sb, info);
+ return rv;
+}
+
+PRInt32 _MD_getopenfileinfo(const PRFileDesc *fd, PRFileInfo *info)
+{
+ struct stat sb;
+ PRInt32 rv = fstat(fd->secret->md.osfd, &sb);
+ if (rv < 0)
+ _PR_MD_MAP_FSTAT_ERROR(_MD_ERRNO());
+ else if (NULL != info)
+ rv = _MD_convert_stat_to_fileinfo(&sb, info);
+ return rv;
+}
+
+PRInt32 _MD_getopenfileinfo64(const PRFileDesc *fd, PRFileInfo64 *info)
+{
+ _MDStat64 sb;
+ PRInt32 rv = _md_iovector._fstat64(fd->secret->md.osfd, &sb);
+ if (rv < 0)
+ _PR_MD_MAP_FSTAT_ERROR(_MD_ERRNO());
+ else if (NULL != info)
+ rv = _MD_convert_stat64_to_fileinfo64(&sb, info);
+ return rv;
+}
+
+struct _MD_IOVector _md_iovector;
+
+/*
+** These implementations are to emulate large file routines on systems that
+** don't have them. Their goal is to check in case overflow occurs. Otherwise
+** they will just operate as normal using 32-bit file routines.
+**
+** The checking might be pre- or post-op, depending on the semantics.
+*/
+
+#if defined(SOLARIS2_5)
+
+static PRIntn _MD_solaris25_fstat64(PRIntn osfd, _MDStat64 *buf)
+{
+ PRInt32 rv;
+ struct stat sb;
+
+ rv = fstat(osfd, &sb);
+ if (rv >= 0)
+ {
+ /*
+ ** I'm only copying the fields that are immediately needed.
+ ** If somebody else calls this function, some of the fields
+ ** may not be defined.
+ */
+ (void)memset(buf, 0, sizeof(_MDStat64));
+ buf->st_mode = sb.st_mode;
+ buf->st_ctim = sb.st_ctim;
+ buf->st_mtim = sb.st_mtim;
+ buf->st_size = sb.st_size;
+ }
+ return rv;
+} /* _MD_solaris25_fstat64 */
+
+static PRIntn _MD_solaris25_stat64(const char *fn, _MDStat64 *buf)
+{
+ PRInt32 rv;
+ struct stat sb;
+
+ rv = stat(fn, &sb);
+ if (rv >= 0)
+ {
+ /*
+ ** I'm only copying the fields that are immediately needed.
+ ** If somebody else calls this function, some of the fields
+ ** may not be defined.
+ */
+ (void)memset(buf, 0, sizeof(_MDStat64));
+ buf->st_mode = sb.st_mode;
+ buf->st_ctim = sb.st_ctim;
+ buf->st_mtim = sb.st_mtim;
+ buf->st_size = sb.st_size;
+ }
+ return rv;
+} /* _MD_solaris25_stat64 */
+#endif /* defined(SOLARIS2_5) */
+
+#if defined(_PR_NO_LARGE_FILES) || defined(SOLARIS2_5)
+
+static PRIntn _MD_Unix_lockf64(PRIntn osfd, PRIntn function, PRInt64 size)
+{
+#if defined(HAVE_BSD_FLOCK)
+ /*
+ * XXX: HAVE_BSD_FLOCK is not really the appropriate macro
+ * to test for here. We are trying to identify the platforms
+ * that don't have lockf, e.g., BSD/OS, FreeBSD, and Rhapsody.
+ */
+ /* No lockf */
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return -1;
+#else
+ PRInt64 desired, maxoff;
+ PRInt32 current = lseek(osfd, SEEK_CUR, 0);
+
+ LL_I2L(maxoff, 0x7fffffff);
+ LL_I2L(desired, current);
+ LL_ADD(desired, desired, size);
+ if (LL_CMP(desired, <=, maxoff))
+ {
+ off_t offset;
+ LL_L2I(offset, size);
+ return lockf(osfd, function, offset);
+ }
+ PR_SetError(PR_FILE_TOO_BIG_ERROR, 0);
+ return -1;
+#endif
+} /* _MD_Unix_lockf64 */
+
+static PRInt64 _MD_Unix_lseek64(PRIntn osfd, PRInt64 offset, PRIntn whence)
+{
+ PRUint64 maxoff;
+ PRInt64 rv = minus_one;
+ LL_I2L(maxoff, 0x7fffffff);
+ if (LL_CMP(offset, <=, maxoff))
+ {
+ off_t off;
+ LL_L2I(off, offset);
+ LL_I2L(rv, lseek(osfd, off, whence));
+ }
+ else errno = EFBIG; /* we can't go there */
+ return rv;
+} /* _MD_Unix_lseek64 */
+
+static void* _MD_Unix_mmap64(
+ void *addr, PRSize len, PRIntn prot, PRIntn flags,
+ PRIntn fildes, PRInt64 offset)
+{
+ PR_SetError(PR_FILE_TOO_BIG_ERROR, 0);
+ return NULL;
+} /* _MD_Unix_mmap64 */
+#endif /* defined(_PR_NO_LARGE_FILES) || defined(SOLARIS2_5) */
+
+#if defined(IRIX) || defined(UNIXWARE)
+
+/*
+** This function emulates a lock64 for IRIX using fcntl calls. It is a true
+** 64-bit operation, just a different system API to get it.
+*/
+static PRIntn _MD_irix_lockf64(PRIntn osfd, PRIntn function, PRInt64 size)
+{
+#if 1
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ PR_NOT_REACHED("NOT IMPLEMENTED");
+ return -1;
+#else
+ flock64_t lock;
+ /* $$$ have no idea what I'm doing $$$ */
+ lock.l_type = function;
+ lock.l_whence = SEEK_CUR;
+ lock.l_start = 0;
+ lock.l_len = size;
+
+ return fcntl(osfd, F_SETLKW64, &lock);
+#endif
+} /* _MD_irix_lockf64 */
+
+#endif /* defined(IRIX) */
+
+static void _PR_InitIOV(void)
+{
+#if defined(SOLARIS2_5)
+ PRLibrary *lib;
+ void *open64_func;
+
+ open64_func = PR_FindSymbolAndLibrary("open64", &lib);
+ if (NULL != open64_func)
+ {
+ PR_ASSERT(NULL != lib);
+ _md_iovector._open64 = (_MD_Open64)open64_func;
+ _md_iovector._mmap64 = (_MD_Mmap64)PR_FindSymbol(lib, "mmap64");
+ _md_iovector._fstat64 = (_MD_Fstat64)PR_FindSymbol(lib, "fstat64");
+ _md_iovector._stat64 = (_MD_Stat64)PR_FindSymbol(lib, "stat64");
+ _md_iovector._lockf64 = (_MD_Lockf64)PR_FindSymbol(lib, "lockf64");
+ _md_iovector._lseek64 = (_MD_Lseek64)PR_FindSymbol(lib, "lseek64");
+ (void)PR_UnloadLibrary(lib);
+ }
+ else
+ {
+ _md_iovector._open64 = open;
+ _md_iovector._mmap64 = _MD_Unix_mmap64;
+ _md_iovector._fstat64 = _MD_solaris25_fstat64;
+ _md_iovector._stat64 = _MD_solaris25_stat64;
+ _md_iovector._lockf64 = _MD_Unix_lockf64;
+ _md_iovector._lseek64 = _MD_Unix_lseek64;
+ }
+#elif defined(_PR_NO_LARGE_FILES)
+ _md_iovector._open64 = open;
+ _md_iovector._mmap64 = _MD_Unix_mmap64;
+ _md_iovector._fstat64 = fstat;
+ _md_iovector._stat64 = stat;
+ _md_iovector._lockf64 = _MD_Unix_lockf64;
+ _md_iovector._lseek64 = _MD_Unix_lseek64;
+#elif defined(_PR_HAVE_OFF64_T)
+ _md_iovector._open64 = open64;
+ _md_iovector._mmap64 = mmap64;
+ _md_iovector._fstat64 = fstat64;
+ _md_iovector._stat64 = stat64;
+
+/*
+** $$$ IRIX does not have a lockf64. One must fabricate it from fcntl
+** calls with 64 bit arguments.
+*/
+#if defined(IRIX) || defined(UNIXWARE)
+ _md_iovector._lockf64 = _MD_irix_lockf64;
+#else
+ _md_iovector._lockf64 = lockf64;
+#endif
+ _md_iovector._lseek64 = lseek64;
+#elif defined(_PR_HAVE_LARGE_OFF_T)
+ _md_iovector._open64 = open;
+ _md_iovector._mmap64 = mmap;
+ _md_iovector._fstat64 = fstat;
+ _md_iovector._stat64 = stat;
+ _md_iovector._lockf64 = lockf;
+ _md_iovector._lseek64 = lseek;
+#else
+#error "I don't know yet"
+#endif
+ LL_I2L(minus_one, -1);
+} /* _PR_InitIOV */
+
+void _PR_UnixInit(void)
+{
+ 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 = 0;
+ sigact.sa_mask = timer_set;
+ sigaction(SIGSEGV, &sigact, 0);
+ }
+
+ if (getenv("NSPR_SIGABRT_HANDLE")) {
+ sigact.sa_handler = sigaborthandler;
+ sigact.sa_flags = 0;
+ sigact.sa_mask = timer_set;
+ sigaction(SIGABRT, &sigact, 0);
+ }
+
+ if (getenv("NSPR_SIGBUS_HANDLE")) {
+ sigact.sa_handler = sigbushandler;
+ sigact.sa_flags = 0;
+ 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);
+
+ _PR_InitIOV(); /* one last hack */
+}
+
+/*
+ * _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(void)
+{
+#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;
+ }
+ goto exit;
+ }
+
+ prot = PROT_READ|PROT_WRITE;
+ /*
+ * On Alpha Linux, the user-level thread stack needs
+ * to be made executable because longjmp/signal seem
+ * to put machine instructions on the stack.
+ */
+#if defined(LINUX) && defined(__alpha)
+ prot |= PROT_EXEC;
+#endif
+ 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->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;
+
+ GETTIMEOFDAY(&tv);
+ 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;
+}
+
+PRIntervalTime _PR_UNIX_GetInterval()
+{
+ struct timeval time;
+ PRIntervalTime ticks;
+
+ (void)GETTIMEOFDAY(&time); /* fallicy of course */
+ 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 */
+
+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 ((rv = local_io_wait(sd->secret->md.osfd,
+ _PR_UNIX_POLL_WRITE, timeout)) < 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 multiple descriptors.
+ *
+ * Return 0 if timed out, return -1 if interrupted,
+ * else return the number of ready descriptors.
+ */
+PRInt32 _PR_WaitForMultipleFDs(
+ _PRUnixPollDesc *unixpds,
+ PRInt32 pdcnt,
+ PRIntervalTime timeout)
+{
+ PRPollQueue pq;
+ PRIntn is;
+ PRInt32 rv;
+ _PRCPU *io_cpu;
+ _PRUnixPollDesc *unixpd, *eunixpd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+
+ pq.pds = unixpds;
+ pq.npds = pdcnt;
+
+ _PR_INTSOFF(is);
+ _PR_MD_IOQ_LOCK();
+ _PR_THREAD_LOCK(me);
+
+ pq.thr = me;
+ io_cpu = me->cpu;
+ pq.on_ioq = PR_TRUE;
+ pq.timeout = timeout;
+ _PR_ADD_TO_IOQ(pq, me->cpu);
+
+#if !defined(_PR_USE_POLL)
+ eunixpd = unixpds + pdcnt;
+ for (unixpd = unixpds; unixpd < eunixpd; unixpd++) {
+ PRInt32 osfd = unixpd->osfd;
+ if (unixpd->in_flags & _PR_UNIX_POLL_READ) {
+ FD_SET(osfd, &_PR_FD_READ_SET(me->cpu));
+ _PR_FD_READ_CNT(me->cpu)[osfd]++;
+ }
+ if (unixpd->in_flags & _PR_UNIX_POLL_WRITE) {
+ FD_SET(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ (_PR_FD_WRITE_CNT(me->cpu))[osfd]++;
+ }
+ if (unixpd->in_flags & _PR_UNIX_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;
+ }
+ }
+#endif /* !defined(_PR_USE_POLL) */
+
+ if (_PR_IOQ_TIMEOUT(me->cpu) > timeout) {
+ _PR_IOQ_TIMEOUT(me->cpu) = timeout;
+ }
+
+ _PR_IOQ_OSFD_CNT(me->cpu) += pdcnt;
+
+ _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);
+#ifndef _PR_USE_POLL
+ eunixpd = unixpds + pdcnt;
+ for (unixpd = unixpds; unixpd < eunixpd; unixpd++) {
+ PRInt32 osfd = unixpd->osfd;
+ PRInt16 in_flags = unixpd->in_flags;
+
+ if (in_flags & _PR_UNIX_POLL_READ) {
+ if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
+ }
+ if (in_flags & _PR_UNIX_POLL_WRITE) {
+ if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ }
+ if (in_flags & _PR_UNIX_POLL_EXCEPT) {
+ if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ }
+ }
+#endif /* _PR_USE_POLL */
+ PR_ASSERT(pq.npds == pdcnt);
+ _PR_IOQ_OSFD_CNT(me->cpu) -= pdcnt;
+ PR_ASSERT(_PR_IOQ_OSFD_CNT(me->cpu) >= 0);
+ }
+ _PR_MD_IOQ_UNLOCK();
+ }
+ /* XXX Should we use _PR_FAST_INTSON or _PR_INTSON? */
+ if (1 == pdcnt) {
+ _PR_FAST_INTSON(is);
+ } else {
+ _PR_INTSON(is);
+ }
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+
+ rv = 0;
+ if (pq.on_ioq == PR_FALSE) {
+ /* Count the number of ready descriptors */
+ while (--pdcnt >= 0) {
+ if (unixpds->out_flags != 0) {
+ rv++;
+ }
+ unixpds++;
+ }
+ }
+
+ 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(void)
+{
+ PR_EnterMonitor(_pr_Xfe_mon);
+}
+
+void PR_XUnlock(void)
+{
+ PR_ExitMonitor(_pr_Xfe_mon);
+}
+
+PRBool PR_XIsLocked(void)
+{
+ 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 = MAP_PRIVATE;
+ } 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;
+}
+
+#if defined(_PR_NEED_FAKE_POLL)
+
+/*
+ * Some platforms don't have poll(). For easier porting of code
+ * that calls poll(), we emulate poll() using select().
+ */
+
+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 poll events to the select fd_sets.
+ * POLLIN, POLLRDNORM ===> readable
+ * POLLOUT, POLLWRNORM ===> writable
+ * POLLPRI, POLLRDBAND ===> 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/unix_errors.c b/pr/src/md/unix/unix_errors.c
new file mode 100644
index 00000000..21e44464
--- /dev/null
+++ b/pr/src/md/unix/unix_errors.c
@@ -0,0 +1,1558 @@
+/* -*- 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(_PR_POLL_AVAILABLE)
+#include <poll.h>
+#endif
+#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 EDIRCORRUPTED
+ case EDIRCORRUPTED:
+ PR_SetError(PR_DIRECTORY_CORRUPTED_ERROR, err);
+ break;
+#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;
+#ifdef EMULTIHOP
+ case EMULTIHOP:
+ PR_SetError(PR_REMOTE_FILE_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_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;
+#ifdef EMULTIHOP
+ case EMULTIHOP:
+#endif
+#ifdef ENOLINK
+ case ENOLINK:
+#endif
+ case ETIMEDOUT:
+ 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;
+#ifdef EMULTIHOP
+ case EMULTIHOP:
+ PR_SetError(PR_REMOTE_FILE_ERROR, err);
+ break;
+#endif
+#ifdef ENOLINK
+ 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;
+#ifdef EMULTIHOP
+ case EMULTIHOP:
+#endif
+#ifdef ENOLINK
+ 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;
+#ifdef EMULTIHOP
+ case EMULTIHOP:
+ PR_SetError(PR_REMOTE_FILE_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_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;
+#ifdef EMULTIHOP
+ case EMULTIHOP:
+#endif
+#ifdef ENOLINK
+ 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 EFBIG:
+ PR_SetError(PR_FILE_TOO_BIG_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)
+ case ENOBUFS:
+#endif /* !defined(SCO) */
+ 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;
+ case EIO:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+#if !defined(SCO)
+ case ENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+#endif /* !defined(SCO) */
+ 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)
+ case ENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+#endif /* !defined(SCO) */
+ 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;
+ case EIO:
+ PR_SetError(PR_IO_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)
+ case ENOBUFS:
+#endif /* !defined(SCO) */
+ 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)
+ case ENOBUFS:
+#endif /* !defined(SCO) */
+ 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;
+#ifdef EMULTIHOP
+ case EMULTIHOP:
+#endif
+#ifdef ENOLINK
+ case ENOLINK:
+#endif
+ case ETIMEDOUT:
+ 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 EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case EAGAIN:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case EBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case EINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case EMFILE:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ENODEV:
+ PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, err);
+ break;
+ case ENOMEM:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, err);
+ break;
+ case ENXIO:
+ PR_SetError(PR_INVALID_ARGUMENT_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;
+ }
+}
+
+#ifdef _PR_POLL_AVAILABLE
+void _MD_unix_map_poll_error(int err)
+{
+ PRErrorCode prerror;
+ switch (err) {
+ case EAGAIN:
+ prerror = PR_INSUFFICIENT_RESOURCES_ERROR;
+ 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, err);
+}
+
+void _MD_unix_map_poll_revents_error(int err)
+{
+
+ if (err & POLLNVAL)
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, EBADF);
+ else if (err & POLLHUP)
+ PR_SetError(PR_CONNECT_RESET_ERROR, EPIPE);
+ else if (err & POLLERR)
+ PR_SetError(PR_IO_ERROR, EIO);
+ else
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+}
+#endif /* _PR_POLL_AVAILABLE */
+
+
+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..76c9c751
--- /dev/null
+++ b/pr/src/md/unix/unixware.c
@@ -0,0 +1,562 @@
+/* -*- 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_ADD(PRInt32 *ptr, PRInt32 val)
+{
+ flockfile(_uw_semf);
+ (*ptr) += 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_ADD(PRInt32 *ptr, PRInt32 val)
+{
+ TEST_THEN_ADD(ptr, val);
+}
+
+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/uxpoll.c b/pr/src/md/unix/uxpoll.c
new file mode 100644
index 00000000..244782bf
--- /dev/null
+++ b/pr/src/md/unix/uxpoll.c
@@ -0,0 +1,681 @@
+/* -*- 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(_PR_PTHREADS)
+#else /* defined(_PR_PTHREADS) */
+
+#include "primpl.h"
+
+#include <sys/time.h>
+
+#include <fcntl.h>
+#ifdef _PR_USE_POLL
+#include <poll.h>
+#endif
+
+#if defined(_PR_USE_POLL)
+static PRInt32 NativeThreadPoll(
+ PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
+{
+ /*
+ * This function is mostly duplicated from ptio.s's PR_Poll().
+ */
+ 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.
+ */
+ PRIntn index, msecs;
+ struct pollfd *syspoll = NULL;
+ PRIntervalTime start, elapsed, remaining;
+
+ syspoll = (struct pollfd*)PR_MALLOC(npds * sizeof(struct pollfd));
+ if (NULL == syspoll)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+ for (index = 0; index < npds; ++index)
+ {
+ PRFileDesc *bottom;
+ PRInt16 in_flags_read = 0, in_flags_write = 0;
+ PRInt16 out_flags_read = 0, out_flags_write = 0;
+
+ if ((NULL != pds[index].fd) && (0 != pds[index].in_flags))
+ {
+ if (pds[index].in_flags & PR_POLL_READ)
+ {
+ in_flags_read = (pds[index].fd->methods->poll)(
+ pds[index].fd,
+ pds[index].in_flags & ~PR_POLL_WRITE,
+ &out_flags_read);
+ }
+ if (pds[index].in_flags & PR_POLL_WRITE)
+ {
+ in_flags_write = (pds[index].fd->methods->poll)(
+ pds[index].fd,
+ pds[index].in_flags & ~PR_POLL_READ,
+ &out_flags_write);
+ }
+ if ((0 != (in_flags_read & out_flags_read))
+ || (0 != (in_flags_write & out_flags_write)))
+ {
+ /* this one is ready right now */
+ if (0 == ready)
+ {
+ /*
+ * We will return without calling the system
+ * poll function. So zero the out_flags
+ * fields of all the poll descriptors before
+ * this one.
+ */
+ int i;
+ for (i = 0; i < index; i++)
+ {
+ pds[i].out_flags = 0;
+ }
+ }
+ ready += 1;
+ pds[index].out_flags = out_flags_read | out_flags_write;
+ }
+ else
+ {
+ pds[index].out_flags = 0; /* pre-condition */
+ /* now locate the NSPR layer at the bottom of the stack */
+ bottom = PR_GetIdentitiesLayer(pds[index].fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom); /* what to do about that? */
+ if ((NULL != bottom)
+ && (_PR_FILEDESC_OPEN == bottom->secret->state))
+ {
+ if (0 == ready)
+ {
+ syspoll[index].fd = bottom->secret->md.osfd;
+ syspoll[index].events = 0; /* pre-condition */
+ if (in_flags_read & PR_POLL_READ)
+ {
+ pds[index].out_flags |=
+ _PR_POLL_READ_SYS_READ;
+ syspoll[index].events |= POLLIN;
+ }
+ if (in_flags_read & PR_POLL_WRITE)
+ {
+ pds[index].out_flags |=
+ _PR_POLL_READ_SYS_WRITE;
+ syspoll[index].events |= POLLOUT;
+ }
+ if (in_flags_write & PR_POLL_READ)
+ {
+ pds[index].out_flags |=
+ _PR_POLL_WRITE_SYS_READ;
+ syspoll[index].events |= POLLIN;
+ }
+ if (in_flags_write & PR_POLL_WRITE)
+ {
+ pds[index].out_flags |=
+ _PR_POLL_WRITE_SYS_WRITE;
+ syspoll[index].events |= POLLOUT;
+ }
+ if (pds[index].in_flags & PR_POLL_EXCEPT)
+ syspoll[index].events |= POLLPRI;
+ }
+ }
+ else
+ {
+ if (0 == ready)
+ {
+ int i;
+ for (i = 0; i < index; i++)
+ {
+ pds[i].out_flags = 0;
+ }
+ }
+ ready += 1; /* this will cause an abrupt return */
+ pds[index].out_flags = PR_POLL_NVAL; /* bogii */
+ }
+ }
+ }
+ else
+ {
+ /* make poll() ignore this entry */
+ syspoll[index].fd = -1;
+ }
+ }
+
+ 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 = _MD_POLL(syspoll, npds, msecs);
+ if (-1 == ready)
+ {
+ PRIntn oserror = errno;
+
+ if (EINTR == oserror)
+ {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) goto retry;
+ else if (timeout == PR_INTERVAL_NO_WAIT) ready = 0;
+ else
+ {
+ elapsed = (PRIntervalTime)(PR_IntervalNow() - start);
+ if (elapsed > timeout) ready = 0; /* timed out */
+ else
+ {
+ remaining = timeout - elapsed;
+ msecs = PR_IntervalToMilliseconds(remaining);
+ goto retry;
+ }
+ }
+ }
+ else _PR_MD_MAP_POLL_ERROR(oserror);
+ }
+ else if (ready > 0)
+ {
+ for (index = 0; index < npds; ++index)
+ {
+ PRInt16 out_flags = 0;
+ if ((NULL != pds[index].fd) && (0 != pds[index].in_flags))
+ {
+ if (0 != syspoll[index].revents)
+ {
+ /*
+ ** Set up the out_flags so that it contains the
+ ** bits that the highest layer thinks are nice
+ ** to have. Then the client of that layer will
+ ** call the appropriate I/O function and maybe
+ ** the protocol will make progress.
+ */
+ if (syspoll[index].revents & POLLIN)
+ {
+ if (pds[index].out_flags
+ & _PR_POLL_READ_SYS_READ)
+ {
+ out_flags |= PR_POLL_READ;
+ }
+ if (pds[index].out_flags
+ & _PR_POLL_WRITE_SYS_READ)
+ {
+ out_flags |= PR_POLL_WRITE;
+ }
+ }
+ if (syspoll[index].revents & POLLOUT)
+ {
+ if (pds[index].out_flags
+ & _PR_POLL_READ_SYS_WRITE)
+ {
+ out_flags |= PR_POLL_READ;
+ }
+ if (pds[index].out_flags
+ & _PR_POLL_WRITE_SYS_WRITE)
+ {
+ out_flags |= PR_POLL_WRITE;
+ }
+ }
+ if (syspoll[index].revents & POLLPRI)
+ out_flags |= PR_POLL_EXCEPT;
+ if (syspoll[index].revents & POLLERR)
+ out_flags |= PR_POLL_ERR;
+ if (syspoll[index].revents & POLLNVAL)
+ out_flags |= PR_POLL_NVAL;
+ if (syspoll[index].revents & POLLHUP)
+ out_flags |= PR_POLL_HUP;
+ }
+ }
+ pds[index].out_flags = out_flags;
+ }
+ }
+ }
+
+ PR_DELETE(syspoll);
+ return ready;
+
+} /* NativeThreadPoll */
+#endif /* defined(_PR_USE_POLL) */
+
+#if !defined(_PR_USE_POLL)
+static PRInt32 NativeThreadSelect(
+ PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
+{
+ /*
+ * This code is almost a duplicate of w32poll.c's _PR_MD_PR_POLL().
+ */
+ fd_set rd, wt, ex;
+ PRFileDesc *bottom;
+ PRPollDesc *pd, *epd;
+ PRInt32 maxfd = -1, ready, err;
+ PRIntervalTime remaining, elapsed, start;
+
+ struct timeval tv, *tvp = NULL;
+
+ FD_ZERO(&rd);
+ FD_ZERO(&wt);
+ FD_ZERO(&ex);
+
+ ready = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ PRInt16 in_flags_read = 0, in_flags_write = 0;
+ PRInt16 out_flags_read = 0, out_flags_write = 0;
+
+ if ((NULL != pd->fd) && (0 != pd->in_flags))
+ {
+ if (pd->in_flags & PR_POLL_READ)
+ {
+ in_flags_read = (pd->fd->methods->poll)(
+ pd->fd, pd->in_flags & ~PR_POLL_WRITE, &out_flags_read);
+ }
+ if (pd->in_flags & PR_POLL_WRITE)
+ {
+ in_flags_write = (pd->fd->methods->poll)(
+ pd->fd, pd->in_flags & ~PR_POLL_READ, &out_flags_write);
+ }
+ if ((0 != (in_flags_read & out_flags_read))
+ || (0 != (in_flags_write & out_flags_write)))
+ {
+ /* this one's ready right now */
+ if (0 == ready)
+ {
+ /*
+ * We will have to return without calling the
+ * system poll/select function. So zero the
+ * out_flags fields of all the poll descriptors
+ * before this one.
+ */
+ PRPollDesc *prev;
+ for (prev = pds; prev < pd; prev++)
+ {
+ prev->out_flags = 0;
+ }
+ }
+ ready += 1;
+ pd->out_flags = out_flags_read | out_flags_write;
+ }
+ else
+ {
+ pd->out_flags = 0; /* pre-condition */
+
+ /* make sure this is an NSPR supported stack */
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom); /* what to do about that? */
+ if ((NULL != bottom)
+ && (_PR_FILEDESC_OPEN == bottom->secret->state))
+ {
+ if (0 == ready)
+ {
+ PRInt32 osfd = bottom->secret->md.osfd;
+ if (osfd > maxfd) maxfd = osfd;
+ if (in_flags_read & PR_POLL_READ)
+ {
+ pd->out_flags |= _PR_POLL_READ_SYS_READ;
+ FD_SET(osfd, &rd);
+ }
+ if (in_flags_read & PR_POLL_WRITE)
+ {
+ pd->out_flags |= _PR_POLL_READ_SYS_WRITE;
+ FD_SET(osfd, &wt);
+ }
+ if (in_flags_write & PR_POLL_READ)
+ {
+ pd->out_flags |= _PR_POLL_WRITE_SYS_READ;
+ FD_SET(osfd, &rd);
+ }
+ if (in_flags_write & PR_POLL_WRITE)
+ {
+ pd->out_flags |= _PR_POLL_WRITE_SYS_WRITE;
+ FD_SET(osfd, &wt);
+ }
+ if (pd->in_flags & PR_POLL_EXCEPT) FD_SET(osfd, &ex);
+ }
+ }
+ else
+ {
+ if (0 == ready)
+ {
+ PRPollDesc *prev;
+ for (prev = pds; prev < pd; prev++)
+ {
+ prev->out_flags = 0;
+ }
+ }
+ ready += 1; /* this will cause an abrupt return */
+ pd->out_flags = PR_POLL_NVAL; /* bogii */
+ }
+ }
+ }
+ }
+
+ if (0 != ready) return ready; /* no need to block */
+
+ remaining = timeout;
+ start = PR_IntervalNow();
+
+retry:
+ if (timeout != PR_INTERVAL_NO_TIMEOUT)
+ {
+ PRInt32 ticksPerSecond = PR_TicksPerSecond();
+ tv.tv_sec = remaining / ticksPerSecond;
+ tv.tv_usec = remaining - (ticksPerSecond * tv.tv_sec);
+ tv.tv_usec = (PR_USEC_PER_SEC * tv.tv_usec) / ticksPerSecond;
+ tvp = &tv;
+ }
+
+ ready = _MD_SELECT(maxfd + 1, &rd, &wt, &ex, tvp);
+
+ if (ready == -1 && errno == EINTR)
+ {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) goto retry;
+ else
+ {
+ elapsed = (PRIntervalTime) (PR_IntervalNow() - start);
+ if (elapsed > timeout) ready = 0; /* timed out */
+ else
+ {
+ remaining = timeout - elapsed;
+ goto retry;
+ }
+ }
+ }
+
+ /*
+ ** Now to unravel the select sets back into the client's poll
+ ** descriptor list. Is this possibly an area for pissing away
+ ** a few cycles or what?
+ */
+ if (ready > 0)
+ {
+ ready = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ PRInt16 out_flags = 0;
+ if ((NULL != pd->fd) && (0 != pd->in_flags))
+ {
+ PRInt32 osfd;
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom);
+
+ osfd = bottom->secret->md.osfd;
+
+ if (FD_ISSET(osfd, &rd))
+ {
+ if (pd->out_flags & _PR_POLL_READ_SYS_READ)
+ out_flags |= PR_POLL_READ;
+ if (pd->out_flags & _PR_POLL_WRITE_SYS_READ)
+ out_flags |= PR_POLL_WRITE;
+ }
+ if (FD_ISSET(osfd, &wt))
+ {
+ if (pd->out_flags & _PR_POLL_READ_SYS_WRITE)
+ out_flags |= PR_POLL_READ;
+ if (pd->out_flags & _PR_POLL_WRITE_SYS_WRITE)
+ out_flags |= PR_POLL_WRITE;
+ }
+ if (FD_ISSET(osfd, &ex)) out_flags |= PR_POLL_EXCEPT;
+ }
+ pd->out_flags = out_flags;
+ if (out_flags) ready++;
+ }
+ PR_ASSERT(ready > 0);
+ }
+ else if (ready < 0)
+ {
+ err = _MD_ERRNO();
+ if (err == EBADF)
+ {
+ /* Find the bad fds */
+ ready = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ pd->out_flags = 0;
+ if ((NULL != pd->fd) && (0 != pd->in_flags))
+ {
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ if (fcntl(bottom->secret->md.osfd, F_GETFL, 0) == -1)
+ {
+ pd->out_flags = PR_POLL_NVAL;
+ ready++;
+ }
+ }
+ }
+ PR_ASSERT(ready > 0);
+ }
+ else _PR_MD_MAP_SELECT_ERROR(err);
+ }
+
+ return ready;
+} /* NativeThreadSelect */
+#endif /* !defined(_PR_USE_POLL) */
+
+static PRInt32 LocalThreads(
+ PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
+{
+ PRPollDesc *pd, *epd;
+ PRInt32 ready, pdcnt;
+ _PRUnixPollDesc *unixpds, *unixpd;
+
+ /*
+ * 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.
+ */
+
+ unixpd = unixpds = (_PRUnixPollDesc*)
+ PR_MALLOC(npds * sizeof(_PRUnixPollDesc));
+ if (NULL == unixpds)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+
+ ready = 0;
+ for (pdcnt = 0, pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ PRFileDesc *bottom;
+ PRInt16 in_flags_read = 0, in_flags_write = 0;
+ PRInt16 out_flags_read = 0, out_flags_write = 0;
+
+ if ((NULL != pd->fd) && (0 != pd->in_flags))
+ {
+ if (pd->in_flags & PR_POLL_READ)
+ {
+ in_flags_read = (pd->fd->methods->poll)(
+ pd->fd, pd->in_flags & ~PR_POLL_WRITE, &out_flags_read);
+ }
+ if (pd->in_flags & PR_POLL_WRITE)
+ {
+ in_flags_write = (pd->fd->methods->poll)(
+ pd->fd, pd->in_flags & ~PR_POLL_READ, &out_flags_write);
+ }
+ if ((0 != (in_flags_read & out_flags_read))
+ || (0 != (in_flags_write & out_flags_write)))
+ {
+ /* this one's ready right now */
+ if (0 == ready)
+ {
+ /*
+ * We will have to return without calling the
+ * system poll/select function. So zero the
+ * out_flags fields of all the poll descriptors
+ * before this one.
+ */
+ PRPollDesc *prev;
+ for (prev = pds; prev < pd; prev++)
+ {
+ prev->out_flags = 0;
+ }
+ }
+ ready += 1;
+ pd->out_flags = out_flags_read | out_flags_write;
+ }
+ else
+ {
+ pd->out_flags = 0; /* pre-condition */
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom); /* what to do about that? */
+ if ((NULL != bottom)
+ && (_PR_FILEDESC_OPEN == bottom->secret->state))
+ {
+ if (0 == ready)
+ {
+ unixpd->osfd = bottom->secret->md.osfd;
+ unixpd->in_flags = 0;
+ if (in_flags_read & PR_POLL_READ)
+ {
+ unixpd->in_flags |= _PR_UNIX_POLL_READ;
+ pd->out_flags |= _PR_POLL_READ_SYS_READ;
+ }
+ if (in_flags_read & PR_POLL_WRITE)
+ {
+ unixpd->in_flags |= _PR_UNIX_POLL_WRITE;
+ pd->out_flags |= _PR_POLL_READ_SYS_WRITE;
+ }
+ if (in_flags_write & PR_POLL_READ)
+ {
+ unixpd->in_flags |= _PR_UNIX_POLL_READ;
+ pd->out_flags |= _PR_POLL_WRITE_SYS_READ;
+ }
+ if (in_flags_write & PR_POLL_WRITE)
+ {
+ unixpd->in_flags |= _PR_UNIX_POLL_WRITE;
+ pd->out_flags |= _PR_POLL_WRITE_SYS_WRITE;
+ }
+ if ((in_flags_read | in_flags_write) & PR_POLL_EXCEPT)
+ {
+ unixpd->in_flags |= _PR_UNIX_POLL_EXCEPT;
+ }
+ unixpd++; pdcnt++;
+ }
+ }
+ else
+ {
+ if (0 == ready)
+ {
+ PRPollDesc *prev;
+ for (prev = pds; prev < pd; prev++)
+ {
+ prev->out_flags = 0;
+ }
+ }
+ ready += 1; /* this will cause an abrupt return */
+ pd->out_flags = PR_POLL_NVAL; /* bogii */
+ }
+ }
+ }
+ }
+
+ if (0 != ready)
+ {
+ /* no need to block */
+ PR_DELETE(unixpds);
+ return ready;
+ }
+
+ ready = _PR_WaitForMultipleFDs(unixpds, pdcnt, timeout);
+
+ /*
+ * 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++)
+ {
+ PRInt16 out_flags = 0;
+ if ((NULL != pd->fd) && (0 != pd->in_flags))
+ {
+ /*
+ * take errors from the poll operation,
+ * the R/W bits from the request
+ */
+ if (0 != unixpd->out_flags)
+ {
+ if (unixpd->out_flags & _PR_UNIX_POLL_READ)
+ {
+ if (pd->out_flags & _PR_POLL_READ_SYS_READ)
+ out_flags |= PR_POLL_READ;
+ if (pd->out_flags & _PR_POLL_WRITE_SYS_READ)
+ out_flags |= PR_POLL_WRITE;
+ }
+ if (unixpd->out_flags & _PR_UNIX_POLL_WRITE)
+ {
+ if (pd->out_flags & _PR_POLL_READ_SYS_WRITE)
+ out_flags |= PR_POLL_READ;
+ if (pd->out_flags & _PR_POLL_WRITE_SYS_WRITE)
+ out_flags |= PR_POLL_WRITE;
+ }
+ if (unixpd->out_flags & _PR_UNIX_POLL_EXCEPT)
+ out_flags |= PR_POLL_EXCEPT;
+ if (unixpd->out_flags & _PR_UNIX_POLL_ERR)
+ out_flags |= PR_POLL_ERR;
+ if (unixpd->out_flags & _PR_UNIX_POLL_NVAL)
+ out_flags |= PR_POLL_NVAL;
+ if (unixpd->out_flags & _PR_UNIX_POLL_HUP)
+ out_flags |= PR_POLL_HUP;
+ }
+ unixpd++;
+ }
+ pd->out_flags = out_flags;
+ }
+
+ PR_DELETE(unixpds);
+
+ return ready;
+} /* LocalThreads */
+
+#if defined(_PR_USE_POLL)
+#define NativeThreads NativeThreadPoll
+#else
+#define NativeThreads NativeThreadSelect
+#endif
+
+PRInt32 _MD_pr_poll(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
+{
+ 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);
+ return -1;
+ }
+ if (0 == npds) PR_Sleep(timeout);
+ else if (_PR_IS_NATIVE_THREAD(me))
+ rv = NativeThreads(pds, npds, timeout);
+ else rv = LocalThreads(pds, npds, timeout);
+
+ return rv;
+} /* _MD_pr_poll */
+
+#endif /* defined(_PR_PTHREADS) */
+
+/* pruxpoll.c */
+
diff --git a/pr/src/md/unix/uxproces.c b/pr/src/md/unix/uxproces.c
new file mode 100644
index 00000000..c2b7eee5
--- /dev/null
+++ b/pr/src/md/unix/uxproces.c
@@ -0,0 +1,760 @@
+/* -*- 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>
+#if defined(AIX)
+#include <dlfcn.h> /* For dlopen, dlsym, dlclose */
+#endif
+
+/*
+ * HP-UX 9 doesn't have the SA_RESTART flag.
+ */
+#ifndef SA_RESTART
+#define SA_RESTART 0
+#endif
+
+/*
+ **********************************************************************
+ *
+ * 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
+
+#ifdef AIX
+ pid_t (*forkptr)(void); /* Newer versions of AIX (starting in 4.3.2)
+ * have f_fork, which is faster than the
+ * regular fork in a multithreaded process
+ * because it skips calling the fork handlers.
+ * So we look up the f_fork symbol to see if
+ * it's available and fall back on fork.
+ */
+#endif /* AIX */
+} 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;
+ }
+
+#ifdef AIX
+ process->md.pid = (*pr_wp.forkptr)();
+#else
+ process->md.pid = fork();
+#endif
+ 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);
+ }
+ if (attr->currentDirectory) {
+ if (chdir(attr->currentDirectory) < 0) {
+ _exit(1); /* failed */
+ }
+ }
+ }
+
+ if (envp) {
+ (void)execve(path, argv, envp);
+ } else {
+ /* Inherit the environment of the parent. */
+ (void)execv(path, argv);
+ }
+ /* 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
+
+#ifdef AIX
+ {
+ void *handle = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL);
+ pr_wp.forkptr = (pid_t (*)(void)) dlsym(handle, "f_fork");
+ if (!pr_wp.forkptr) {
+ pr_wp.forkptr = fork;
+ }
+ dlclose(handle);
+ }
+#endif /* AIX */
+
+ 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..a6c7cc06
--- /dev/null
+++ b/pr/src/md/unix/uxwrap.c
@@ -0,0 +1,517 @@
+/* -*- 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
+
+/* 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 osfd;
+ _PRUnixPollDesc *unixpds, *unixpd, *eunixpd;
+ PRInt32 pdcnt;
+ 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
+
+#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())) {
+ return _MD_SELECT(width, rd, wr, ex, tv);
+ }
+#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;
+ }
+
+ /*
+ * 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.
+ */
+ unixpds = (_PRUnixPollDesc *) PR_CALLOC(width * sizeof(_PRUnixPollDesc));
+ if (!unixpds) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ pdcnt = 0;
+ unixpd = unixpds;
+ for (osfd = 0; osfd < width; osfd++) {
+ int in_flags = 0;
+ if (rd && FD_ISSET(osfd, rd)) {
+ in_flags |= _PR_UNIX_POLL_READ;
+ }
+ if (wr && FD_ISSET(osfd, wr)) {
+ in_flags |= _PR_UNIX_POLL_WRITE;
+ }
+ if (ex && FD_ISSET(osfd, ex)) {
+ in_flags |= _PR_UNIX_POLL_EXCEPT;
+ }
+ if (in_flags) {
+ unixpd->osfd = osfd;
+ unixpd->in_flags = in_flags;
+ unixpd->out_flags = 0;
+ unixpd++;
+ pdcnt++;
+ }
+ }
+
+ /*
+ * see comments in mozilla/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, rd) && PR_XIsLocked()
+ && (!_pr_xt_hack_okayToReleaseXLock
+ || _pr_xt_hack_okayToReleaseXLock())) {
+ PR_XUnlock();
+ needToLockXAgain = 1;
+ }
+
+ /* This is the potentially blocking step */
+ retVal = _PR_WaitForMultipleFDs(unixpds, pdcnt, 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 */
+ eunixpd = unixpds + pdcnt;
+ for (unixpd = unixpds; unixpd < eunixpd; unixpd++) {
+ if (unixpd->out_flags) {
+ int nbits = 0; /* The number of set bits on for this fd */
+
+ if (unixpd->out_flags & _PR_UNIX_POLL_NVAL) {
+ errno = EBADF;
+ PR_LOG(_pr_io_lm, PR_LOG_ERROR,
+ ("select returns EBADF for %d", unixpd->osfd));
+ retVal = -1;
+ break;
+ }
+ /*
+ * If a socket has a pending error, it is considered
+ * both readable and writable. (See W. Richard Stevens,
+ * Unix Network Programming, Vol. 1, 2nd Ed., Section 6.3,
+ * pp. 153-154.) We also consider a socket readable if
+ * it has a hangup condition.
+ */
+ if (rd && (unixpd->in_flags & _PR_UNIX_POLL_READ)
+ && (unixpd->out_flags & (_PR_UNIX_POLL_READ
+ | _PR_UNIX_POLL_ERR | _PR_UNIX_POLL_HUP))) {
+ FD_SET(unixpd->osfd, rd);
+ nbits++;
+ }
+ if (wr && (unixpd->in_flags & _PR_UNIX_POLL_WRITE)
+ && (unixpd->out_flags & (_PR_UNIX_POLL_WRITE
+ | _PR_UNIX_POLL_ERR))) {
+ FD_SET(unixpd->osfd, wr);
+ nbits++;
+ }
+ if (ex && (unixpd->in_flags & _PR_UNIX_POLL_WRITE)
+ && (unixpd->out_flags & PR_POLL_EXCEPT)) {
+ FD_SET(unixpd->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(unixpds);
+
+ return retVal;
+}
+
+/*
+ * Linux, BSDI, FreeBSD, and Rhapsody don't have poll().
+ */
+
+#if !defined(LINUX) && !defined(FREEBSD) && !defined(BSDI) && !defined(RHAPSODY)
+
+/*
+ *-----------------------------------------------------------------------
+ * 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)
+#elif defined(NETBSD)
+int poll(struct pollfd *filedes, nfds_t nfds, int timeout)
+#elif defined(OPENBSD)
+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
+ struct pollfd *pfd, *epfd;
+ _PRUnixPollDesc *unixpds, *unixpd, *eunixpd;
+ PRIntervalTime ticks;
+ PRInt32 pdcnt;
+ int ready;
+
+ /*
+ * 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())) {
+ return _MD_POLL(filedes, nfds, timeout);
+ }
+#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 {
+ ticks = PR_MillisecondsToInterval(timeout);
+ }
+
+ /* Check for no descriptor case (just do a timeout) */
+ if (nfds == 0) {
+ PR_Sleep(ticks);
+ return 0;
+ }
+
+ unixpds = (_PRUnixPollDesc *)
+ PR_MALLOC(nfds * sizeof(_PRUnixPollDesc));
+ if (NULL == unixpds) {
+ errno = EAGAIN;
+ return -1;
+ }
+
+ pdcnt = 0;
+ epfd = filedes + nfds;
+ unixpd = unixpds;
+ for (pfd = filedes; pfd < epfd; pfd++) {
+ /*
+ * poll() ignores negative fd's.
+ */
+ if (pfd->fd >= 0) {
+ unixpd->osfd = pfd->fd;
+#ifdef _PR_USE_POLL
+ unixpd->in_flags = pfd->events;
+#else
+ /*
+ * Map the poll events to one of the three that can be
+ * represented by the select fd_sets:
+ * POLLIN, POLLRDNORM ===> readable
+ * POLLOUT, POLLWRNORM ===> writable
+ * POLLPRI, POLLRDBAND ===> 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.
+ */
+ unixpd->in_flags = 0;
+ if (pfd->events & (POLLIN
+#ifdef POLLRDNORM
+ | POLLRDNORM
+#endif
+ )) {
+ unixpd->in_flags |= _PR_UNIX_POLL_READ;
+ }
+ if (pfd->events & (POLLOUT
+#ifdef POLLWRNORM
+ | POLLWRNORM
+#endif
+ )) {
+ unixpd->in_flags |= _PR_UNIX_POLL_WRITE;
+ }
+ if (pfd->events & (POLLPRI
+#ifdef POLLRDBAND
+ | POLLRDBAND
+#endif
+ )) {
+ unixpd->in_flags |= PR_POLL_EXCEPT;
+ }
+#endif /* _PR_USE_POLL */
+ unixpd->out_flags = 0;
+ unixpd++;
+ pdcnt++;
+ }
+ }
+
+ ready = _PR_WaitForMultipleFDs(unixpds, pdcnt, ticks);
+ if (-1 == ready) {
+ if (PR_GetError() == PR_PENDING_INTERRUPT_ERROR) {
+ errno = EINTR; /* XXX we aren't interrupted by a signal, but... */
+ } else {
+ errno = PR_GetOSError();
+ }
+ }
+ if (ready <= 0) {
+ goto done;
+ }
+
+ /*
+ * Copy the out_flags from the _PRUnixPollDesc structures to the
+ * user's pollfd structures and free the allocated memory
+ */
+ unixpd = unixpds;
+ for (pfd = filedes; pfd < epfd; pfd++) {
+ pfd->revents = 0;
+ if (pfd->fd >= 0) {
+#ifdef _PR_USE_POLL
+ pfd->revents = unixpd->out_flags;
+#else
+ if (0 != unixpd->out_flags) {
+ if (unixpd->out_flags & _PR_UNIX_POLL_READ) {
+ if (pfd->events & POLLIN) {
+ pfd->revents |= POLLIN;
+ }
+#ifdef POLLRDNORM
+ if (pfd->events & POLLRDNORM) {
+ pfd->revents |= POLLRDNORM;
+ }
+#endif
+ }
+ if (unixpd->out_flags & _PR_UNIX_POLL_WRITE) {
+ if (pfd->events & POLLOUT) {
+ pfd->revents |= POLLOUT;
+ }
+#ifdef POLLWRNORM
+ if (pfd->events & POLLWRNORM) {
+ pfd->revents |= POLLWRNORM;
+ }
+#endif
+ }
+ if (unixpd->out_flags & _PR_UNIX_POLL_EXCEPT) {
+ if (pfd->events & POLLPRI) {
+ pfd->revents |= POLLPRI;
+ }
+#ifdef POLLRDBAND
+ if (pfd->events & POLLRDBAND) {
+ pfd->revents |= POLLRDBAND;
+ }
+#endif
+ }
+ if (unixpd->out_flags & _PR_UNIX_POLL_ERR) {
+ pfd->revents |= POLLERR;
+ }
+ if (unixpd->out_flags & _PR_UNIX_POLL_NVAL) {
+ pfd->revents |= POLLNVAL;
+ }
+ if (unixpd->out_flags & _PR_UNIX_POLL_HUP) {
+ pfd->revents |= POLLHUP;
+ }
+ }
+#endif /* _PR_USE_POLL */
+ unixpd++;
+ }
+ }
+
+done:
+ PR_DELETE(unixpds);
+ return ready;
+}
+
+#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..c513a342
--- /dev/null
+++ b/pr/src/md/windows/Makefile
@@ -0,0 +1,77 @@
+#
+# 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 \
+ w95dllmain.c \
+ $(NULL)
+else
+CSRCS = \
+ ntdllmn.c \
+ 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/ntdllmn.c b/pr/src/md/windows/ntdllmn.c
new file mode 100644
index 00000000..2f660d3d
--- /dev/null
+++ b/pr/src/md/windows/ntdllmn.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.
+ */
+
+/*
+ * The DLL entry point (DllMain) for NSPR.
+ *
+ * The only reason we use DLLMain() now is to find out whether
+ * the NSPR DLL is statically or dynamically loaded. When
+ * dynamically loaded, we cannot use static thread-local storage.
+ * However, static TLS is faster than the TlsXXX() functions.
+ * So we want to use static TLS whenever we can. A global
+ * variable _pr_use_static_tls is set in DllMain() during process
+ * attachment to indicate whether it is safe to use static TLS
+ * or not.
+ */
+
+#include <windows.h>
+#include <primpl.h>
+
+extern BOOL _pr_use_static_tls; /* defined in ntthread.c */
+
+BOOL WINAPI DllMain(
+ HINSTANCE hinstDLL,
+ DWORD fdwReason,
+ LPVOID lpvReserved)
+{
+PRThread *me;
+
+ switch (fdwReason) {
+ case DLL_PROCESS_ATTACH:
+ /*
+ * If lpvReserved is NULL, we are dynamically loaded
+ * and therefore can't use static thread-local storage.
+ */
+ if (lpvReserved == NULL) {
+ _pr_use_static_tls = FALSE;
+ } else {
+ _pr_use_static_tls = TRUE;
+ }
+ break;
+ case DLL_THREAD_ATTACH:
+ break;
+ case DLL_THREAD_DETACH:
+ if (_pr_initialized) {
+ me = _MD_GET_ATTACHED_THREAD();
+ if ((me != NULL) && (me->flags & _PR_ATTACHED))
+ _PRI_DetachThread();
+ }
+ break;
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
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..32f52cc0
--- /dev/null
+++ b/pr/src/md/windows/ntinrval.c
@@ -0,0 +1,84 @@
+/* -*- 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()
+{
+ 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..0e09cd8b
--- /dev/null
+++ b/pr/src/md/windows/ntio.c
@@ -0,0 +1,3912 @@
+/* -*- 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 "pprmwait.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;
+DWORD _pr_io_restartedIOIndex; /* The thread local storage slot for each
+ * thread is initialized to 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_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;
+ _MDOverlapped *mdOlp;
+ 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;
+
+ mdOlp = (_MDOverlapped *)olp;
+
+ if (mdOlp->ioModel == _MD_MultiWaitIO) {
+ PRRecvWait *desc;
+ PRWaitGroup *group;
+ PRThread *thred = NULL;
+ PRMWStatus mwstatus;
+
+ desc = mdOlp->data.mw.desc;
+ PR_ASSERT(desc != NULL);
+ mwstatus = rv ? PR_MW_SUCCESS : PR_MW_FAILURE;
+ if (InterlockedCompareExchange((PVOID *)&desc->outcome,
+ (PVOID)mwstatus, (PVOID)PR_MW_PENDING)
+ == (PVOID)PR_MW_PENDING) {
+ if (mwstatus == PR_MW_SUCCESS) {
+ desc->bytesRecv = bytes;
+ } else {
+ mdOlp->data.mw.error = GetLastError();
+ }
+ }
+ group = mdOlp->data.mw.group;
+ PR_ASSERT(group != NULL);
+
+ _PR_MD_LOCK(&group->mdlock);
+ PR_APPEND_LINK(&mdOlp->data.mw.links, &group->io_ready);
+ PR_ASSERT(desc->fd != NULL);
+ NT_HashRemoveInternal(group, desc->fd);
+ if (!PR_CLIST_IS_EMPTY(&group->wait_list)) {
+ thred = _PR_THREAD_CONDQ_PTR(PR_LIST_HEAD(&group->wait_list));
+ PR_REMOVE_LINK(&thred->waitQLinks);
+ }
+ _PR_MD_UNLOCK(&group->mdlock);
+
+ if (thred) {
+ if (!_PR_IS_NATIVE_THREAD(thred)) {
+ int pri = thred->priority;
+ _PRCPU *lockedCPU = _PR_MD_CURRENT_CPU();
+ _PR_THREAD_LOCK(thred);
+ if (thred->flags & _PR_ON_PAUSEQ) {
+ _PR_SLEEPQ_LOCK(thred->cpu);
+ _PR_DEL_SLEEPQ(thred, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(thred->cpu);
+ _PR_THREAD_UNLOCK(thred);
+ thred->cpu = lockedCPU;
+ thred->state = _PR_RUNNABLE;
+ _PR_RUNQ_LOCK(lockedCPU);
+ _PR_ADD_RUNQ(thred, lockedCPU, pri);
+ _PR_RUNQ_UNLOCK(lockedCPU);
+ } else {
+ /*
+ * The thread was just interrupted and moved
+ * from the pause queue to the run queue.
+ */
+ _PR_THREAD_UNLOCK(thred);
+ }
+ } else {
+ _PR_THREAD_LOCK(thred);
+ thred->state = _PR_RUNNABLE;
+ _PR_THREAD_UNLOCK(thred);
+ ReleaseSemaphore(thred->md.blocked_sema, 1, NULL);
+ }
+ }
+ } else {
+ PRThread *completed_io;
+
+ PR_ASSERT(mdOlp->ioModel == _MD_BlockingIO);
+ completed_io = _PR_THREAD_MD_TO_PTR(mdOlp->data.mdThread);
+ 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;
+
+ _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)) {
+ if (_pr_use_static_tls) {
+ _pr_io_restarted_io = thread;
+ } else {
+ TlsSetValue(_pr_io_restartedIOIndex, 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.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.
+ */
+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) {
+ /*
+ * Call Sleep(0) to work around a Winsock timing bug.
+ */
+ Sleep(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 (me->io_suspended) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return -1;
+ }
+
+ 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.overlapped), 0, sizeof(OVERLAPPED));
+
+ 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.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 fast, _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;
+ PR_FreeFileDesc(nd);
+ }
+ return bytes;
+ }
+
+ if (me->io_suspended) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return -1;
+ }
+
+ 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.overlapped), 0, sizeof(OVERLAPPED));
+
+ 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.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 (!fast)
+ _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 (me->io_suspended) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return -1;
+ }
+
+ 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.overlapped), 0, sizeof(OVERLAPPED));
+
+ 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.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 (me->io_suspended) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return -1;
+ }
+
+ 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.overlapped), 0, sizeof(OVERLAPPED));
+
+ me->io_pending = PR_TRUE;
+ me->io_fd = osfd;
+ me->state = _PR_IO_WAIT;
+ rv = ReadFile((HANDLE)osfd,
+ buf,
+ amount,
+ &bytes,
+ &(me->md.overlapped.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 (me->io_suspended) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return -1;
+ }
+
+ 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.overlapped), 0, sizeof(OVERLAPPED));
+
+ me->io_pending = PR_TRUE;
+ me->io_fd = osfd;
+ me->state = _PR_IO_WAIT;
+ rv = WriteFile((HANDLE)osfd,
+ buf,
+ amount,
+ &bytes,
+ &(me->md.overlapped.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();
+
+ if (me->io_suspended) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return -1;
+ }
+
+ memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
+
+ me->md.overlapped.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.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;
+
+ if (_nt_use_async && !fd->secret->md.nonoverlapped) {
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (me->io_suspended) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return -1;
+ }
+
+ memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
+
+ me->md.overlapped.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.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)
+{
+ PRUint64 result;
+ PRUint32 position, uhi;
+ PRInt32 low = (PRInt32)offset, hi = (PRInt32)(offset >> 32);
+
+ position = SetFilePointer((HANDLE)fd->secret->md.osfd, low, &hi, whence);
+
+ /*
+ * The lpDistanceToMoveHigh argument (third argument) is not
+ * NULL. Therefore, a -1 (unsigned) result is ambiguious. If
+ * the result just happens to be -1, also test to see if the
+ * last error is non-zero. If it is, the operation failed.
+ * Otherwise, the -1 is just the low half of the 64 bit position.
+ */
+ if (0xffffffff == position)
+ {
+ PRInt32 oserr = GetLastError();
+ if (0 != oserr)
+ {
+ _PR_MD_MAP_LSEEK_ERROR(oserr);
+ return -1;
+ }
+ }
+
+ /*
+ ** All this 'cause we keep extending the sign of rv into
+ ** the high bits of the result. We just know that the final
+ ** position of the file must be positive and probably nowhere
+ ** close to the maximum value of a PRUint64.
+ */
+ uhi = (PRUint32)hi;
+ PR_ASSERT((PRInt32)uhi >= 0);
+ result = uhi;
+ PR_ASSERT((PRInt64)result >= 0);
+ result = (result << 32);
+ PR_ASSERT((PRInt64)result >= 0);
+ result += position;
+ PR_ASSERT((PRInt64)result >= 0);
+ return (PRInt64)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.
+ */
+
+ BOOL ok = FlushFileBuffers((HANDLE)fd->secret->md.osfd);
+
+ 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 ];
+ int len;
+
+ len = strlen(name);
+ /* Need 5 bytes for \*.* and the trailing null byte. */
+ if (len + 5 > MAX_PATH) {
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
+ return PR_FAILURE;
+ }
+ strcpy(filename, name);
+
+ /*
+ * If 'name' ends in a slash or backslash, do not append
+ * another backslash.
+ */
+ if (filename[len - 1] == '/' || filename[len - 1] == '\\') {
+ len--;
+ }
+ strcpy(&filename[len], "\\*.*");
+ 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();
+
+ if (me->io_suspended) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
+
+ me->state = _PR_IO_WAIT;
+ me->io_pending = PR_TRUE;
+ rv = LockFileEx((HANDLE)f,
+ LOCKFILE_EXCLUSIVE_LOCK,
+ 0,
+ 0x7fffffff,
+ 0,
+ &me->md.overlapped.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();
+
+ if (me->io_suspended) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
+
+ 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.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();
+
+ if (me->io_suspended) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
+
+ rv = UnlockFileEx((HANDLE)f,
+ 0,
+ 0x7fffffff,
+ 0,
+ &me->md.overlapped.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 */
+
+PR_IMPLEMENT(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 = (PRNetAddr*)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..869bc897
--- /dev/null
+++ b/pr/src/md/windows/ntmisc.c
@@ -0,0 +1,719 @@
+/* -*- 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)
+ *
+ *-----------------------------------------------------------------------
+ */
+
+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;
+}
+
+/*
+ * 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;
+ char *cwd = NULL; /* current working directory */
+ 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;
+ }
+ cwd = attr->currentDirectory;
+ }
+
+ 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 */
+ cwd, /* 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;
+}
+
+/*
+ ***********************************************************************
+ *
+ * Atomic increment and decrement operations for x86 processors
+ *
+ * We don't use InterlockedIncrement and InterlockedDecrement
+ * because on NT 3.51 and Win95, they return a number with
+ * the same sign as the incremented/decremented result, rather
+ * than the result itself. On NT 4.0 these functions do return
+ * the incremented/decremented result.
+ *
+ * The result is returned in the eax register by the inline
+ * assembly code. We disable the harmless "no return value"
+ * warning (4035) for these two functions.
+ *
+ ***********************************************************************
+ */
+
+#if defined(_M_IX86) || defined(_X86_)
+
+#pragma warning(disable: 4035)
+PRInt32 _PR_MD_ATOMIC_INCREMENT(PRInt32 *val)
+{
+ __asm
+ {
+ mov ecx, val
+ mov eax, 1
+ lock xadd dword ptr [ecx], eax
+ inc eax
+ }
+}
+#pragma warning(default: 4035)
+
+#pragma warning(disable: 4035)
+PRInt32 _PR_MD_ATOMIC_DECREMENT(PRInt32 *val)
+{
+ __asm
+ {
+ mov ecx, val
+ mov eax, 0ffffffffh
+ lock xadd dword ptr [ecx], eax
+ dec eax
+ }
+}
+#pragma warning(default: 4035)
+
+#pragma warning(disable: 4035)
+PRInt32 _PR_MD_ATOMIC_ADD(PRInt32 *intp, PRInt32 val)
+{
+ __asm
+ {
+ mov ecx, intp
+ mov eax, val
+ mov ebx, val
+ lock xadd dword ptr [ecx], eax
+ add eax, ebx
+ }
+}
+#pragma warning(default: 4035)
+
+#ifdef _PR_HAVE_ATOMIC_CAS
+
+#pragma warning(disable: 4035)
+void
+PR_StackPush(PRStack *stack, PRStackElem *stack_elem)
+{
+ __asm
+ {
+ mov ebx, stack
+ mov ecx, stack_elem
+retry: mov eax,[ebx]
+ cmp eax,-1
+ je retry
+ mov eax,-1
+ xchg dword ptr [ebx], eax
+ cmp eax,-1
+ je retry
+ mov [ecx],eax
+ mov [ebx],ecx
+ }
+}
+#pragma warning(default: 4035)
+
+#pragma warning(disable: 4035)
+PRStackElem *
+PR_StackPop(PRStack *stack)
+{
+ __asm
+ {
+ mov ebx, stack
+retry: mov eax,[ebx]
+ cmp eax,-1
+ je retry
+ mov eax,-1
+ xchg dword ptr [ebx], eax
+ cmp eax,-1
+ je retry
+ cmp eax,0
+ je empty
+ mov ecx,[eax]
+ mov [ebx],ecx
+ mov [eax],0
+ jmp done
+empty:
+ mov [ebx],eax
+done:
+ }
+}
+#pragma warning(default: 4035)
+
+#endif /* _PR_HAVE_ATOMIC_CAS */
+
+#endif /* x86 processors */
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..1f6de5ac
--- /dev/null
+++ b/pr/src/md/windows/ntthread.c
@@ -0,0 +1,486 @@
+/* -*- 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 } };
+
+BOOL _pr_use_static_tls = TRUE;
+__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;
+DWORD _pr_currentFiberIndex;
+DWORD _pr_lastFiberIndex;
+DWORD _pr_currentCPUIndex;
+DWORD _pr_intsOffIndex;
+
+_MDLock _nt_idleLock;
+PRCList _nt_idleList;
+PRUint32 _nt_idleCount;
+
+extern __declspec(thread) PRThread *_pr_io_restarted_io;
+extern DWORD _pr_io_restartedIOIndex;
+
+/* Must check the restarted_io *before* decrementing no_sched to 0 */
+#define POST_SWITCH_WORK() \
+ PR_BEGIN_MACRO \
+ PRThread *restarted_io = \
+ (_pr_use_static_tls ? _pr_io_restarted_io \
+ : (PRThread *) TlsGetValue(_pr_io_restartedIOIndex)); \
+ if (restarted_io) { \
+ _nt_handle_restarted_io(restarted_io); \
+ } \
+ _PR_MD_LAST_THREAD()->no_sched = 0; \
+ PR_END_MACRO
+
+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);
+
+ if (_pr_use_static_tls) {
+ _pr_io_restarted_io = NULL;
+ } else {
+ TlsSetValue(_pr_io_restartedIOIndex, 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
+
+ if (!_pr_use_static_tls) {
+ _pr_currentFiberIndex = TlsAlloc();
+ _pr_lastFiberIndex = TlsAlloc();
+ _pr_currentCPUIndex = TlsAlloc();
+ _pr_intsOffIndex = TlsAlloc();
+ _pr_io_restartedIOIndex = TlsAlloc();
+ }
+}
+
+void _PR_MD_CLEANUP_BEFORE_EXIT(void)
+{
+ WSACleanup();
+
+ if (!_pr_use_static_tls) {
+ TlsFree(_pr_currentFiberIndex);
+ TlsFree(_pr_lastFiberIndex);
+ TlsFree(_pr_currentCPUIndex);
+ TlsFree(_pr_intsOffIndex);
+ TlsFree(_pr_io_restartedIOIndex);
+ }
+}
+
+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)
+{
+ thread->md.overlapped.ioModel = _MD_BlockingIO;
+ thread->md.overlapped.data.mdThread = &thread->md;
+ /* 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)
+{
+ 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.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);
+ }
+}
+
+PRThread*
+_MD_CURRENT_THREAD(void)
+{
+PRThread *thread;
+
+ thread = _MD_GET_ATTACHED_THREAD();
+
+ if (NULL == thread) {
+ thread = _PRI_AttachThread(
+ PR_USER_THREAD, PR_PRIORITY_NORMAL, NULL, 0);
+ }
+ PR_ASSERT(thread != NULL);
+ return thread;
+}
+
diff --git a/pr/src/md/windows/w16callb.c b/pr/src/md/windows/w16callb.c
new file mode 100644
index 00000000..9c9a4a79
--- /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, 1, 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..dac82384
--- /dev/null
+++ b/pr/src/md/windows/w16mem.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.
+ */
+
+/*******************************************************************
+** 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->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..bd505697
--- /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
+PR_IMPLEMENT(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..5b73124d
--- /dev/null
+++ b/pr/src/md/windows/w32poll.c
@@ -0,0 +1,288 @@
+/* -*- 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)
+{
+ int ready, err;
+ fd_set rd, wt, ex;
+ PRFileDesc *bottom;
+ PRPollDesc *pd, *epd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ struct timeval tv, *tvp = NULL;
+
+ if (_PR_PENDING_INTERRUPT(me))
+ {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ }
+
+ /*
+ ** Is it an empty set? If so, just sleep for the timeout and return
+ */
+ if (0 == npds)
+ {
+ PR_Sleep(timeout);
+ return 0;
+ }
+
+ FD_ZERO(&rd);
+ FD_ZERO(&wt);
+ FD_ZERO(&ex);
+
+ ready = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ SOCKET osfd;
+ PRInt16 in_flags_read = 0, in_flags_write = 0;
+ PRInt16 out_flags_read = 0, out_flags_write = 0;
+
+ if ((NULL != pd->fd) && (0 != pd->in_flags))
+ {
+ if (pd->in_flags & PR_POLL_READ)
+ {
+ in_flags_read = (pd->fd->methods->poll)(
+ pd->fd, (PRInt16)(pd->in_flags & ~PR_POLL_WRITE),
+ &out_flags_read);
+ }
+ if (pd->in_flags & PR_POLL_WRITE)
+ {
+ in_flags_write = (pd->fd->methods->poll)(
+ pd->fd, (PRInt16)(pd->in_flags & ~PR_POLL_READ),
+ &out_flags_write);
+ }
+ if ((0 != (in_flags_read & out_flags_read))
+ || (0 != (in_flags_write & out_flags_write)))
+ {
+ /* this one's ready right now (buffered input) */
+ if (0 == ready)
+ {
+ /*
+ * We will have to return without calling the
+ * system poll/select function. So zero the
+ * out_flags fields of all the poll descriptors
+ * before this one.
+ */
+ PRPollDesc *prev;
+ for (prev = pds; prev < pd; prev++)
+ {
+ prev->out_flags = 0;
+ }
+ }
+ ready += 1;
+ pd->out_flags = out_flags_read | out_flags_write;
+ }
+ else
+ {
+ pd->out_flags = 0; /* pre-condition */
+ /* make sure this is an NSPR supported stack */
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom); /* what to do about that? */
+ if ((NULL != bottom)
+ && (_PR_FILEDESC_OPEN == bottom->secret->state))
+ {
+ if (0 == ready)
+ {
+ osfd = (SOCKET) bottom->secret->md.osfd;
+ if (in_flags_read & PR_POLL_READ)
+ {
+ pd->out_flags |= _PR_POLL_READ_SYS_READ;
+ FD_SET(osfd, &rd);
+ }
+ if (in_flags_read & PR_POLL_WRITE)
+ {
+ pd->out_flags |= _PR_POLL_READ_SYS_WRITE;
+ FD_SET(osfd, &wt);
+ }
+ if (in_flags_write & PR_POLL_READ)
+ {
+ pd->out_flags |= _PR_POLL_WRITE_SYS_READ;
+ FD_SET(osfd, &rd);
+ }
+ if (in_flags_write & PR_POLL_WRITE)
+ {
+ pd->out_flags |= _PR_POLL_WRITE_SYS_WRITE;
+ FD_SET(osfd, &wt);
+ }
+ if (pd->in_flags & PR_POLL_EXCEPT) FD_SET(osfd, &ex);
+ }
+ }
+ else
+ {
+ if (0 == ready)
+ {
+ PRPollDesc *prev;
+ for (prev = pds; prev < pd; prev++)
+ {
+ prev->out_flags = 0;
+ }
+ }
+ ready += 1; /* this will cause an abrupt return */
+ pd->out_flags = PR_POLL_NVAL; /* bogii */
+ }
+ }
+ }
+ }
+
+ if (0 != ready) return ready; /* no need to block */
+
+ if (timeout != PR_INTERVAL_NO_TIMEOUT)
+ {
+ PRInt32 ticksPerSecond = PR_TicksPerSecond();
+ tv.tv_sec = timeout / ticksPerSecond;
+ tv.tv_usec = timeout - (ticksPerSecond * tv.tv_sec);
+ tv.tv_usec = (PR_USEC_PER_SEC * tv.tv_usec) / ticksPerSecond;
+ tvp = &tv;
+ }
+
+#if defined(_PR_GLOBAL_THREADS_ONLY)
+ ready = _MD_SELECT(0, &rd, &wt, &ex, tvp);
+#else
+ if (_PR_IS_NATIVE_THREAD(me)) {
+ ready = _MD_SELECT(0, &rd, &wt, &ex, tvp);
+ }
+ else
+ {
+ /*
+ ** Creating a new thread on each call to Poll()!!
+ ** I guess web server doesn't use non-block I/O.
+ */
+ 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);
+ ready = data.status;
+ if (ready == SOCKET_ERROR) WSASetLastError(data.error);
+ }
+#endif
+
+ /*
+ ** Now to unravel the select sets back into the client's poll
+ ** descriptor list. Is this possibly an area for pissing away
+ ** a few cycles or what?
+ */
+ if (ready > 0)
+ {
+ ready = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ PRInt16 out_flags = 0;
+ if ((NULL != pd->fd) && (0 != pd->in_flags))
+ {
+ SOCKET osfd;
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom);
+
+ osfd = (SOCKET) bottom->secret->md.osfd;
+
+ if (FD_ISSET(osfd, &rd))
+ {
+ if (pd->out_flags & _PR_POLL_READ_SYS_READ)
+ out_flags |= PR_POLL_READ;
+ if (pd->out_flags & _PR_POLL_WRITE_SYS_READ)
+ out_flags |= PR_POLL_WRITE;
+ }
+ if (FD_ISSET(osfd, &wt))
+ {
+ if (pd->out_flags & _PR_POLL_READ_SYS_WRITE)
+ out_flags |= PR_POLL_READ;
+ if (pd->out_flags & _PR_POLL_WRITE_SYS_WRITE)
+ out_flags |= PR_POLL_WRITE;
+ }
+ if (FD_ISSET(osfd, &ex)) out_flags |= PR_POLL_EXCEPT;
+ }
+ pd->out_flags = out_flags;
+ if (out_flags) ready++;
+ }
+ PR_ASSERT(ready > 0);
+ }
+ else if (ready == SOCKET_ERROR)
+ {
+ err = WSAGetLastError();
+ if (err == WSAENOTSOCK)
+ {
+ /* Find the bad fds */
+ int optval;
+ int optlen = sizeof(optval);
+ ready = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++)
+ {
+ pd->out_flags = 0;
+ if ((NULL != pd->fd) && (0 != pd->in_flags))
+ {
+ bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+ 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;
+ ready++;
+ }
+ }
+ }
+ }
+ PR_ASSERT(ready > 0);
+ }
+ else _PR_MD_MAP_SELECT_ERROR(err);
+ }
+
+ return ready;
+}
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/w95dllmain.c b/pr/src/md/windows/w95dllmain.c
new file mode 100644
index 00000000..1ef299d3
--- /dev/null
+++ b/pr/src/md/windows/w95dllmain.c
@@ -0,0 +1,52 @@
+/* -*- 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.
+ */
+
+/*
+ * The DLL entry point (DllMain) for NSPR.
+ *
+ * This is used to detach threads that were automatically attached by
+ * nspr.
+ */
+
+#include <windows.h>
+#include <primpl.h>
+
+BOOL WINAPI DllMain(
+ HINSTANCE hinstDLL,
+ DWORD fdwReason,
+ LPVOID lpvReserved)
+{
+PRThread *me;
+
+ switch (fdwReason) {
+ case DLL_PROCESS_ATTACH:
+ break;
+ case DLL_THREAD_ATTACH:
+ break;
+ case DLL_THREAD_DETACH:
+ if (_pr_initialized) {
+ me = _MD_GET_ATTACHED_THREAD();
+ if ((me != NULL) && (me->flags & _PR_ATTACHED))
+ _PRI_DetachThread();
+ }
+ break;
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
diff --git a/pr/src/md/windows/w95io.c b/pr/src/md/windows/w95io.c
new file mode 100644
index 00000000..173a3a58
--- /dev/null
+++ b/pr/src/md/windows/w95io.c
@@ -0,0 +1,885 @@
+/* -*- 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.
+ */
+
+ BOOL ok = FlushFileBuffers((HANDLE)fd->secret->md.osfd);
+
+ 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 ];
+ int len;
+
+ len = strlen(name);
+ /* Need 5 bytes for \*.* and the trailing null byte. */
+ if (len + 5 > MAX_PATH) {
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
+ return PR_FAILURE;
+ }
+ strcpy(filename, name);
+
+ /*
+ * If 'name' ends in a slash or backslash, do not append
+ * another backslash.
+ */
+ if (filename[len - 1] == '/' || filename[len - 1] == '\\') {
+ len--;
+ }
+ strcpy(&filename[len], "\\*.*");
+ 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..75030f55
--- /dev/null
+++ b/pr/src/md/windows/w95sock.c
@@ -0,0 +1,612 @@
+/* -*- 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"
+
+#define READ_FD 1
+#define WRITE_FD 2
+#define CONNECT_FD 3
+
+static PRInt32 socket_io_wait(
+ PRInt32 osfd,
+ PRInt32 fd_type,
+ PRIntervalTime timeout);
+
+
+/* --- SOCKET IO --------------------------------------------------------- */
+
+
+PRInt32
+_PR_MD_SOCKET(int af, int type, int flags)
+{
+ SOCKET sock;
+ u_long one = 1;
+
+ sock = socket(af, type, flags);
+
+ if (sock == INVALID_SOCKET )
+ {
+ _PR_MD_MAP_SOCKET_ERROR(WSAGetLastError());
+ return (PRInt32)sock;
+ }
+
+ /*
+ ** Make the socket Non-Blocking
+ */
+ if (ioctlsocket( sock, FIONBIO, &one) != 0)
+ {
+ PR_SetError(PR_UNKNOWN_ERROR, WSAGetLastError());
+ closesocket(sock);
+ return -1;
+ }
+
+ return (PRInt32)sock;
+}
+
+/*
+** _MD_CloseSocket() -- Close a socket
+**
+*/
+PRInt32
+_MD_CloseSocket(PRInt32 osfd)
+{
+ PRInt32 rv;
+
+ rv = closesocket((SOCKET) osfd );
+ if (rv < 0)
+ _PR_MD_MAP_CLOSE_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;
+
+ while ((rv = accept(osfd, (struct sockaddr *) raddr, rlen)) == -1)
+ {
+ err = WSAGetLastError();
+ if ((err == WSAEWOULDBLOCK) && (!fd->secret->nonblocking))
+ {
+ if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0)
+ {
+ return(-1);
+ }
+ }
+ else
+ {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ break;
+ }
+ }
+ 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;
+
+ if ((rv = connect(osfd, (struct sockaddr *) addr, addrlen)) == -1)
+ {
+ err = WSAGetLastError();
+ if ((!fd->secret->nonblocking) && (err == WSAEWOULDBLOCK))
+ {
+ rv = socket_io_wait(osfd, CONNECT_FD, timeout);
+ if ( rv < 0 )
+ {
+ return(-1);
+ }
+ else
+ {
+ PR_ASSERT(rv > 0);
+ /* it's connected */
+ return(0);
+ }
+ }
+ _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;
+
+ while ((rv = recv( osfd, buf, amount, 0)) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ rv = socket_io_wait(osfd, READ_FD, timeout);
+ if ( rv < 0 )
+ {
+ return -1;
+ }
+ }
+ 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;
+ PRInt32 bytesSent = 0;
+
+ while(bytesSent < amount )
+ {
+ while ((rv = send( osfd, buf, amount, 0 )) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ rv = socket_io_wait(osfd, WRITE_FD, timeout);
+ if ( rv < 0 )
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ _PR_MD_MAP_SEND_ERROR(err);
+ return -1;
+ }
+ }
+ bytesSent += rv;
+ if (fd->secret->nonblocking)
+ {
+ break;
+ }
+ if (bytesSent < amount)
+ {
+ rv = socket_io_wait(osfd, WRITE_FD, timeout);
+ if ( rv < 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;
+
+ while(bytesSent < amount)
+ {
+ while ((rv = sendto( osfd, buf, amount, 0, (struct sockaddr *) addr,
+ addrlen)) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ rv = socket_io_wait(osfd, WRITE_FD, timeout);
+ if ( rv < 0 )
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ _PR_MD_MAP_SENDTO_ERROR(err);
+ return -1;
+ }
+ }
+ bytesSent += rv;
+ if (fd->secret->nonblocking)
+ {
+ break;
+ }
+ if (bytesSent < amount)
+ {
+ rv = socket_io_wait(osfd, WRITE_FD, timeout);
+ if (rv < 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;
+
+ while ((rv = recvfrom( osfd, buf, amount, 0, (struct sockaddr *) addr,
+ addrlen)) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ rv = socket_io_wait(osfd, READ_FD, timeout);
+ if ( rv < 0)
+ {
+ return -1;
+ }
+ }
+ 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 */
+}
+
+
+
+/*
+ * socket_io_wait --
+ *
+ * Wait for socket i/o, periodically checking for interrupt.
+ *
+ * This function returns 1 on success. On failure, it returns
+ * -1 and sets the error codes. It never returns 0.
+ */
+#define _PR_INTERRUPT_CHECK_INTERVAL_SECS 5
+
+static PRInt32 socket_io_wait(
+ PRInt32 osfd,
+ PRInt32 fd_type,
+ PRIntervalTime timeout)
+{
+ PRInt32 rv = -1;
+ struct timeval tv;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRIntervalTime elapsed, remaining;
+ fd_set rd_wr, ex;
+ int err, len;
+
+ 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;
+ FD_ZERO(&rd_wr);
+ FD_ZERO(&ex);
+ do {
+ FD_SET(osfd, &rd_wr);
+ FD_SET(osfd, &ex);
+ switch( fd_type )
+ {
+ case READ_FD:
+ rv = _MD_SELECT(osfd + 1, &rd_wr, NULL, NULL, &tv);
+ break;
+ case WRITE_FD:
+ rv = _MD_SELECT(osfd + 1, NULL, &rd_wr, NULL, &tv);
+ break;
+ case CONNECT_FD:
+ rv = _MD_SELECT(osfd + 1, NULL, &rd_wr, &ex, &tv);
+ break;
+ default:
+ PR_ASSERT(0);
+ break;
+ } /* end switch() */
+ if (rv == -1 )
+ {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ if ( rv > 0 && fd_type == CONNECT_FD )
+ {
+ /*
+ * Call Sleep(0) to work around a Winsock timing bug.
+ */
+ Sleep(0);
+ if (FD_ISSET((SOCKET)osfd, &ex))
+ {
+ 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, &rd_wr))
+ {
+ /* it's connected */
+ return 1;
+ }
+ PR_ASSERT(0);
+ }
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ } while (rv == 0);
+ break;
+ default:
+ remaining = timeout;
+ FD_ZERO(&rd_wr);
+ FD_ZERO(&ex);
+ 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);
+ FD_SET(osfd, &ex);
+ switch( fd_type )
+ {
+ case READ_FD:
+ rv = _MD_SELECT(osfd + 1, &rd_wr, NULL, NULL, &tv);
+ break;
+ case WRITE_FD:
+ rv = _MD_SELECT(osfd + 1, NULL, &rd_wr, NULL, &tv);
+ break;
+ case CONNECT_FD:
+ rv = _MD_SELECT(osfd + 1, NULL, &rd_wr, &ex, &tv);
+ break;
+ default:
+ PR_ASSERT(0);
+ break;
+ } /* end switch() */
+ if (rv == -1)
+ {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ if ( rv > 0 && fd_type == CONNECT_FD )
+ {
+ /*
+ * Call Sleep(0) to work around a Winsock timing bug.
+ */
+ Sleep(0);
+ if (FD_ISSET((SOCKET)osfd, &ex))
+ {
+ 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, &rd_wr))
+ {
+ /* it's connected */
+ return 1;
+ }
+ PR_ASSERT(0);
+ }
+ 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 and the
+ * timeout deadline has not passed yet.
+ */
+ if (rv == 0 )
+ {
+ elapsed = PR_SecondsToInterval(tv.tv_sec)
+ + PR_MicrosecondsToInterval(tv.tv_usec);
+ if (elapsed >= remaining) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ break;
+ } else {
+ remaining = remaining - elapsed;
+ }
+ }
+ } while (rv == 0 );
+ break;
+ }
+ return(rv);
+} /* end socket_io_wait() */
diff --git a/pr/src/md/windows/w95thred.c b/pr/src/md/windows/w95thred.c
new file mode 100644
index 00000000..89103bb2
--- /dev/null
+++ b/pr/src/md/windows/w95thred.c
@@ -0,0 +1,257 @@
+/* -*- 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 ------------------------------------------------ */
+#ifdef _PR_USE_STATIC_TLS
+__declspec(thread) struct PRThread *_pr_thread_last_run;
+__declspec(thread) struct PRThread *_pr_currentThread;
+__declspec(thread) struct _PRCPU *_pr_currentCPU;
+#else
+DWORD _pr_currentThreadIndex;
+DWORD _pr_lastThreadIndex;
+DWORD _pr_currentCPUIndex;
+#endif
+int _pr_intsOff = 0;
+_PRInterruptTable _pr_interruptTable[] = { { 0 } };
+
+void
+_PR_MD_EARLY_INIT()
+{
+ _PR_Win32InitTimeZone();
+
+#ifndef _PR_USE_STATIC_TLS
+ _pr_currentThreadIndex = TlsAlloc();
+ _pr_lastThreadIndex = TlsAlloc();
+ _pr_currentCPUIndex = TlsAlloc();
+#endif
+}
+
+void _PR_MD_CLEANUP_BEFORE_EXIT(void)
+{
+ WSACleanup();
+
+#ifndef _PR_USE_STATIC_TLS
+ TlsFree(_pr_currentThreadIndex);
+ TlsFree(_pr_lastThreadIndex);
+ TlsFree(_pr_currentCPUIndex);
+#endif
+}
+
+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);
+ }
+}
+
+PRThread*
+_MD_CURRENT_THREAD(void)
+{
+PRThread *thread;
+
+ thread = _MD_GET_ATTACHED_THREAD();
+
+ if (NULL == thread) {
+ thread = _PRI_AttachThread(
+ PR_USER_THREAD, PR_PRIORITY_NORMAL, NULL, 0);
+ }
+ PR_ASSERT(thread != NULL);
+}
diff --git a/pr/src/md/windows/win32_errors.c b/pr/src/md/windows/win32_errors.c
new file mode 100644
index 00000000..1899a1be
--- /dev/null
+++ b/pr/src/md/windows/win32_errors.c
@@ -0,0 +1,1240 @@
+/* -*- 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 "prlog.h"
+#include <errno.h>
+#include <windows.h>
+
+/*
+ * On Win32, we map three kinds of error codes:
+ * - GetLastError(): for Win32 functions
+ * - WSAGetLastError(): for Winsock functions
+ * - errno: for standard C library functions
+ *
+ * We do not check for WSAEINPROGRESS and WSAEINTR because we do not
+ * use blocking Winsock 1.1 calls.
+ *
+ * Except for the 'socket' call, we do not check for WSAEINITIALISED.
+ * It is assumed that if Winsock is not initialized, that fact will
+ * be detected at the time we create new sockets.
+ */
+
+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:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_BUSY_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_NOACCESS:
+ 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:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_BUSY_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_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:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_BUSY_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_INVALID_PARAMETER:
+ 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:
+ case ERROR_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_BUSY_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;
+ 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;
+ }
+}
+
+/*
+ * For both CloseHandle() and closesocket().
+ */
+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;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAENETDOWN:
+ PR_SetError(PR_NETWORK_DOWN_ERROR, err);
+ break;
+ case WSAEWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_socket_error(PRInt32 err)
+{
+ PR_ASSERT(err != WSANOTINITIALISED);
+ switch (err) {
+ case WSAENETDOWN:
+ PR_SetError(PR_NETWORK_DOWN_ERROR, err);
+ break;
+ case WSAEPROTONOSUPPORT:
+ PR_SetError(PR_PROTOCOL_NOT_SUPPORTED_ERROR, err);
+ break;
+ case WSAEACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case WSAEMFILE:
+ PR_SetError(PR_PROC_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ case WSAENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case WSAEAFNOSUPPORT:
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err);
+ break;
+ case WSAEPROTOTYPE:
+ case WSAESOCKTNOSUPPORT:
+ PR_SetError(PR_INVALID_ARGUMENT_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 WSAENETDOWN:
+ PR_SetError(PR_NETWORK_DOWN_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAENOTCONN:
+ PR_SetError(PR_NOT_CONNECTED_ERROR, err);
+ break;
+ case WSAESHUTDOWN:
+ PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, err);
+ break;
+ case WSAEMSGSIZE:
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err);
+ break;
+ case WSAECONNRESET:
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ case WSAEINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case WSAECONNABORTED:
+ case WSAETIMEDOUT:
+ case WSAENETRESET:
+ PR_SetError(PR_CONNECT_ABORTED_ERROR, err);
+ break;
+ case WSAEOPNOTSUPP:
+ PR_SetError(PR_OPERATION_NOT_SUPPORTED_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 WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAECONNRESET:
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ case WSAEINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case WSAENETDOWN:
+ PR_SetError(PR_NETWORK_DOWN_ERROR, err);
+ break;
+ case WSAEISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+ case WSAENETRESET:
+ case WSAECONNABORTED:
+ case WSAETIMEDOUT:
+ PR_SetError(PR_CONNECT_ABORTED_ERROR, err);
+ break;
+ case WSAESHUTDOWN:
+ PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, err);
+ break;
+ case WSAEMSGSIZE:
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err);
+ break;
+ case WSAEOPNOTSUPP:
+ PR_SetError(PR_OPERATION_NOT_SUPPORTED_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 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 WSAENOTCONN:
+ PR_SetError(PR_NOT_CONNECTED_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAECONNRESET:
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ case WSAENETDOWN:
+ PR_SetError(PR_NETWORK_DOWN_ERROR, err);
+ break;
+ case WSAENETRESET:
+ case WSAECONNABORTED:
+ case WSAETIMEDOUT:
+ PR_SetError(PR_CONNECT_ABORTED_ERROR, err);
+ break;
+ case WSAESHUTDOWN:
+ PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, err);
+ break;
+ case WSAEHOSTUNREACH:
+ PR_SetError(PR_HOST_UNREACHABLE_ERROR, err);
+ break;
+ case WSAEOPNOTSUPP:
+ PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, err);
+ break;
+ case WSAEACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_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 WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEMSGSIZE:
+ case WSAEDESTADDRREQ:
+ case WSAEINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case WSAENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case WSAENOTCONN:
+ PR_SetError(PR_NOT_CONNECTED_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAECONNRESET:
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ case WSAENETDOWN:
+ PR_SetError(PR_NETWORK_DOWN_ERROR, err);
+ break;
+ case WSAENETRESET:
+ case WSAECONNABORTED:
+ case WSAETIMEDOUT:
+ PR_SetError(PR_CONNECT_ABORTED_ERROR, err);
+ break;
+ case WSAESHUTDOWN:
+ PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, err);
+ break;
+ case WSAEHOSTUNREACH:
+ PR_SetError(PR_HOST_UNREACHABLE_ERROR, err);
+ break;
+ case WSAENETUNREACH:
+ PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, err);
+ break;
+ case WSAEAFNOSUPPORT:
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err);
+ break;
+ case WSAEOPNOTSUPP:
+ PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, err);
+ break;
+ case WSAEACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case WSAEADDRNOTAVAIL:
+ PR_SetError(PR_ADDRESS_NOT_AVAILABLE_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 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_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case WSAENETDOWN:
+ PR_SetError(PR_NETWORK_DOWN_ERROR, err);
+ break;
+ case WSAEINVAL:
+ PR_SetError(PR_INVALID_STATE_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 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;
+ case WSAENETDOWN:
+ PR_SetError(PR_NETWORK_DOWN_ERROR, err);
+ break;
+ case WSAENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case WSAEACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_bind_error(PRInt32 err)
+{
+ switch (err) {
+ 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 WSAEINVAL:
+ PR_SetError(PR_SOCKET_ADDRESS_IS_BOUND_ERROR, err);
+ break;
+ case WSAENETDOWN:
+ PR_SetError(PR_NETWORK_DOWN_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_listen_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEOPNOTSUPP:
+ PR_SetError(PR_NOT_TCP_SOCKET_ERROR, err);
+ break;
+ case WSAENETDOWN:
+ PR_SetError(PR_NETWORK_DOWN_ERROR, err);
+ break;
+ case WSAEINVAL:
+ PR_SetError(PR_INVALID_STATE_ERROR, err);
+ break;
+ case WSAEISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+ case WSAENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case WSAEADDRINUSE:
+ PR_SetError(PR_ADDRESS_IN_USE_ERROR, err);
+ break;
+ case WSAEMFILE:
+ PR_SetError(PR_PROC_DESC_TABLE_FULL_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_shutdown_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAENOTCONN:
+ PR_SetError(PR_NOT_CONNECTED_ERROR, err);
+ break;
+ case WSAENETDOWN:
+ PR_SetError(PR_NETWORK_DOWN_ERROR, err);
+ break;
+ case WSAEINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_getsockname_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAENETDOWN:
+ PR_SetError(PR_NETWORK_DOWN_ERROR, err);
+ break;
+ case WSAEINVAL:
+ PR_SetError(PR_INVALID_STATE_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_getpeername_error(PRInt32 err)
+{
+
+ switch (err) {
+ 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 WSAENETDOWN:
+ PR_SetError(PR_NETWORK_DOWN_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_getsockopt_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEINVAL:
+ case WSAENOPROTOOPT:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAENETDOWN:
+ PR_SetError(PR_NETWORK_DOWN_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_setsockopt_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEINVAL:
+ case WSAENOPROTOOPT:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAENETDOWN:
+ PR_SetError(PR_NETWORK_DOWN_ERROR, err);
+ break;
+ case WSAENETRESET:
+ PR_SetError(PR_CONNECT_ABORTED_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_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;
+ case WSAENETDOWN:
+ prerror = PR_NETWORK_DOWN_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:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_BUSY_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..86399186
--- /dev/null
+++ b/pr/src/memory/prseg.c
@@ -0,0 +1,61 @@
+/* -*- 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.
+*/
+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.
+*/
+void _PR_DestroySegment(PRSegment *seg)
+{
+ _PR_MD_FREE_SEGMENT(seg);
+ PR_DELETE(seg);
+}
diff --git a/pr/src/misc/Makefile b/pr/src/misc/Makefile
new file mode 100644
index 00000000..313021c8
--- /dev/null
+++ b/pr/src/misc/Makefile
@@ -0,0 +1,67 @@
+#
+# 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 \
+ prcountr.c \
+ prdtoa.c \
+ prenv.c \
+ prerror.c \
+ prinit.c \
+ prinrval.c \
+ prlog2.c \
+ prlong.c \
+ prnetdb.c \
+ prolock.c \
+ prsystem.c \
+ prtime.c \
+ prthinfo.c \
+ prtrace.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..d69bb6b0
--- /dev/null
+++ b/pr/src/misc/pratom.c
@@ -0,0 +1,222 @@
+/* -*- 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();
+ }
+}
+
+PRInt32
+_PR_MD_ATOMIC_INCREMENT(PRInt32 *val)
+{
+ PRInt32 rv;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ PR_Lock(monitor);
+ rv = ++(*val);
+ PR_Unlock(monitor);
+ return rv;
+}
+
+PRInt32
+_PR_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val)
+{
+ PRInt32 rv;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ PR_Lock(monitor);
+ rv = ((*ptr) += val);
+ PR_Unlock(monitor);
+ return rv;
+}
+
+PRInt32
+_PR_MD_ATOMIC_DECREMENT(PRInt32 *val)
+{
+ PRInt32 rv;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ PR_Lock(monitor);
+ rv = --(*val);
+ PR_Unlock(monitor);
+ return rv;
+}
+
+PRInt32
+_PR_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
+{
+ PRInt32 rv;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ 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);
+}
+
+PR_IMPLEMENT(PRInt32)
+PR_AtomicAdd(PRInt32 *ptr, PRInt32 val)
+{
+ return _PR_MD_ATOMIC_ADD(ptr, val);
+}
+/*
+ * For platforms, which don't support the CAS (compare-and-swap) instruction
+ * (or an equivalent), the stack operations are implemented by use of PRLock
+ */
+
+PR_IMPLEMENT(PRStack *)
+PR_CreateStack(const char *stack_name)
+{
+PRStack *stack;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if ((stack = PR_NEW(PRStack)) == NULL) {
+ return NULL;
+ }
+ if (stack_name) {
+ stack->prstk_name = (char *) PR_Malloc(strlen(stack_name) + 1);
+ if (stack->prstk_name == NULL) {
+ PR_DELETE(stack);
+ return NULL;
+ }
+ strcpy(stack->prstk_name, stack_name);
+ } else
+ stack->prstk_name = NULL;
+
+#ifndef _PR_HAVE_ATOMIC_CAS
+ stack->prstk_lock = PR_NewLock();
+ if (stack->prstk_lock == NULL) {
+ PR_Free(stack->prstk_name);
+ PR_DELETE(stack);
+ return NULL;
+ }
+#endif /* !_PR_HAVE_ATOMIC_CAS */
+
+ stack->prstk_head.prstk_elem_next = NULL;
+
+ return stack;
+}
+
+PR_IMPLEMENT(PRStatus)
+PR_DestroyStack(PRStack *stack)
+{
+ if (stack->prstk_head.prstk_elem_next != NULL) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ if (stack->prstk_name)
+ PR_Free(stack->prstk_name);
+#ifndef _PR_HAVE_ATOMIC_CAS
+ PR_DestroyLock(stack->prstk_lock);
+#endif /* !_PR_HAVE_ATOMIC_CAS */
+ PR_DELETE(stack);
+
+ return PR_SUCCESS;
+}
+
+#ifndef _PR_HAVE_ATOMIC_CAS
+
+PR_IMPLEMENT(void)
+PR_StackPush(PRStack *stack, PRStackElem *stack_elem)
+{
+ PR_Lock(stack->prstk_lock);
+ stack_elem->prstk_elem_next = stack->prstk_head.prstk_elem_next;
+ stack->prstk_head.prstk_elem_next = stack_elem;
+ PR_Unlock(stack->prstk_lock);
+ return;
+}
+
+PR_IMPLEMENT(PRStackElem *)
+PR_StackPop(PRStack *stack)
+{
+PRStackElem *element;
+
+ PR_Lock(stack->prstk_lock);
+ element = stack->prstk_head.prstk_elem_next;
+ if (element != NULL) {
+ stack->prstk_head.prstk_elem_next = element->prstk_elem_next;
+ element->prstk_elem_next = NULL; /* debugging aid */
+ }
+ PR_Unlock(stack->prstk_lock);
+ return element;
+}
+#endif /* !_PR_HAVE_ATOMIC_CAS */
diff --git a/pr/src/misc/prcountr.c b/pr/src/misc/prcountr.c
new file mode 100644
index 00000000..33e85d82
--- /dev/null
+++ b/pr/src/misc/prcountr.c
@@ -0,0 +1,510 @@
+/* -*- 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.
+ */
+
+/*
+** prcountr.c -- NSPR Instrumentation Counters
+**
+** Implement the interface defined in prcountr.h
+**
+** Design Notes:
+**
+** The Counter Facility (CF) has a single anchor: qNameList.
+** The anchor is a PRCList. qNameList is a list of links in QName
+** structures. From qNameList any QName structure and its
+** associated RName structure can be located.
+**
+** For each QName, a list of RName structures is anchored at
+** rnLink in the QName structure.
+**
+** The counter itself is embedded in the RName structure.
+**
+** For manipulating the counter database, single lock is used to
+** protect the entire list: counterLock.
+**
+** A PRCounterHandle, defined in prcountr.h, is really a pointer
+** to a RName structure. References by PRCounterHandle are
+** dead-reconed to the RName structure. The PRCounterHandle is
+** "overloaded" for traversing the QName structures; only the
+** function PR_FindNextQnameHandle() uses this overloading.
+**
+**
+** ToDo (lth): decide on how to lock or atomically update
+** individual counters. Candidates are: the global lock; a lock
+** per RName structure; Atomic operations (Note that there are
+** not adaquate atomic operations (yet) to achieve this goal). At
+** this writing (6/19/98) , the update of the counter variable in
+** a QName structure is unprotected.
+**
+*/
+
+#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS)
+#include "prcountr.h"
+#include "prclist.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prmem.h"
+#include <string.h>
+
+/*
+**
+*/
+typedef struct QName
+{
+ PRCList link;
+ PRCList rNameList;
+ char name[PRCOUNTER_NAME_MAX+1];
+} QName;
+
+/*
+**
+*/
+typedef struct RName
+{
+ PRCList link;
+ QName *qName;
+ PRLock *lock;
+ volatile PRUint32 counter;
+ char name[PRCOUNTER_NAME_MAX+1];
+ char desc[PRCOUNTER_DESC_MAX+1];
+} RName;
+
+
+/*
+** Define the Counter Facility database
+*/
+static PRLock *counterLock;
+static PRCList qNameList;
+static PRLogModuleInfo *lm;
+
+/*
+** _PR_CounterInitialize() -- Initialize the Counter Facility
+**
+*/
+static void _PR_CounterInitialize( void )
+{
+ /*
+ ** This function should be called only once
+ */
+ PR_ASSERT( counterLock == NULL );
+
+ counterLock = PR_NewLock();
+ PR_INIT_CLIST( &qNameList );
+ lm = PR_NewLogModule("counters");
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Initialization complete"));
+
+ return;
+} /* end _PR_CounterInitialize() */
+
+/*
+** PR_CreateCounter() -- Create a counter
+**
+** ValidateArguments
+** Lock
+** if (qName not already in database)
+** NewQname
+** if (rName already in database )
+** Assert
+** else NewRname
+** NewCounter
+** link 'em up
+** Unlock
+**
+*/
+PR_IMPLEMENT(PRCounterHandle)
+ PR_CreateCounter(
+ const char *qName,
+ const char *rName,
+ const char *description
+)
+{
+ QName *qnp;
+ RName *rnp;
+ PRBool matchQname = PR_FALSE;
+
+ /* Self initialize, if necessary */
+ if ( counterLock == NULL )
+ _PR_CounterInitialize();
+
+ /* Validate input arguments */
+ PR_ASSERT( strlen(qName) <= PRCOUNTER_NAME_MAX );
+ PR_ASSERT( strlen(rName) <= PRCOUNTER_NAME_MAX );
+ PR_ASSERT( strlen(description) <= PRCOUNTER_DESC_MAX );
+
+ /* Lock the Facility */
+ PR_Lock( counterLock );
+
+ /* Do we already have a matching QName? */
+ if (!PR_CLIST_IS_EMPTY( &qNameList ))
+ {
+ qnp = (QName *) PR_LIST_HEAD( &qNameList );
+ do {
+ if ( strcmp(qnp->name, qName) == 0)
+ {
+ matchQname = PR_TRUE;
+ break;
+ }
+ qnp = (QName *)PR_NEXT_LINK( &qnp->link );
+ } while( qnp != (QName *)PR_LIST_HEAD( &qNameList ));
+ }
+ /*
+ ** If we did not find a matching QName,
+ ** allocate one and initialize it.
+ ** link it onto the qNameList.
+ **
+ */
+ if ( matchQname != PR_TRUE )
+ {
+ qnp = PR_NEWZAP( QName );
+ PR_ASSERT( qnp != NULL );
+ PR_INIT_CLIST( &qnp->link );
+ PR_INIT_CLIST( &qnp->rNameList );
+ strcpy( qnp->name, qName );
+ PR_APPEND_LINK( &qnp->link, &qNameList );
+ }
+
+ /* Do we already have a matching RName? */
+ if (!PR_CLIST_IS_EMPTY( &qnp->rNameList ))
+ {
+ rnp = (RName *) PR_LIST_HEAD( &qnp->rNameList );
+ do {
+ /*
+ ** No duplicate RNames are allowed within a QName
+ **
+ */
+ PR_ASSERT( strcmp(rnp->name, rName));
+ rnp = (RName *)PR_NEXT_LINK( &rnp->link );
+ } while( rnp != (RName *)PR_LIST_HEAD( &qnp->rNameList ));
+ }
+
+ /* Get a new RName structure; initialize its members */
+ rnp = PR_NEWZAP( RName );
+ PR_ASSERT( rnp != NULL );
+ PR_INIT_CLIST( &rnp->link );
+ strcpy( rnp->name, rName );
+ strcpy( rnp->desc, description );
+ rnp->lock = PR_NewLock();
+ if ( rnp->lock == NULL )
+ {
+ PR_ASSERT(0);
+ }
+
+ PR_APPEND_LINK( &rnp->link, &qnp->rNameList ); /* add RName to QName's rnList */
+ rnp->qName = qnp; /* point the RName to the QName */
+
+ /* Unlock the Facility */
+ PR_Unlock( counterLock );
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Create: QName: %s %p, RName: %s %p\n\t",
+ qName, qnp, rName, rnp ));
+
+ return((PRCounterHandle)rnp);
+} /* end PR_CreateCounter() */
+
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+ PR_DestroyCounter(
+ PRCounterHandle handle
+)
+{
+ RName *rnp = (RName *)handle;
+ QName *qnp = rnp->qName;
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Deleting: QName: %s, RName: %s",
+ qnp->name, rnp->name));
+
+ /* Lock the Facility */
+ PR_Lock( counterLock );
+
+ /*
+ ** Remove RName from the list of RNames in QName
+ ** and free RName
+ */
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Deleting RName: %s, %p",
+ rnp->name, rnp));
+ PR_REMOVE_LINK( &rnp->link );
+ PR_Free( rnp->lock );
+ PR_DELETE( rnp );
+
+ /*
+ ** If this is the last RName within QName
+ ** remove QName from the qNameList and free it
+ */
+ if ( PR_CLIST_IS_EMPTY( &qnp->rNameList ) )
+ {
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Deleting unused QName: %s, %p",
+ qnp->name, qnp));
+ PR_REMOVE_LINK( &qnp->link );
+ PR_DELETE( qnp );
+ }
+
+ /* Unlock the Facility */
+ PR_Unlock( counterLock );
+ return;
+} /* end PR_DestroyCounter() */
+
+/*
+**
+*/
+PR_IMPLEMENT(PRCounterHandle)
+ PR_GetCounterHandleFromName(
+ const char *qName,
+ const char *rName
+)
+{
+ const char *qn, *rn, *desc;
+ PRCounterHandle qh, rh;
+ RName *rnp = NULL;
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: GetCounterHandleFromName:\n\t"
+ "QName: %s, RName: %s", qName, rName ));
+
+ qh = PR_FindNextCounterQname( NULL );
+ while (qh != NULL)
+ {
+ rh = PR_FindNextCounterRname( NULL, qh );
+ while ( rh != NULL )
+ {
+ PR_GetCounterNameFromHandle( rh, &qn, &rn, &desc );
+ if ( (strcmp( qName, qn ) == 0)
+ && (strcmp( rName, rn ) == 0 ))
+ {
+ rnp = (RName *)rh;
+ goto foundIt;
+ }
+ rh = PR_FindNextCounterRname( rh, qh );
+ }
+ qh = PR_FindNextCounterQname( NULL );
+ }
+
+foundIt:
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: GetConterHandleFromName: %p", rnp ));
+ return(rh);
+} /* end PR_GetCounterHandleFromName() */
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+ PR_GetCounterNameFromHandle(
+ PRCounterHandle handle,
+ const char **qName,
+ const char **rName,
+ const char **description
+)
+{
+ RName *rnp = (RName *)handle;
+ QName *qnp = rnp->qName;
+
+ *qName = qnp->name;
+ *rName = rnp->name;
+ *description = rnp->desc;
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: GetConterNameFromHandle: "
+ "QNp: %p, RNp: %p,\n\tQName: %s, RName: %s, Desc: %s",
+ qnp, rnp, qnp->name, rnp->name, rnp->desc ));
+
+ return;
+} /* end PR_GetCounterNameFromHandle() */
+
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+ PR_IncrementCounter(
+ PRCounterHandle handle
+)
+{
+ PR_Lock(((RName *)handle)->lock);
+ ((RName *)handle)->counter++;
+ PR_Unlock(((RName *)handle)->lock);
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Increment: %p, %ld",
+ handle, ((RName *)handle)->counter ));
+
+ return;
+} /* end PR_IncrementCounter() */
+
+
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+ PR_DecrementCounter(
+ PRCounterHandle handle
+)
+{
+ PR_Lock(((RName *)handle)->lock);
+ ((RName *)handle)->counter--;
+ PR_Unlock(((RName *)handle)->lock);
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Decrement: %p, %ld",
+ handle, ((RName *)handle)->counter ));
+
+ return;
+} /* end PR_DecrementCounter() */
+
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+ PR_AddToCounter(
+ PRCounterHandle handle,
+ PRUint32 value
+)
+{
+ PR_Lock(((RName *)handle)->lock);
+ ((RName *)handle)->counter += value;
+ PR_Unlock(((RName *)handle)->lock);
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: AddToCounter: %p, %ld",
+ handle, ((RName *)handle)->counter ));
+
+ return;
+} /* end PR_AddToCounter() */
+
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+ PR_SubtractFromCounter(
+ PRCounterHandle handle,
+ PRUint32 value
+)
+{
+ PR_Lock(((RName *)handle)->lock);
+ ((RName *)handle)->counter -= value;
+ PR_Unlock(((RName *)handle)->lock);
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: SubtractFromCounter: %p, %ld",
+ handle, ((RName *)handle)->counter ));
+
+ return;
+} /* end PR_SubtractFromCounter() */
+
+/*
+**
+*/
+PR_IMPLEMENT(PRUint32)
+ PR_GetCounter(
+ PRCounterHandle handle
+)
+{
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: GetCounter: %p, %ld",
+ handle, ((RName *)handle)->counter ));
+
+ return(((RName *)handle)->counter);
+} /* end PR_GetCounter() */
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+ PR_SetCounter(
+ PRCounterHandle handle,
+ PRUint32 value
+)
+{
+ ((RName *)handle)->counter = value;
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: SetCounter: %p, %ld",
+ handle, ((RName *)handle)->counter ));
+
+ return;
+} /* end PR_SetCounter() */
+
+/*
+**
+*/
+PR_IMPLEMENT(PRCounterHandle)
+ PR_FindNextCounterQname(
+ PRCounterHandle handle
+)
+{
+ QName *qnp = (QName *)handle;
+
+ if ( PR_CLIST_IS_EMPTY( &qNameList ))
+ qnp = NULL;
+ else if ( qnp == NULL )
+ qnp = (QName *)PR_LIST_HEAD( &qNameList );
+ else if ( PR_NEXT_LINK( &qnp->link ) == &qNameList )
+ qnp = NULL;
+ else
+ qnp = (QName *)PR_NEXT_LINK( &qnp->link );
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: FindNextQname: Handle: %p, Returns: %p",
+ handle, qnp ));
+
+ return((PRCounterHandle)qnp);
+} /* end PR_FindNextCounterQname() */
+
+
+/*
+**
+*/
+PR_IMPLEMENT(PRCounterHandle)
+ PR_FindNextCounterRname(
+ PRCounterHandle rhandle,
+ PRCounterHandle qhandle
+)
+{
+ RName *rnp = (RName *)rhandle;
+ QName *qnp = (QName *)qhandle;
+
+
+ if ( PR_CLIST_IS_EMPTY( &qnp->rNameList ))
+ rnp = NULL;
+ else if ( rnp == NULL )
+ rnp = (RName *)PR_LIST_HEAD( &qnp->rNameList );
+ else if ( PR_NEXT_LINK( &rnp->link ) == &qnp->rNameList )
+ rnp = NULL;
+ else
+ rnp = (RName *)PR_NEXT_LINK( &rnp->link );
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: FindNextRname: Rhandle: %p, QHandle: %p, Returns: %p",
+ rhandle, qhandle, rnp ));
+
+ return((PRCounterHandle)rnp);
+} /* end PR_FindNextCounterRname() */
+
+
+#else /* !(defined(DEBUG) || defined(FORCE_NSPR_COUNTERS)) */
+/*
+** NSPR Counters are not defined in this case
+**
+**
+*/
+#endif /* defined(DEBUG) || defined(FORCE_NSPR_COUNTERS) */
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pr/src/misc/prdtoa.c b/pr/src/misc/prdtoa.c
new file mode 100644
index 00000000..3fadb2b1
--- /dev/null
+++ b/pr/src/misc/prdtoa.c
@@ -0,0 +1,2569 @@
+/* -*- 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"
+
+/****************************************************************
+ *
+ * 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
+ */
+
+/* PR_strtod for IEEE-, VAX-, and IBM-arithmetic machines.
+ *
+ * This PR_strtod returns a nearest machine number to the input decimal
+ * string (or sets the error code to PR_RANGE_ERROR). 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
+
+#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:
+ PR_SetError(PR_RANGE_ERROR, 0);
+#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.;
+ PR_SetError(PR_RANGE_ERROR, 0);
+ 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;
+}
+
+/* PR_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(PRFloat64 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(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){
+ if(decpt == 0){
+ *bufp++ = '0';
+ }
+ else {
+ 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..716e1d5f
--- /dev/null
+++ b/pr/src/misc/prenv.c
@@ -0,0 +1,57 @@
+/* -*- 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)
+extern _PRCPU * _pr_primordialCPU;
+static PRIntn _is;
+#define _PR_NEW_LOCK_ENV()
+#define _PR_LOCK_ENV() if (_pr_primordialCPU) _PR_INTSOFF(_is);
+#define _PR_UNLOCK_ENV() if (_pr_primordialCPU) _PR_INTSON(_is);
+#else
+static PRLock *_pr_envLock = NULL;
+#define _PR_NEW_LOCK_ENV() {_pr_envLock = PR_NewLock();}
+#define _PR_LOCK_ENV() { if (_pr_envLock) PR_Lock(_pr_envLock); }
+#define _PR_UNLOCK_ENV() { if (_pr_envLock) 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..6960b153
--- /dev/null
+++ b/pr/src/misc/prinit.c
@@ -0,0 +1,577 @@
+/* -*- 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;
+
+PRInt32 _pr_userActive;
+PRInt32 _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) */
+
+PRLock *_pr_sleeplock; /* used in PR_Sleep(), classic and pthreads */
+
+static void _PR_InitCallOnce(void);
+
+PRBool _pr_initialized = PR_FALSE;
+
+
+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 clever.
+ */
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+ return ((0 == strcmp(importedVersion, PR_VERSION)) ? PR_TRUE : PR_FALSE);
+} /* PR_VersionCheck */
+
+
+PR_IMPLEMENT(PRBool) PR_Initialized(void)
+{
+ return _pr_initialized;
+}
+
+static void _PR_InitStuff(void)
+{
+ 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_InitLocks();
+ _PR_InitAtomic();
+ _PR_InitSegs();
+ _PR_InitStacks();
+ _PR_InitTPD();
+ _PR_InitEnv();
+ _PR_InitLayerCache();
+ _PR_InitClock();
+
+ _pr_sleeplock = PR_NewLock();
+ PR_ASSERT(NULL != _pr_sleeplock);
+
+ _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();
+#ifdef PR_LOGGING
+ _PR_InitLog();
+#endif
+ _PR_InitLinker();
+ _PR_InitCallOnce();
+ _PR_InitDtoa();
+ _PR_InitMW();
+
+ _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_EnableClockInterrupts(void)
+{
+#if !defined(_PR_PTHREADS)
+ if (!_pr_initialized) {
+ _PR_InitStuff();
+ }
+ _PR_MD_ENABLE_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);
+
+#ifdef IRIX
+ _PR_MD_PRE_CLEANUP(me);
+ /*
+ * The primordial thread must now be running on the primordial cpu
+ */
+ PR_ASSERT((_PR_IS_NATIVE_THREAD(me)) || (me->cpu->id == 0));
+#endif
+
+#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
+ _PR_CleanupFdCache();
+
+ /*
+ * 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();
+ _pr_initialized = PR_FALSE;
+ 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)
+{
+ PR_FREEIF(attr->currentDirectory);
+ memset(attr, 0, sizeof(*attr));
+}
+
+PR_IMPLEMENT(void)
+PR_DestroyProcessAttr(PRProcessAttr *attr)
+{
+ PR_FREEIF(attr->currentDirectory);
+ PR_DELETE(attr);
+}
+
+PR_IMPLEMENT(void)
+PR_ProcessAttrSetStdioRedirect(
+ 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);
+ }
+}
+
+/*
+ * OBSOLETE
+ */
+PR_IMPLEMENT(void)
+PR_SetStdioRedirect(
+ PRProcessAttr *attr,
+ PRSpecialFD stdioFd,
+ PRFileDesc *redirectFd)
+{
+#if defined(DEBUG)
+ static PRBool warn = PR_TRUE;
+ if (warn) {
+ warn = _PR_Obsolete("PR_SetStdioRedirect()",
+ "PR_ProcessAttrSetStdioRedirect()");
+ }
+#endif
+ PR_ProcessAttrSetStdioRedirect(attr, stdioFd, redirectFd);
+}
+
+PR_IMPLEMENT(PRStatus)
+PR_ProcessAttrSetCurrentDirectory(
+ PRProcessAttr *attr,
+ const char *dir)
+{
+ PR_FREEIF(attr->currentDirectory);
+ attr->currentDirectory = PR_MALLOC(strlen(dir) + 1);
+ if (!attr->currentDirectory) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return PR_FAILURE;
+ }
+ strcpy(attr->currentDirectory, dir);
+ return PR_SUCCESS;
+}
+
+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..1c64ca6f
--- /dev/null
+++ b/pr/src/misc/prnetdb.c
@@ -0,0 +1,827 @@
+/* -*- 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(LINUX) && defined(_REENTRANT) \
+ && !(defined(__GLIBC__) && __GLIBC__ >= 2))
+#define _PR_HAVE_GETPROTO_R
+#define _PR_HAVE_GETPROTO_R_POINTER
+#endif
+
+#if defined(OSF1) \
+ || defined(AIX4_3) || (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(LINUX) && 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;
+#if defined(_PR_HAVE_GETPROTO_R)
+ struct protoent* res = (struct protoent*)result;
+#endif
+
+ 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;
+#if defined(_PR_HAVE_GETPROTO_R)
+ struct protoent* res = (struct protoent*)result;
+#endif
+
+ 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 (AF_INET == addr->raw.family)
+ addrsize = sizeof(addr->inet);
+#if defined(_PR_INET6)
+ else if (AF_INET6 == addr->raw.family)
+ addrsize = sizeof(addr->ipv6);
+#endif
+#if defined(XP_UNIX)
+ else if (AF_UNIX == addr->raw.family)
+ addrsize = sizeof(addr->local);
+#endif
+ else addrsize = 0;
+
+ 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/prolock.c b/pr/src/misc/prolock.c
new file mode 100644
index 00000000..08123640
--- /dev/null
+++ b/pr/src/misc/prolock.c
@@ -0,0 +1,91 @@
+/* -*- 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.
+ */
+
+/*
+** prolock.c -- NSPR Ordered Lock
+**
+** Implement the API defined in prolock.h
+**
+*/
+#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS)
+#include "prolock.h"
+#include "prlog.h"
+#include "prerror.h"
+
+PR_IMPLEMENT(PROrderedLock *)
+ PR_CreateOrderedLock(
+ PRInt32 order,
+ const char *name
+)
+{
+ PR_ASSERT(!"Not implemented"); /* Not implemented yet */
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+} /* end PR_CreateOrderedLock() */
+
+
+PR_IMPLEMENT(void)
+ PR_DestroyOrderedLock(
+ PROrderedLock *lock
+)
+{
+ PR_ASSERT(!"Not implemented"); /* Not implemented yet */
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+} /* end PR_DestroyOrderedLock() */
+
+
+PR_IMPLEMENT(void)
+ PR_LockOrderedLock(
+ PROrderedLock *lock
+)
+{
+ PR_ASSERT(!"Not implemented"); /* Not implemented yet */
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+} /* end PR_LockOrderedLock() */
+
+
+PR_IMPLEMENT(PRStatus)
+ PR_UnlockOrderedLock(
+ PROrderedLock *lock
+)
+{
+ PR_ASSERT(!"Not implemented"); /* Not implemented yet */
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+} /* end PR_UnlockOrderedLock() */
+
+#else /* ! defined(FORCE_NSPR_ORDERED_LOCK) */
+/*
+** NSPR Ordered Lock is not defined when !DEBUG and !FORCE_NSPR_ORDERED_LOCK
+**
+*/
+
+#endif /* defined(FORCE_NSPR_ORDERED_LOCK */
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pr/src/misc/prsystem.c b/pr/src/misc/prsystem.c
new file mode 100644
index 00000000..de907d5a
--- /dev/null
+++ b/pr/src/misc/prsystem.c
@@ -0,0 +1,91 @@
+/* -*- 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_GetDirectorySeparator()
+{
+ return PR_DIRECTORY_SEPARATOR;
+} /* PR_GetDirectorySeparator */
+
+/*
+** OBSOLETE -- the function name is misspelled.
+*/
+PR_IMPLEMENT(char) PR_GetDirectorySepartor()
+{
+#if defined(DEBUG)
+ static PRBool warn = PR_TRUE;
+ if (warn) {
+ warn = _PR_Obsolete("PR_GetDirectorySepartor()",
+ "PR_GetDirectorySeparator()");
+ }
+#endif
+ return PR_GetDirectorySeparator();
+} /* 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..e53fc3f2
--- /dev/null
+++ b/pr/src/misc/prthinfo.c
@@ -0,0 +1,225 @@
+/* -*- 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
+ **
+ ** 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;
+ }
+
+ 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_IMPLEMENT(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..25bccf79
--- /dev/null
+++ b/pr/src/misc/prtime.c
@@ -0,0 +1,1938 @@
+/* -*- 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(HAVE_INT_LOCALTIME_R)
+
+/*
+ * In this case we could define the macro as
+ * #define MT_safe_localtime(timer, result) \
+ * (localtime_r(timer, result) == 0 ? result : NULL)
+ * I chose to compare the return value of localtime_r with -1 so
+ * that I can catch the cases where localtime_r returns a pointer
+ * to struct tm. The macro definition above would not be able to
+ * detect such mistakes because it is legal to compare a pointer
+ * with 0.
+ */
+
+#define MT_safe_localtime(timer, result) \
+ (localtime_r(timer, result) == -1 ? NULL: result)
+
+#elif defined(HAVE_POINTER_LOCALTIME_R)
+
+#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 != -1)
+ {
+ /* 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 ((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 > 31 || n1 == 0) /* must be YY/MM/DD */
+ {
+ if (n2 > 12) break;
+ if (n3 > 31) break;
+ year = n1;
+ if (year < 70)
+ year += 2000;
+ else if (year < 100)
+ year += 1900;
+ month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1);
+ date = n3;
+ rest = s;
+ break;
+ }
+
+ if (n1 > 12 && n2 > 12) /* illegal */
+ {
+ rest = s;
+ break;
+ }
+
+ if (n3 < 70)
+ n3 += 2000;
+ else if (n3 < 100)
+ 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, const 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 some platforms, for example 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) || (__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/misc/prtrace.c b/pr/src/misc/prtrace.c
new file mode 100644
index 00000000..c2a64b6c
--- /dev/null
+++ b/pr/src/misc/prtrace.c
@@ -0,0 +1,912 @@
+/* -*- 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.
+ */
+
+/*
+** prtrace.c -- NSPR Trace Instrumentation
+**
+** Implement the API defined in prtrace.h
+**
+**
+**
+*/
+
+#if defined (DEBUG) || defined (FORCE_NSPR_TRACE)
+#include <string.h>
+#include "prtrace.h"
+#include "prclist.h"
+#include "prlock.h"
+#include "prcvar.h"
+#include "prio.h"
+#include "prlog.h"
+#include "prenv.h"
+#include "prmem.h"
+#include "prerror.h"
+
+
+#define DEFAULT_TRACE_BUFSIZE ( 1024 * 1024 )
+#define DEFAULT_BUFFER_SEGMENTS 2
+
+/*
+** Enumerate states in a RName structure
+*/
+typedef enum TraceState
+{
+ Running = 1,
+ Suspended = 2
+} TraceState;
+
+/*
+** Define QName structure
+*/
+typedef struct QName
+{
+ PRCList link;
+ PRCList rNameList;
+ char name[PRTRACE_NAME_MAX+1];
+} QName;
+
+/*
+** Define RName structure
+*/
+typedef struct RName
+{
+ PRCList link;
+ PRLock *lock;
+ QName *qName;
+ TraceState state;
+ char name[PRTRACE_NAME_MAX+1];
+ char desc[PRTRACE_DESC_MAX+1];
+} RName;
+
+
+/*
+** The Trace Facility database
+**
+*/
+static PRLogModuleInfo *lm;
+
+static PRLock *traceLock; /* Facility Lock */
+static PRCList qNameList; /* anchor to all QName structures */
+static TraceState traceState = Running;
+
+/*
+** in-memory trace buffer controls
+*/
+static PRTraceEntry *tBuf; /* pointer to buffer */
+static PRInt32 bufSize; /* size of buffer, in bytes, rounded up to sizeof(PRTraceEntry) */
+static volatile PRInt32 next; /* index to next PRTraceEntry */
+static PRInt32 last; /* index of highest numbered trace entry */
+
+/*
+** Real-time buffer capture controls
+*/
+static PRInt32 fetchLastSeen = 0;
+static PRBool fetchLostData = PR_FALSE;
+
+/*
+** Buffer write-to-file controls
+*/
+static PRLock *logLock; /* Sync lock */
+static PRCondVar *logCVar; /* Sync Condidtion Variable */
+/*
+** Inter-thread state communication.
+** Controling thread writes to logOrder under protection of logCVar
+** the logging thread reads logOrder and sets logState on Notify.
+**
+** logSegments, logCount, logLostData must be read and written under
+** protection of logLock, logCVar.
+**
+*/
+static enum LogState
+{
+ LogNotRunning, /* Initial state */
+ LogReset, /* Causes logger to re-calc controls */
+ LogActive, /* Logging in progress, set only by log thread */
+ LogSuspend, /* Suspend Logging */
+ LogResume, /* Resume Logging => LogActive */
+ LogStop /* Stop the log thread */
+} logOrder, logState, localState; /* controlling state variables */
+static PRInt32 logSegments; /* Number of buffer segments */
+static PRInt32 logEntries; /* number of Trace Entries in the buffer */
+static PRInt32 logEntriesPerSegment; /* number of PRTraceEntries per buffer segment */
+static PRInt32 logSegSize; /* size of buffer segment */
+static PRInt32 logCount; /* number of segments pending output */
+static PRInt32 logLostData; /* number of lost log buffer segments */
+
+/*
+** end Trace Database
+**
+*/
+
+/*
+** _PR_InitializeTrace() -- Initialize the trace facility
+*/
+static void NewTraceBuffer( PRInt32 size )
+{
+ /*
+ ** calculate the size of the buffer
+ ** round down so that each segment has the same number of
+ ** trace entries
+ */
+ logSegments = DEFAULT_BUFFER_SEGMENTS;
+ logEntries = size / sizeof(PRTraceEntry);
+ logEntriesPerSegment = logEntries / logSegments;
+ logEntries = logSegments * logEntriesPerSegment;
+ bufSize = logEntries * sizeof(PRTraceEntry);
+ logSegSize = logEntriesPerSegment * sizeof(PRTraceEntry);
+ PR_ASSERT( bufSize != 0);
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("NewTraceBuffer: logSegments: %ld, logEntries: %ld, logEntriesPerSegment: %ld, logSegSize: %ld",
+ logSegments, logEntries, logEntriesPerSegment, logSegSize ));
+
+
+ tBuf = PR_Malloc( bufSize );
+ if ( tBuf == NULL )
+ {
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("PRTrace: Failed to get trace buffer"));
+ PR_ASSERT( 0 );
+ }
+ else
+ {
+ PR_LOG( lm, PR_LOG_NOTICE,
+ ("PRTrace: Got trace buffer of size: %ld, at %p", bufSize, tBuf));
+ }
+
+ next = 0;
+ last = logEntries -1;
+ logCount = 0;
+ logLostData = PR_TRUE; /* not really on first call */
+ logOrder = LogReset;
+
+} /* end NewTraceBuffer() */
+
+/*
+** _PR_InitializeTrace() -- Initialize the trace facility
+*/
+static void _PR_InitializeTrace( void )
+{
+ /* The lock pointer better be null on this call */
+ PR_ASSERT( traceLock == NULL );
+
+ traceLock = PR_NewLock();
+ PR_ASSERT( traceLock != NULL );
+
+ PR_Lock( traceLock );
+
+ PR_INIT_CLIST( &qNameList );
+
+ lm = PR_NewLogModule("trace");
+
+ bufSize = DEFAULT_TRACE_BUFSIZE;
+ NewTraceBuffer( bufSize );
+
+ /* Initialize logging controls */
+ logLock = PR_NewLock();
+ logCVar = PR_NewCondVar( logLock );
+
+ PR_Unlock( traceLock );
+ return;
+} /* end _PR_InitializeTrace() */
+
+/*
+** Create a Trace Handle
+*/
+PR_IMPLEMENT(PRTraceHandle)
+ PR_CreateTrace(
+ const char *qName, /* QName for this trace handle */
+ const char *rName, /* RName for this trace handle */
+ const char *description /* description for this trace handle */
+)
+{
+ QName *qnp;
+ RName *rnp;
+ PRBool matchQname = PR_FALSE;
+
+ /* Self initialize, if necessary */
+ if ( traceLock == NULL )
+ _PR_InitializeTrace();
+
+ /* Validate input arguments */
+ PR_ASSERT( strlen(qName) <= PRTRACE_NAME_MAX );
+ PR_ASSERT( strlen(rName) <= PRTRACE_NAME_MAX );
+ PR_ASSERT( strlen(description) <= PRTRACE_DESC_MAX );
+
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRTRACE: CreateTrace: Qname: %s, RName: %s", qName, rName));
+
+ /* Lock the Facility */
+ PR_Lock( traceLock );
+
+ /* Do we already have a matching QName? */
+ if (!PR_CLIST_IS_EMPTY( &qNameList ))
+ {
+ qnp = (QName *) PR_LIST_HEAD( &qNameList );
+ do {
+ if ( strcmp(qnp->name, qName) == 0)
+ {
+ matchQname = PR_TRUE;
+ break;
+ }
+ qnp = (QName *)PR_NEXT_LINK( &qnp->link );
+ } while( qnp != (QName *)PR_LIST_HEAD( &qNameList ));
+ }
+ /*
+ ** If we did not find a matching QName,
+ ** allocate one and initialize it.
+ ** link it onto the qNameList.
+ **
+ */
+ if ( matchQname != PR_TRUE )
+ {
+ qnp = PR_NEWZAP( QName );
+ PR_ASSERT( qnp != NULL );
+ PR_INIT_CLIST( &qnp->link );
+ PR_INIT_CLIST( &qnp->rNameList );
+ strcpy( qnp->name, qName );
+ PR_APPEND_LINK( &qnp->link, &qNameList );
+ }
+
+ /* Do we already have a matching RName? */
+ if (!PR_CLIST_IS_EMPTY( &qnp->rNameList ))
+ {
+ rnp = (RName *) PR_LIST_HEAD( &qnp->rNameList );
+ do {
+ /*
+ ** No duplicate RNames are allowed within a QName
+ **
+ */
+ PR_ASSERT( strcmp(rnp->name, rName));
+ rnp = (RName *)PR_NEXT_LINK( &rnp->link );
+ } while( rnp != (RName *)PR_LIST_HEAD( &qnp->rNameList ));
+ }
+
+ /* Get a new RName structure; initialize its members */
+ rnp = PR_NEWZAP( RName );
+ PR_ASSERT( rnp != NULL );
+ PR_INIT_CLIST( &rnp->link );
+ strcpy( rnp->name, rName );
+ strcpy( rnp->desc, description );
+ rnp->lock = PR_NewLock();
+ rnp->state = Running;
+ if ( rnp->lock == NULL )
+ {
+ PR_ASSERT(0);
+ }
+
+ PR_APPEND_LINK( &rnp->link, &qnp->rNameList ); /* add RName to QName's rnList */
+ rnp->qName = qnp; /* point the RName to the QName */
+
+ /* Unlock the Facility */
+ PR_Unlock( traceLock );
+ PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: Create: QName: %s %p, RName: %s %p\n\t",
+ qName, qnp, rName, rnp ));
+
+ return((PRTraceHandle)rnp);
+} /* end PR_CreateTrace() */
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+ PR_DestroyTrace(
+ PRTraceHandle handle /* Handle to be destroyed */
+)
+{
+ RName *rnp = (RName *)handle;
+ QName *qnp = rnp->qName;
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: Deleting: QName: %s, RName: %s",
+ qnp->name, rnp->name));
+
+ /* Lock the Facility */
+ PR_Lock( traceLock );
+
+ /*
+ ** Remove RName from the list of RNames in QName
+ ** and free RName
+ */
+ PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: Deleting RName: %s, %p",
+ rnp->name, rnp));
+ PR_REMOVE_LINK( &rnp->link );
+ PR_Free( rnp->lock );
+ PR_DELETE( rnp );
+
+ /*
+ ** If this is the last RName within QName
+ ** remove QName from the qNameList and free it
+ */
+ if ( PR_CLIST_IS_EMPTY( &qnp->rNameList ) )
+ {
+ PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: Deleting unused QName: %s, %p",
+ qnp->name, qnp));
+ PR_REMOVE_LINK( &qnp->link );
+ PR_DELETE( qnp );
+ }
+
+ /* Unlock the Facility */
+ PR_Unlock( traceLock );
+ return;
+} /* end PR_DestroyTrace() */
+
+/*
+** Create a TraceEntry in the trace buffer
+*/
+PR_IMPLEMENT(void)
+ PR_Trace(
+ PRTraceHandle handle, /* use this trace handle */
+ PRUint32 userData0, /* User supplied data word 0 */
+ PRUint32 userData1, /* User supplied data word 1 */
+ PRUint32 userData2, /* User supplied data word 2 */
+ PRUint32 userData3, /* User supplied data word 3 */
+ PRUint32 userData4, /* User supplied data word 4 */
+ PRUint32 userData5, /* User supplied data word 5 */
+ PRUint32 userData6, /* User supplied data word 6 */
+ PRUint32 userData7 /* User supplied data word 7 */
+)
+{
+ PRTraceEntry *tep;
+ PRInt32 mark;
+
+ if ( (traceState == Suspended )
+ || ( ((RName *)handle)->state == Suspended ))
+ return;
+
+ /*
+ ** Get the next trace entry slot w/ minimum delay
+ */
+ PR_Lock( traceLock );
+
+ tep = &tBuf[next++];
+ if ( next > last )
+ next = 0;
+ if ( fetchLostData == PR_FALSE && next == fetchLastSeen )
+ fetchLostData = PR_TRUE;
+
+ mark = next;
+
+ PR_Unlock( traceLock );
+
+ /*
+ ** We have a trace entry. Fill it in.
+ */
+ tep->thread = PR_GetCurrentThread();
+ tep->handle = handle;
+ tep->time = PR_Now();
+ tep->userData[0] = userData0;
+ tep->userData[1] = userData1;
+ tep->userData[2] = userData2;
+ tep->userData[3] = userData3;
+ tep->userData[4] = userData4;
+ tep->userData[5] = userData5;
+ tep->userData[6] = userData6;
+ tep->userData[7] = userData7;
+
+ /* When buffer segment is full, signal trace log thread to run */
+ if (( mark % logEntriesPerSegment) == 0 )
+ {
+ PR_Lock( logLock );
+ logCount++;
+ PR_NotifyCondVar( logCVar );
+ PR_Unlock( logLock );
+ /*
+ ** Gh0D! This is awful!
+ ** Anyway, to minimize lost trace data segments,
+ ** I inserted the PR_Sleep(0) to cause a context switch
+ ** so that the log thread could run.
+ ** I know, it perturbs the universe and may cause
+ ** funny things to happen in the optimized builds.
+ ** Take it out, loose data; leave it in risk Heisenberg.
+ */
+ /* PR_Sleep(0); */
+ }
+
+ return;
+} /* end PR_Trace() */
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+ PR_SetTraceOption(
+ PRTraceOption command, /* One of the enumerated values */
+ void *value /* command value or NULL */
+)
+{
+ RName * rnp;
+
+ switch ( command )
+ {
+ case PRTraceBufSize :
+ PR_Lock( traceLock );
+ PR_Free( tBuf );
+ bufSize = *(PRInt32 *)value;
+ NewTraceBuffer( bufSize );
+ PR_Unlock( traceLock );
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRSetTraceOption: PRTraceBufSize: %ld", bufSize));
+ break;
+
+ case PRTraceEnable :
+ rnp = *(RName **)value;
+ rnp->state = Running;
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRSetTraceOption: PRTraceEnable: %p", rnp));
+ break;
+
+ case PRTraceDisable :
+ rnp = *(RName **)value;
+ rnp->state = Suspended;
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRSetTraceOption: PRTraceDisable: %p", rnp));
+ break;
+
+ case PRTraceSuspend :
+ traceState = Suspended;
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRSetTraceOption: PRTraceSuspend"));
+ break;
+
+ case PRTraceResume :
+ traceState = Running;
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRSetTraceOption: PRTraceResume"));
+ break;
+
+ case PRTraceSuspendRecording :
+ PR_Lock( logLock );
+ logOrder = LogSuspend;
+ PR_NotifyCondVar( logCVar );
+ PR_Unlock( logLock );
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRSetTraceOption: PRTraceSuspendRecording"));
+ break;
+
+ case PRTraceResumeRecording :
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRSetTraceOption: PRTraceResumeRecording"));
+ if ( logState != LogSuspend )
+ break;
+ PR_Lock( logLock );
+ logOrder = LogResume;
+ PR_NotifyCondVar( logCVar );
+ PR_Unlock( logLock );
+ break;
+
+ case PRTraceStopRecording :
+ PR_Lock( logLock );
+ logOrder = LogStop;
+ PR_NotifyCondVar( logCVar );
+ PR_Unlock( logLock );
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRSetTraceOption: PRTraceStopRecording"));
+ break;
+
+ case PRTraceLockHandles :
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRSetTraceOption: PRTraceLockTraceHandles"));
+ PR_Lock( traceLock );
+ break;
+
+ case PRTraceUnLockHandles :
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRSetTraceOption: PRTraceUnLockHandles"));
+ PR_Lock( traceLock );
+ break;
+
+ default:
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("PRSetTraceOption: Invalid command %ld", command ));
+ PR_ASSERT( 0 );
+ break;
+ } /* end switch() */
+ return;
+} /* end PR_SetTraceOption() */
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+ PR_GetTraceOption(
+ PRTraceOption command, /* One of the enumerated values */
+ void *value /* command value or NULL */
+)
+{
+ switch ( command )
+ {
+ case PRTraceBufSize :
+ *((PRInt32 *)value) = bufSize;
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PRGetTraceOption: PRTraceBufSize: %ld", bufSize ));
+ break;
+
+ default:
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("PRGetTraceOption: Invalid command %ld", command ));
+ PR_ASSERT( 0 );
+ break;
+ } /* end switch() */
+ return;
+} /* end PR_GetTraceOption() */
+
+/*
+**
+*/
+PR_IMPLEMENT(PRTraceHandle)
+ PR_GetTraceHandleFromName(
+ const char *qName, /* QName search argument */
+ const char *rName /* RName search argument */
+)
+{
+ const char *qn, *rn, *desc;
+ PRTraceHandle qh, rh;
+ RName *rnp = NULL;
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: GetTraceHandleFromName:\n\t"
+ "QName: %s, RName: %s", qName, rName ));
+
+ qh = PR_FindNextTraceQname( NULL );
+ while (qh != NULL)
+ {
+ rh = PR_FindNextTraceRname( NULL, qh );
+ while ( rh != NULL )
+ {
+ PR_GetTraceNameFromHandle( rh, &qn, &rn, &desc );
+ if ( (strcmp( qName, qn ) == 0)
+ && (strcmp( rName, rn ) == 0 ))
+ {
+ rnp = (RName *)rh;
+ goto foundIt;
+ }
+ rh = PR_FindNextTraceRname( rh, qh );
+ }
+ qh = PR_FindNextTraceQname( NULL );
+ }
+
+foundIt:
+ PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: GetConterHandleFromName: %p", rnp ));
+ return(rh);
+} /* end PR_GetTraceHandleFromName() */
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+ PR_GetTraceNameFromHandle(
+ PRTraceHandle handle, /* handle as search argument */
+ const char **qName, /* pointer to associated QName */
+ const char **rName, /* pointer to associated RName */
+ const char **description /* pointer to associated description */
+)
+{
+ RName *rnp = (RName *)handle;
+ QName *qnp = rnp->qName;
+
+ *qName = qnp->name;
+ *rName = rnp->name;
+ *description = rnp->desc;
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: GetConterNameFromHandle: "
+ "QNp: %p, RNp: %p,\n\tQName: %s, RName: %s, Desc: %s",
+ qnp, rnp, qnp->name, rnp->name, rnp->desc ));
+
+ return;
+} /* end PR_GetTraceNameFromHandle() */
+
+/*
+**
+*/
+PR_IMPLEMENT(PRTraceHandle)
+ PR_FindNextTraceQname(
+ PRTraceHandle handle
+)
+{
+ QName *qnp = (QName *)handle;
+
+ if ( PR_CLIST_IS_EMPTY( &qNameList ))
+ qnp = NULL;
+ else if ( qnp == NULL )
+ qnp = (QName *)PR_LIST_HEAD( &qNameList );
+ else if ( PR_NEXT_LINK( &qnp->link ) == &qNameList )
+ qnp = NULL;
+ else
+ qnp = (QName *)PR_NEXT_LINK( &qnp->link );
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: FindNextQname: Handle: %p, Returns: %p",
+ handle, qnp ));
+
+ return((PRTraceHandle)qnp);
+} /* end PR_FindNextTraceQname() */
+
+/*
+**
+*/
+PR_IMPLEMENT(PRTraceHandle)
+ PR_FindNextTraceRname(
+ PRTraceHandle rhandle,
+ PRTraceHandle qhandle
+)
+{
+ RName *rnp = (RName *)rhandle;
+ QName *qnp = (QName *)qhandle;
+
+
+ if ( PR_CLIST_IS_EMPTY( &qnp->rNameList ))
+ rnp = NULL;
+ else if ( rnp == NULL )
+ rnp = (RName *)PR_LIST_HEAD( &qnp->rNameList );
+ else if ( PR_NEXT_LINK( &rnp->link ) == &qnp->rNameList )
+ rnp = NULL;
+ else
+ rnp = (RName *)PR_NEXT_LINK( &rnp->link );
+
+ PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: FindNextRname: Rhandle: %p, QHandle: %p, Returns: %p",
+ rhandle, qhandle, rnp ));
+
+ return((PRTraceHandle)rnp);
+} /* end PR_FindNextTraceRname() */
+
+/*
+**
+*/
+static PRFileDesc * InitializeRecording( void )
+{
+ char *logFileName;
+ PRFileDesc *logFile;
+
+ /* Self initialize, if necessary */
+ if ( traceLock == NULL )
+ _PR_InitializeTrace();
+
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PR_RecordTraceEntries: begins"));
+
+ logLostData = 0; /* reset at entry */
+ logState = LogReset;
+
+ /* Get the filename for the logfile from the environment */
+ logFileName = PR_GetEnv( "NSPR_TRACE_LOG" );
+ if ( logFileName == NULL )
+ {
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("RecordTraceEntries: Environment variable not defined. Exiting"));
+ return NULL;
+ }
+
+ /* Open the logfile */
+ logFile = PR_Open( logFileName, PR_WRONLY | PR_CREATE_FILE, 0666 );
+ if ( logFile == NULL )
+ {
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("RecordTraceEntries: Cannot open %s as trace log file. OS error: %ld",
+ logFileName, PR_GetOSError()));
+ return NULL;
+ }
+ return logFile;
+} /* end InitializeRecording() */
+
+/*
+**
+*/
+static void ProcessOrders( void )
+{
+ switch ( logOrder )
+ {
+ case LogReset :
+ logOrder = logState = localState;
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("RecordTraceEntries: LogReset"));
+ break;
+
+ case LogSuspend :
+ localState = logOrder = logState = LogSuspend;
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("RecordTraceEntries: LogSuspend"));
+ break;
+
+ case LogResume :
+ localState = logOrder = logState = LogActive;
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("RecordTraceEntries: LogResume"));
+ break;
+
+ case LogStop :
+ logOrder = logState = LogStop;
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("RecordTraceEntries: LogStop"));
+ break;
+
+ default :
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("RecordTraceEntries: Invalid logOrder: %ld", logOrder ));
+ PR_ASSERT( 0 );
+ break;
+ } /* end switch() */
+ return ;
+} /* end ProcessOrders() */
+
+/*
+**
+*/
+static void WriteTraceSegment( PRFileDesc *logFile, void *buf, PRInt32 amount )
+{
+ PRInt32 rc;
+
+
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("WriteTraceSegment: Buffer: %p, Amount: %ld", buf, amount));
+ rc = PR_Write( logFile, buf , amount );
+ if ( rc == -1 )
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("RecordTraceEntries: PR_Write() failed. Error: %ld", PR_GetError() ));
+ else if ( rc != amount )
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("RecordTraceEntries: PR_Write() Tried to write: %ld, Wrote: %ld", amount, rc));
+ else
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("RecordTraceEntries: PR_Write(): Buffer: %p, bytes: %ld", buf, amount));
+
+ return;
+} /* end WriteTraceSegment() */
+
+/*
+**
+*/
+PR_IMPLEMENT(void)
+ PR_RecordTraceEntries(
+ void
+)
+{
+ PRFileDesc *logFile;
+ PRInt32 lostSegments;
+ PRInt32 currentSegment = 0;
+ void *buf;
+ PRBool doWrite;
+
+ logFile = InitializeRecording();
+ if ( logFile == NULL )
+ {
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PR_RecordTraceEntries: Failed to initialize"));
+ return;
+ }
+
+ /* Do this until told to stop */
+ while ( logState != LogStop )
+ {
+
+ PR_Lock( logLock );
+
+ while ( (logCount == 0) && ( logOrder == logState ) )
+ PR_WaitCondVar( logCVar, PR_INTERVAL_NO_TIMEOUT );
+
+ /* Handle state transitions */
+ if ( logOrder != logState )
+ ProcessOrders();
+
+ /* recalculate local controls */
+ if ( logCount )
+ {
+ lostSegments = logCount - logSegments;
+ if ( lostSegments > 0 )
+ {
+ logLostData += ( logCount - logSegments );
+ logCount = (logCount % logSegments);
+ currentSegment = logCount;
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("PR_RecordTraceEntries: LostData segments: %ld", logLostData));
+ }
+ else
+ {
+ logCount--;
+ }
+
+ buf = tBuf + ( logEntriesPerSegment * currentSegment );
+ if (++currentSegment >= logSegments )
+ currentSegment = 0;
+ doWrite = PR_TRUE;
+ }
+ else
+ doWrite = PR_FALSE;
+
+ PR_Unlock( logLock );
+
+ if ( doWrite == PR_TRUE )
+ {
+ if ( localState != LogSuspend )
+ WriteTraceSegment( logFile, buf, logSegSize );
+ else
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("RecordTraceEntries: PR_Write(): is suspended" ));
+ }
+
+ } /* end while(logState...) */
+
+ PR_Close( logFile );
+ PR_LOG( lm, PR_LOG_DEBUG,
+ ("RecordTraceEntries: exiting"));
+ return;
+} /* end PR_RecordTraceEntries() */
+
+/*
+**
+*/
+PR_IMPLEMENT(PRIntn)
+ PR_GetTraceEntries(
+ PRTraceEntry *buffer, /* where to write output */
+ PRInt32 count, /* number to get */
+ PRInt32 *found /* number you got */
+)
+{
+ PRInt32 rc;
+ PRInt32 copied = 0;
+
+ PR_Lock( traceLock );
+
+ /*
+ ** Depending on where the LastSeen and Next indices are,
+ ** copy the trace buffer in one or two pieces.
+ */
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("PR_GetTraceEntries: Next: %ld, LastSeen: %ld", next, fetchLastSeen));
+
+ if ( fetchLastSeen <= next )
+ {
+ while (( count-- > 0 ) && (fetchLastSeen < next ))
+ {
+ *(buffer + copied++) = *(tBuf + fetchLastSeen++);
+ }
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("PR_GetTraceEntries: Copied: %ld, LastSeen: %ld", copied, fetchLastSeen));
+ }
+ else /* copy in 2 parts */
+ {
+ while ( count-- > 0 && fetchLastSeen <= last )
+ {
+ *(buffer + copied++) = *(tBuf + fetchLastSeen++);
+ }
+ fetchLastSeen = 0;
+
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("PR_GetTraceEntries: Copied: %ld, LastSeen: %ld", copied, fetchLastSeen));
+
+ while ( count-- > 0 && fetchLastSeen < next )
+ {
+ *(buffer + copied++) = *(tBuf + fetchLastSeen++);
+ }
+ PR_LOG( lm, PR_LOG_ERROR,
+ ("PR_GetTraceEntries: Copied: %ld, LastSeen: %ld", copied, fetchLastSeen));
+ }
+
+ *found = copied;
+ rc = ( fetchLostData == PR_TRUE )? 1 : 0;
+ fetchLostData = PR_FALSE;
+
+ PR_Unlock( traceLock );
+ return rc;
+} /* end PR_GetTraceEntries() */
+
+#else /* !defined(FORCE_NSPR_TRACE) */
+/*
+** The trace facility is not defined when !DEBUG and !FORCE_NSPR_TRACE
+**
+*/
+
+#endif /* defined(FORCE_NSPR_TRACE) */
+
+/* end prtrace.c */
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/prvrsion.c b/pr/src/prvrsion.c
new file mode 100644
index 00000000..285f697b
--- /dev/null
+++ b/pr/src/prvrsion.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.
+ */
+
+#include "prinit.h"
+#include "prvrsion.h"
+
+/************************************************************************/
+/**************************IDENTITY AND VERSIONING***********************/
+/************************************************************************/
+#include "_pr_bld.h"
+#if !defined(_BUILD_TIME)
+#ifdef HAVE_LONG_LONG
+#define _BUILD_TIME 0
+#else
+#define _BUILD_TIME {0, 0}
+#endif
+#endif
+#if !defined(_BUILD_STRING)
+#define _BUILD_STRING ""
+#endif
+#if !defined(_PRODUCTION)
+#define _PRODUCTION ""
+#endif
+
+static PRVersionDescription prVersionDescription_libnsrp21 =
+{
+ /* version */ 2, /* this is the only one supported */
+ /* buildTime */ _BUILD_TIME, /* usecs since midnight 1/1/1970 GMT */
+ /* buildTimeString */ _BUILD_STRING, /* ditto, but human readable */
+ /* vMajor */ PR_VMAJOR, /* NSPR's version number */
+ /* vMinor */ PR_VMINOR, /* and minor version */
+ /* vPatch */ PR_VPATCH, /* and patch */
+ /* beta */ PR_BETA, /* beta build boolean */
+#if defined(DEBUG)
+ /* debug */ PR_TRUE, /* a debug build */
+#else
+ /* debug */ PR_FALSE, /* an optomized build */
+#endif
+ /* special */ PR_FALSE, /* they're all special, but ... */
+ /* filename */ _PRODUCTION, /* the produced library name */
+ /* description */ "Portable runtime", /* what we are */
+ /* security */ "N/A", /* not applicable here */
+ /* copywrite */ "Copyright (c) 1998 Netscape Communications Corporation. All Rights Reserved",
+ /* comment */ "License information: http://www.mozilla.org/NPL/",
+ /* specialString */ ""
+};
+
+PR_IMPLEMENT(const PRVersionDescription*) libVersionPoint(void)
+{
+ return &prVersionDescription_libnsrp21;
+} /* versionEntryPointType */
+
+/* prvrsion.c */
+
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..02335004
--- /dev/null
+++ b/pr/src/pthreads/ptio.c
@@ -0,0 +1,3565 @@
+/* -*- 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 <pthread.h>
+#include <string.h> /* for memset() */
+#include <sys/types.h>
+#include <dirent.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 <sys/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) || defined(NETBSD) || defined(OPENBSD)
+#define _PRSelectFdSetArg_t fd_set *
+#else
+#error "Cannot determine architecture"
+#endif
+
+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() */
+
+/**************************************************************************/
+
+/* These two functions are only used in assertions. */
+#if defined(DEBUG)
+
+static PRBool IsValidNetAddr(const PRNetAddr *addr)
+{
+ if ((addr != NULL)
+ && (addr->raw.family != AF_UNIX)
+#ifdef _PR_INET6
+ && (addr->raw.family != AF_INET6)
+#endif
+ && (addr->raw.family != AF_INET)) {
+ return PR_FALSE;
+ }
+ return PR_TRUE;
+}
+
+static PRBool IsValidNetAddrLen(const PRNetAddr *addr, PRInt32 addr_len)
+{
+ /*
+ * The definition of the length of a Unix domain socket address
+ * is not uniform, so we don't check it.
+ */
+ if ((addr != NULL)
+ && (addr->raw.family != AF_UNIX)
+ && (PR_NETADDR_SIZE(addr) != addr_len)) {
+ return PR_FALSE;
+ }
+ return PR_TRUE;
+}
+
+#endif /* DEBUG */
+
+/*****************************************************************************/
+/************************* 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_pending,
+ pt_continuation_recycle,
+ 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
+{
+ PRLock *ml; /* a little protection */
+ PRThread *thread; /* internal thread's identification */
+ 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 */
+} pt_tq;
+
+#if defined(DEBUG)
+
+PTDebug pt_debug; /* this is shared between several modules */
+
+PR_IMPLEMENT(void) PT_GetStats(PTDebug* here) { *here = pt_debug; }
+
+PR_IMPLEMENT(void) PT_FPrintStats(PRFileDesc *debug_out, const char *msg)
+{
+ PTDebug stats;
+ char buffer[100];
+ PRExplodedTime tod;
+ PRInt64 elapsed, aMil;
+ PT_GetStats(&stats); /* a copy */
+ PR_ExplodeTime(stats.timeStarted, PR_LocalTimeParameters, &tod);
+ (void)PR_FormatTime(buffer, sizeof(buffer), "%T", &tod);
+
+ LL_SUB(elapsed, PR_Now(), stats.timeStarted);
+ LL_I2L(aMil, 1000000);
+ LL_DIV(elapsed, elapsed, aMil);
+
+ if (NULL != msg) PR_fprintf(debug_out, "%s", msg);
+ PR_fprintf(
+ debug_out, "\tstarted: %s[%lld]\n", buffer, elapsed);
+ PR_fprintf(
+ debug_out, "\tmissed predictions: %u\n", stats.predictionsFoiled);
+ PR_fprintf(
+ debug_out, "\tpollingList max: %u\n", stats.pollingListMax);
+ PR_fprintf(
+ debug_out, "\tcontinuations served: %u\n", stats.continuationsServed);
+ PR_fprintf(
+ debug_out, "\trecycles needed: %u\n", stats.recyclesNeeded);
+ PR_fprintf(
+ debug_out, "\tquiescent IO: %u\n", stats.quiescentIO);
+ PR_fprintf(
+ debug_out, "\tlocks [created: %u, destroyed: %u]\n",
+ stats.locks_created, stats.locks_destroyed);
+ PR_fprintf(
+ debug_out, "\tlocks [acquired: %u, released: %u]\n",
+ stats.locks_acquired, stats.locks_released);
+ PR_fprintf(
+ debug_out, "\tcvars [created: %u, destroyed: %u]\n",
+ stats.cvars_created, stats.cvars_destroyed);
+ PR_fprintf(
+ debug_out, "\tcvars [notified: %u, delayed_delete: %u]\n",
+ stats.cvars_notified, stats.delayed_cv_deletes);
+} /* PT_FPrintStats */
+
+#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 defined(DEBUG)
+ {
+ PRIntn count;
+ pt_Continuation *tmp;
+ PR_ASSERT((pt_tq.head == NULL) == (pt_tq.tail == NULL));
+ PR_ASSERT((pt_tq.head == NULL) == (pt_tq.op_count == 0));
+ for (tmp = pt_tq.head, count = 0; tmp != NULL; tmp = tmp->next) count += 1;
+ PR_ASSERT(count == pt_tq.op_count);
+ for (tmp = pt_tq.tail, count = 0; tmp != NULL; tmp = tmp->prev) count += 1;
+ PR_ASSERT(count == pt_tq.op_count);
+ }
+#endif /* defined(DEBUG) */
+
+ /*
+ * 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 pt_ContinuationThreadInternal). 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;
+
+#if defined(DEBUG)
+ {
+ PRIntn count;
+ pt_Continuation *tmp;
+ PR_ASSERT(pt_tq.head != NULL);
+ PR_ASSERT(pt_tq.tail != NULL);
+ PR_ASSERT(pt_tq.op_count != 0);
+ PR_ASSERT(pt_tq.head->prev == NULL);
+ PR_ASSERT(pt_tq.tail->next == NULL);
+ if (pt_tq.op_count > 1)
+ {
+ PR_ASSERT(pt_tq.head->next != NULL);
+ PR_ASSERT(pt_tq.tail->prev != NULL);
+ }
+ else
+ {
+ PR_ASSERT(pt_tq.head->next == NULL);
+ PR_ASSERT(pt_tq.tail->prev == NULL);
+ }
+ for (tmp = pt_tq.head, count = 0; tmp != NULL; tmp = tmp->next) count += 1;
+ PR_ASSERT(count == pt_tq.op_count);
+ for (tmp = pt_tq.tail, count = 0; tmp != NULL; tmp = tmp->prev) count += 1;
+ PR_ASSERT(count == pt_tq.op_count);
+ }
+#endif /* defined(DEBUG) */
+
+} /* 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;
+
+#if defined(DEBUG)
+ {
+ PRIntn count;
+ pt_Continuation *tmp;
+ PR_ASSERT(pt_tq.head != NULL);
+ PR_ASSERT(pt_tq.tail != NULL);
+ PR_ASSERT(pt_tq.op_count != 0);
+ PR_ASSERT(pt_tq.head->prev == NULL);
+ PR_ASSERT(pt_tq.tail->next == NULL);
+ if (pt_tq.op_count > 1)
+ {
+ PR_ASSERT(pt_tq.head->next != NULL);
+ PR_ASSERT(pt_tq.tail->prev != NULL);
+ }
+ else
+ {
+ PR_ASSERT(pt_tq.head->next == NULL);
+ PR_ASSERT(pt_tq.tail->prev == NULL);
+ }
+ for (tmp = pt_tq.head, count = 0; tmp != NULL; tmp = tmp->next) count += 1;
+ PR_ASSERT(count == pt_tq.op_count);
+ for (tmp = pt_tq.tail, count = 0; tmp != NULL; tmp = tmp->prev) count += 1;
+ PR_ASSERT(count == pt_tq.op_count);
+ }
+#endif /* defined(DEBUG) */
+
+ /* 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);
+
+#if defined(DEBUG)
+ {
+ PRIntn count;
+ pt_Continuation *tmp;
+ PR_ASSERT((pt_tq.head == NULL) == (pt_tq.tail == NULL));
+ PR_ASSERT((pt_tq.head == NULL) == (pt_tq.op_count == 0));
+ for (tmp = pt_tq.head, count = 0; tmp != NULL; tmp = tmp->next) count += 1;
+ PR_ASSERT(count == pt_tq.op_count);
+ for (tmp = pt_tq.tail, count = 0; tmp != NULL; tmp = tmp->prev) count += 1;
+ PR_ASSERT(count == pt_tq.op_count);
+ }
+#endif /* defined(DEBUG) */
+
+ return next;
+} /* pt_FinishTimedInternal */
+
+static void pt_ContinuationThreadInternal(pt_Continuation *my_op)
+{
+ /* initialization */
+ PRInt32 msecs, mx_poll_ticks;
+ PRThreadPriority priority; /* used to save caller's prio */
+ PRIntn pollingListUsed; /* # entries used in the list */
+ PRIntn pollingListNeeded; /* # entries needed this time */
+ static struct pollfd *pollingList = 0; /* list built for polling */
+ static PRIntn pollingSlotsAllocated = 0;/* # entries available in list */
+ static pt_Continuation **pollingOps = 0;/* list paralleling polling list */
+
+ PR_Unlock(pt_tq.ml); /* don't need that silly lock for a bit */
+
+ priority = PR_GetThreadPriority(pt_tq.thread);
+ PR_SetThreadPriority(pt_tq.thread, PR_PRIORITY_HIGH);
+
+ 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, *next_op;
+
+ PR_ASSERT(NULL != pt_tq.head);
+
+ pollingListNeeded = pt_tq.op_count;
+
+ /*
+ * 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.
+ *
+ * The space allocated is for both a vector that parallels the
+ * polling list, providing pointers directly into the operation's
+ * table and the polling list itself. There is a guard element
+ * between the two structures.
+ */
+ pollingListNeeded += 10;
+ if (pollingListNeeded > pollingSlotsAllocated)
+ {
+ if (NULL != pollingOps) PR_Free(pollingOps);
+ pollingOps = (pt_Continuation**)PR_Malloc(
+ sizeof(pt_Continuation**) + pollingListNeeded *
+ (sizeof(struct pollfd) + sizeof(pt_Continuation*)));
+ PR_ASSERT(NULL != pollingOps);
+ pollingSlotsAllocated = pollingListNeeded;
+ pollingOps[pollingSlotsAllocated] = (pt_Continuation*)-1;
+ pollingList = (struct pollfd*)(&pollingOps[pollingSlotsAllocated + 1]);
+
+ }
+
+#if defined(DEBUG)
+ if (pollingListNeeded > pt_debug.pollingListMax)
+ pt_debug.pollingListMax = pollingListNeeded;
+#endif
+
+ /*
+ ** This is interrupt processing. If this thread was interrupted,
+ ** the thread state will have the PT_THREAD_ABORTED bit set. This
+ ** overrides good completions as well as timeouts.
+ **
+ ** BTW, it does no good to hold the lock here. This lock doesn't
+ ** protect the thread structure in any way. Testing the bit and
+ ** (perhaps) resetting it are safe 'cause it's the only modifiable
+ ** bit in that word.
+ */
+ if (pt_tq.thread->state & PT_THREAD_ABORTED)
+ {
+ my_op->status = pt_continuation_abort;
+ pt_tq.thread->state &= ~PT_THREAD_ABORTED;
+ }
+
+
+ /*
+ * 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;
+ next_op = pt_FinishTimedInternal(op);
+ if (op == my_op) goto recycle;
+ else op = next_op;
+ PR_ASSERT(NULL != pt_tq.head);
+ }
+ else
+ {
+ op->status = pt_continuation_pending;
+ if (pollingListUsed >= pollingSlotsAllocated)
+ {
+#if defined(DEBUG)
+ pt_debug.predictionsFoiled += 1;
+#endif
+ break;
+ }
+ PR_ASSERT((pt_Continuation*)-1 == pollingOps[pollingSlotsAllocated]);
+ pollingOps[pollingListUsed] = op;
+ pollingList[pollingListUsed].revents = 0;
+ pollingList[pollingListUsed].fd = op->arg1.osfd;
+ pollingList[pollingListUsed].events = op->event;
+ pollingListUsed += 1;
+ op = op->next;
+ }
+ }
+
+ /*
+ * 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);
+ }
+
+ 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) continue; /* make it rethink things */
+
+ PR_ASSERT((pt_Continuation*)-1 == pollingOps[pollingSlotsAllocated]);
+
+ 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.
+ */
+
+ for (pollIndex = 0; pollIndex < pollingListUsed; ++pollIndex)
+ {
+ PRIntn fd = pollingList[pollIndex].fd;
+ PRInt16 events = pollingList[pollIndex].events;
+ PRInt16 revents = pollingList[pollIndex].revents;
+
+ op = pollingOps[pollIndex]; /* this is the operation */
+
+ /* (ref: Bug #153459)
+ ** In case of POLLERR we let the operation retry in hope
+ ** of getting a more definitive OS error.
+ */
+ if ((revents & POLLNVAL) /* busted in all cases */
+ || ((events & POLLOUT) && (revents & POLLHUP))) /* write op & hup */
+ {
+ PR_Lock(pt_tq.ml);
+ op->result.code = -1;
+ if (POLLNVAL & revents) op->syserrno = EBADF;
+ else if (POLLHUP & revents) op->syserrno = EPIPE;
+ (void)pt_FinishTimedInternal(op);
+ if (op == my_op) goto recycle;
+ PR_Unlock(pt_tq.ml);
+ }
+ else if ((0 != revents)
+ && (pt_continuation_pending == op->status)
+ && (op->function(op, revents)))
+ {
+ /*
+ * Only good?(?) revents left. Operations not pending
+ * will be pruned next time we build a list. This operation
+ * will be pruned if the continueation indicates it is
+ * finished.
+ */
+
+ PR_Lock(pt_tq.ml);
+ (void)pt_FinishTimedInternal(op);
+ if (op == my_op) goto recycle;
+ PR_Unlock(pt_tq.ml);
+ }
+ }
+ }
+
+ /*
+ * 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.
+ */
+ now = PR_IntervalNow();
+ PR_Lock(pt_tq.ml);
+ while ((NULL != pt_tq.head)
+ && (PR_INTERVAL_NO_TIMEOUT != pt_tq.head->timeout))
+ {
+ op = pt_tq.head; /* get a copy of this before finishing it */
+ if ((PRInt32)(op->absolute - now) > 0) break;
+ /*
+ * The head element of the timed queue has timed out. Record
+ * the reason for completion and take it out of the list.
+ */
+ op->result.code = -1;
+ op->syserrno = ETIMEDOUT;
+ (void)pt_FinishTimedInternal(op);
+
+ /*
+ * If it's 'my_op' then we have to switch threads. Exit w/o
+ * finishing the scan. The scan will be completed when another
+ * thread calls in acting as the continuation thread.
+ */
+ if (op == my_op) goto recycle; /* exit w/o unlocking */
+ }
+ PR_Unlock(pt_tq.ml);
+ }
+
+ PR_NOT_REACHED("This is a while(true) loop /w no breaks");
+
+recycle:
+ /*
+ ** Recycling the continuation thread.
+ **
+ ** The thread we were using for I/O continuations just completed
+ ** the I/O it submitted. It has to return to it's caller. We need
+ ** another thread to act in the continuation role. We can do that
+ ** by taking any operation off the timed queue, setting its state
+ ** to 'recycle' and notifying the condition.
+ **
+ ** Selecting a likely thread seems like magic. I'm going to try
+ ** using one that has the longest (or no) timeout, pt_tq.tail.
+ ** If that slot's empty, then there's no outstanding I/O and we
+ ** don't need a thread at all.
+ **
+ ** BTW, we're locked right now, and we'll be returning with the
+ ** the lock held as well. Seems odd, doesn't it?
+ */
+
+ /* $$$ should this be called with the lock held? $$$ */
+ PR_SetThreadPriority(pt_tq.thread, priority); /* reset back to caller's */
+
+ PR_ASSERT((NULL == pt_tq.head) == (0 == pt_tq.op_count));
+ PR_ASSERT((NULL == pt_tq.head) == (NULL == pt_tq.tail));
+ PR_ASSERT(pt_continuation_done == my_op->status);
+
+ if (NULL != pt_tq.tail)
+ {
+ if (pt_tq.tail->status != pt_continuation_abort)
+ {
+ pt_tq.tail->status = pt_continuation_recycle;
+ }
+ PR_NotifyCondVar(pt_tq.tail->complete);
+#if defined(DEBUG)
+ pt_debug.recyclesNeeded += 1;
+#endif
+ }
+#if defined(DEBUG)
+ else pt_debug.quiescentIO += 1;
+#endif
+
+} /* pt_ContinuationThreadInternal */
+
+static PRIntn pt_Continue(pt_Continuation *op)
+{
+ PRStatus rv;
+ PRThread *self = PR_GetCurrentThread();
+ /* lazy allocation of the thread's cv */
+ if (NULL == self->io_cv)
+ self->io_cv = PR_NewCondVar(pt_tq.ml);
+ /* Finish filling in the blank slots */
+ op->complete = self->io_cv;
+ op->status = pt_continuation_pending; /* set default value */
+ PR_Lock(pt_tq.ml); /* we provide the locking */
+
+ pt_InsertTimedInternal(op); /* insert in the structure */
+
+ /*
+ ** At this point, we try to decide whether there is a continuation
+ ** thread, or whether we should assign this one to serve in that role.
+ */
+ do
+ {
+ if (NULL == pt_tq.thread)
+ {
+ /*
+ ** We're the one. Call the processing function with the lock
+ ** held. It will return with it held as well, though there
+ ** will certainly be times within the function when it gets
+ ** released.
+ */
+ pt_tq.thread = self; /* I'm taking control */
+ pt_ContinuationThreadInternal(op); /* go slash and burn */
+ PR_ASSERT(pt_continuation_done == op->status);
+ pt_tq.thread = NULL; /* I'm abdicating my rule */
+ }
+ else
+ {
+ 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. When he does
+ * notice, this operation will be finished and the op's status
+ * marked as pt_continuation_done.
+ */
+ if ((PR_FAILURE == rv) /* the wait was interrupted */
+ && (PR_PENDING_INTERRUPT_ERROR == PR_GetError()))
+ {
+ if (pt_continuation_done == op->status)
+ {
+ /*
+ * The op is done and has been removed
+ * from the timed queue. We must not
+ * change op->status, otherwise this
+ * thread will go around the loop again.
+ *
+ * It's harsh to mark the op failed with
+ * interrupt error when the io is already
+ * done, but we should indicate the fact
+ * that the thread was interrupted. So
+ * we set the aborted flag to abort the
+ * thread's next blocking call. Is this
+ * the right thing to do?
+ */
+ self->state |= PT_THREAD_ABORTED;
+ }
+ else
+ {
+ /* go around the loop again */
+ op->status = pt_continuation_abort;
+ }
+ }
+ /*
+ * If we're to recycle, continue within this loop. This will
+ * cause this thread to become the continuation thread.
+ */
+
+ }
+ } while (pt_continuation_done != op->status);
+
+
+ PR_Unlock(pt_tq.ml); /* we provided the locking */
+
+ 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.
+ */
+#if defined(SOLARIS)
+ op->result.code = read(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.amount);
+#else
+ op->result.code = recv(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags);
+#endif
+ 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)
+{
+ PRIntn bytes;
+ /*
+ * 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).
+ */
+#if defined(SOLARIS)
+ bytes = write(op->arg1.osfd, op->arg2.buffer, op->arg3.amount);
+#else
+ bytes = send(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags);
+#endif
+ 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)
+{
+ PRIntn bytes;
+ /*
+ * 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).
+ */
+ 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)
+{
+ PRIntn bytes;
+ struct iovec *iov = (struct iovec*)op->arg2.buffer;
+ /*
+ * 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.
+ */
+ bytes = writev(op->arg1.osfd, iov, op->arg3.amount);
+ op->syserrno = errno;
+ if (bytes > 0) /* this is progress */
+ {
+ PRIntn iov_index;
+ 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 */
+
+void _PR_InitIO()
+{
+ PRIntn rv;
+
+ pt_tq.ml = PR_NewLock();
+ PR_ASSERT(NULL != pt_tq.ml);
+
+#if defined(DEBUG)
+ memset(&pt_debug, 0, sizeof(PTDebug));
+ pt_debug.timeStarted = PR_Now();
+#endif
+
+ rv = PTHREAD_CONDATTR_INIT(&_pt_cvar_attr);
+ PR_ASSERT(0 == rv);
+
+ pt_tq.thread = NULL;
+
+ _pr_flock_lock = PR_NewLock();
+ PR_ASSERT(NULL != _pr_flock_lock);
+ _pr_rename_lock = PR_NewLock();
+ PR_ASSERT(NULL != _pr_rename_lock);
+
+ _PR_InitFdCache(); /* do that */
+
+ _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_InitIO */
+
+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)
+{
+ if ((NULL == fd) || (NULL == fd->secret)
+ || ((_PR_FILEDESC_OPEN != fd->secret->state)
+ && (_PR_FILEDESC_CLOSED != fd->secret->state)))
+ {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+ return PR_FAILURE;
+ }
+ if (pt_TestAbort()) return PR_FAILURE;
+
+ if (_PR_FILEDESC_OPEN == fd->secret->state)
+ {
+ if (-1 == close(fd->secret->md.osfd))
+ {
+ pt_MapError(_PR_MD_MAP_CLOSE_ERROR, errno);
+ return PR_FAILURE;
+ }
+ fd->secret->state = _PR_FILEDESC_CLOSED;
+ }
+ _PR_Putfd(fd);
+ return PR_SUCCESS;
+} /* pt_Close */
+
+static PRInt32 pt_Read(PRFileDesc *fd, void *buf, PRInt32 amount)
+{
+ PRInt32 syserrno, bytes = -1;
+
+ if (pt_TestAbort()) return bytes;
+
+ 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 bytes;
+
+ 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)
+{
+ return _PR_MD_LSEEK(fd, offset, whence);
+} /* pt_Seek */
+
+static PRInt64 pt_Seek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence)
+{
+ return _PR_MD_LSEEK64(fd, offset, whence);
+} /* pt_Seek64 */
+
+static PRInt32 pt_Available_f(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;
+} /* pt_Available_f */
+
+static PRInt64 pt_Available64_f(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;
+} /* pt_Available64_f */
+
+static PRInt32 pt_Available_s(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_s */
+
+static PRInt64 pt_Available64_s(PRFileDesc *fd)
+{
+ PRInt64 rv;
+ LL_I2L(rv, pt_Available_s(fd));
+ return rv;
+} /* pt_Available64_s */
+
+static PRStatus pt_FileInfo(PRFileDesc *fd, PRFileInfo *info)
+{
+ PRInt32 rv = _PR_MD_GETOPENFILEINFO(fd, info);
+ return (-1 == rv) ? PR_FAILURE : PR_SUCCESS;
+} /* pt_FileInfo */
+
+static PRStatus pt_FileInfo64(PRFileDesc *fd, PRFileInfo64 *info)
+{
+ PRInt32 rv = _PR_MD_GETOPENFILEINFO64(fd, info);
+ return (-1 == rv) ? PR_FAILURE : PR_SUCCESS;
+} /* pt_FileInfo64 */
+
+static PRStatus pt_Synch(PRFileDesc *fd)
+{
+ return (NULL == fd) ? PR_FAILURE : PR_SUCCESS;
+} /* pt_Synch */
+
+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)
+{
+ int err;
+ PRInt32 osfd;
+ PRFileDesc *bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
+
+ if (NULL == bottom) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ 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;
+ }
+
+ 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 _PR_HAVE_SOCKADDR_LEN
+ /* mask off the first byte of struct sockaddr (the length field) */
+ if (addr)
+ {
+ *((unsigned char *) addr) = 0;
+#ifdef IS_LITTLE_ENDIAN
+ addr->raw.family = ntohs(addr->raw.family);
+#endif
+ }
+#endif /* _PR_HAVE_SOCKADDR_LEN */
+ newfd = pt_SetMethods(osfd, PR_DESC_SOCKET_TCP);
+ if (newfd == NULL) close(osfd); /* $$$ whoops! this doesn't work $$$ */
+ else
+ {
+ PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
+ PR_ASSERT(IsValidNetAddrLen(addr, addr_len) == PR_TRUE);
+ }
+ 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;
+
+ PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
+
+ if (addr->raw.family == AF_UNIX)
+ {
+ /* Disallow relative pathnames */
+ if (addr->local.path[0] != '/')
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ }
+ else
+ {
+ 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 PRInt16 pt_Poll(PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
+{
+ *out_flags = 0;
+ return in_flags;
+} /* pt_Poll */
+
+static PRInt32 pt_Recv(
+ PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ PRInt32 syserrno, bytes = -1;
+
+ if (pt_TestAbort()) return bytes;
+
+ /* recv() is a much slower call on pre-2.6 Solaris than read(). */
+#if defined(SOLARIS)
+ PR_ASSERT(0 == flags);
+ bytes = read(fd->secret->md.osfd, buf, amount);
+#else
+ bytes = recv(fd->secret->md.osfd, buf, amount, flags);
+#endif
+ 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 bytes;
+
+ /*
+ * On pre-2.6 Solaris, send() is much slower than write().
+ * On 2.6 and beyond, with in-kernel sockets, send() and
+ * write() are fairly equivalent in performance.
+ */
+#if defined(SOLARIS)
+ PR_ASSERT(0 == flags);
+ bytes = write(fd->secret->md.osfd, PT_SENDBUF_CAST buf, amount);
+#else
+ bytes = send(fd->secret->md.osfd, PT_SENDBUF_CAST buf, amount, flags);
+#endif
+ 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 bytes;
+
+ PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
+ 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 bytes;
+
+ 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 _PR_HAVE_SOCKADDR_LEN
+ if (bytes >= 0)
+ {
+ /* mask off the first byte of struct sockaddr (the length field) */
+ if (addr)
+ {
+ *((unsigned char *) addr) = 0;
+#ifdef IS_LITTLE_ENDIAN
+ addr->raw.family = ntohs(addr->raw.family);
+#endif
+ }
+ }
+#endif /* _PR_HAVE_SOCKADDR_LEN */
+ 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;
+ /* The socket must be in blocking mode. */
+ if (sd->secret->nonblocking)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ 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;
+
+ if (pt_TestAbort()) return rv;
+ /* The socket must be in blocking mode. */
+ if (sd->secret->nonblocking)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return rv;
+ }
+
+ /*
+ ** The timeout does not apply to the accept portion of the
+ ** operation - it waits indefinitely.
+ */
+ accepted = PR_Accept(sd, &remote, PR_INTERVAL_NO_TIMEOUT);
+ if (NULL == accepted) return rv;
+
+ rv = PR_Recv(accepted, buf, amount, 0, timeout);
+ if (rv >= 0)
+ {
+ /* copy the new info out where caller can see it */
+ PRPtrdiff aligned = (PRPtrdiff)buf + amount + sizeof(void*) - 1;
+ *raddr = (PRNetAddr*)(aligned & ~(sizeof(void*) - 1));
+ memcpy(*raddr, &remote, PR_NETADDR_SIZE(&remote));
+ *nd = accepted;
+ 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);
+ if (rv == -1) {
+ pt_MapError(_PR_MD_MAP_GETSOCKNAME_ERROR, errno);
+ return PR_FAILURE;
+ } else {
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ /* mask off the first byte of struct sockaddr (the length field) */
+ if (addr)
+ {
+ *((unsigned char *) addr) = 0;
+#ifdef IS_LITTLE_ENDIAN
+ addr->raw.family = ntohs(addr->raw.family);
+#endif
+ }
+#endif /* _PR_HAVE_SOCKADDR_LEN */
+ PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
+ PR_ASSERT(IsValidNetAddrLen(addr, addr_len) == PR_TRUE);
+ 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);
+
+ if (rv == -1) {
+ pt_MapError(_PR_MD_MAP_GETPEERNAME_ERROR, errno);
+ return PR_FAILURE;
+ } else {
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ /* mask off the first byte of struct sockaddr (the length field) */
+ if (addr)
+ {
+ *((unsigned char *) addr) = 0;
+#ifdef IS_LITTLE_ENDIAN
+ addr->raw.family = ntohs(addr->raw.family);
+#endif
+ }
+#endif /* _PR_HAVE_SOCKADDR_LEN */
+ PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
+ PR_ASSERT(IsValidNetAddrLen(addr, addr_len) == PR_TRUE);
+ 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 xbool;
+ length = sizeof(xbool);
+ rv = getsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&xbool, &length);
+ PR_ASSERT((-1 == rv) || (sizeof(xbool) == length));
+ data->value.mcast_loopback = (0 == xbool) ? 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 xbool = data->value.mcast_loopback ? 1 : 0;
+ rv = setsockopt(
+ fd->secret->md.osfd, level, name,
+ (char*)&xbool, sizeof(xbool));
+ 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_f,
+ pt_Available64_f,
+ 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,
+ pt_Poll,
+ (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_s,
+ pt_Available64_s,
+ 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,
+ pt_Poll,
+ 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_s,
+ pt_Available64_s,
+ 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,
+ pt_Poll,
+ (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) || defined(NETBSD) \
+ || defined(OPENBSD)
+#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 = _PR_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(const PRIOMethods*) PR_GetFileMethods()
+{
+ return &_pr_file_methods;
+} /* PR_GetFileMethods */
+
+PR_IMPLEMENT(const PRIOMethods*) PR_GetTCPMethods()
+{
+ return &_pr_tcp_methods;
+} /* PR_GetTCPMethods */
+
+PR_IMPLEMENT(const PRIOMethods*) PR_GetUDPMethods()
+{
+ return &_pr_udp_methods;
+} /* PR_GetUDPMethods */
+
+PR_IMPLEMENT(PRFileDesc*) PR_AllocFileDesc(
+ PRInt32 osfd, const PRIOMethods *methods)
+{
+ PRFileDesc *fd = _PR_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
+ && PF_UNIX != domain)
+ {
+ 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 = _md_iovector._open64(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 = _PR_MD_GETFILEINFO(fn, info);
+ return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
+} /* PR_GetFileInfo */
+
+PR_IMPLEMENT(PRStatus) PR_GetFileInfo64(const char *fn, PRFileInfo64 *info)
+{
+ PRInt32 rv;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+ rv = _PR_MD_GETFILEINFO64(fn, info);
+ return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
+} /* PR_GetFileInfo64 */
+
+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)
+ {
+ if (closedir(dir->md.d) == -1)
+ {
+ _PR_MD_MAP_CLOSEDIR_ERROR(errno);
+ return PR_FAILURE;
+ }
+ dir->md.d = NULL;
+ PR_DELETE(dir);
+ }
+ 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 (pt_TestAbort()) return -1;
+
+ if (0 == npds) PR_Sleep(timeout);
+ else
+ {
+ PRIntn index, msecs;
+ struct pollfd *syspoll = NULL;
+ syspoll = (struct pollfd*)PR_MALLOC(npds * sizeof(struct pollfd));
+ if (NULL == syspoll)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+ for (index = 0; index < npds; ++index)
+ {
+ PRInt16 in_flags_read = 0, in_flags_write = 0;
+ PRInt16 out_flags_read = 0, out_flags_write = 0;
+
+ if ((NULL != pds[index].fd) && (0 != pds[index].in_flags))
+ {
+ if (pds[index].in_flags & PR_POLL_READ)
+ {
+ in_flags_read = (pds[index].fd->methods->poll)(
+ pds[index].fd,
+ pds[index].in_flags & ~PR_POLL_WRITE,
+ &out_flags_read);
+ }
+ if (pds[index].in_flags & PR_POLL_WRITE)
+ {
+ in_flags_write = (pds[index].fd->methods->poll)(
+ pds[index].fd,
+ pds[index].in_flags & ~PR_POLL_READ,
+ &out_flags_write);
+ }
+ if ((0 != (in_flags_read & out_flags_read))
+ || (0 != (in_flags_write & out_flags_write)))
+ {
+ /* this one is ready right now */
+ if (0 == ready)
+ {
+ /*
+ * We will return without calling the system
+ * poll function. So zero the out_flags
+ * fields of all the poll descriptors before
+ * this one.
+ */
+ int i;
+ for (i = 0; i < index; i++)
+ {
+ pds[i].out_flags = 0;
+ }
+ }
+ ready += 1;
+ pds[index].out_flags = out_flags_read | out_flags_write;
+ }
+ else
+ {
+ /* now locate the NSPR layer at the bottom of the stack */
+ PRFileDesc *bottom = PR_GetIdentitiesLayer(
+ pds[index].fd, PR_NSPR_IO_LAYER);
+ PR_ASSERT(NULL != bottom); /* what to do about that? */
+ pds[index].out_flags = 0; /* pre-condition */
+ if ((NULL != bottom)
+ && (_PR_FILEDESC_OPEN == bottom->secret->state))
+ {
+ if (0 == ready)
+ {
+ syspoll[index].fd = bottom->secret->md.osfd;
+ syspoll[index].events = 0;
+ if (in_flags_read & PR_POLL_READ)
+ {
+ pds[index].out_flags |=
+ _PR_POLL_READ_SYS_READ;
+ syspoll[index].events |= POLLIN;
+ }
+ if (in_flags_read & PR_POLL_WRITE)
+ {
+ pds[index].out_flags |=
+ _PR_POLL_READ_SYS_WRITE;
+ syspoll[index].events |= POLLOUT;
+ }
+ if (in_flags_write & PR_POLL_READ)
+ {
+ pds[index].out_flags |=
+ _PR_POLL_WRITE_SYS_READ;
+ syspoll[index].events |= POLLIN;
+ }
+ if (in_flags_write & PR_POLL_WRITE)
+ {
+ pds[index].out_flags |=
+ _PR_POLL_WRITE_SYS_WRITE;
+ syspoll[index].events |= POLLOUT;
+ }
+ if (pds[index].in_flags & PR_POLL_EXCEPT)
+ syspoll[index].events |= POLLPRI;
+ }
+ }
+ else
+ {
+ if (0 == ready)
+ {
+ int i;
+ for (i = 0; i < index; i++)
+ {
+ pds[i].out_flags = 0;
+ }
+ }
+ ready += 1; /* this will cause an abrupt return */
+ pds[index].out_flags = PR_POLL_NVAL; /* bogii */
+ }
+ }
+ }
+ else
+ {
+ /* make poll() ignore this entry */
+ syspoll[index].fd = -1;
+ }
+ }
+ 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;
+
+ if (EINTR == oserror)
+ {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ goto retry;
+ else if (timeout == PR_INTERVAL_NO_WAIT)
+ ready = 0; /* don't retry, just time out */
+ else
+ {
+ elapsed = (PRIntervalTime) (PR_IntervalNow()
+ - start);
+ if (elapsed > timeout)
+ ready = 0; /* timed out */
+ else
+ {
+ remaining = timeout - elapsed;
+ msecs = PR_IntervalToMilliseconds(remaining);
+ goto retry;
+ }
+ }
+ }
+ else
+ {
+ _PR_MD_MAP_POLL_ERROR(oserror);
+ }
+ }
+ else if (ready > 0)
+ {
+ for (index = 0; index < npds; ++index)
+ {
+ PRInt16 out_flags = 0;
+ if ((NULL != pds[index].fd) && (0 != pds[index].in_flags))
+ {
+ if (0 != syspoll[index].revents)
+ {
+ if (syspoll[index].revents & POLLIN)
+ {
+ if (pds[index].out_flags
+ & _PR_POLL_READ_SYS_READ)
+ {
+ out_flags |= PR_POLL_READ;
+ }
+ if (pds[index].out_flags
+ & _PR_POLL_WRITE_SYS_READ)
+ {
+ out_flags |= PR_POLL_WRITE;
+ }
+ }
+ if (syspoll[index].revents & POLLOUT)
+ {
+ if (pds[index].out_flags
+ & _PR_POLL_READ_SYS_WRITE)
+ {
+ out_flags |= PR_POLL_READ;
+ }
+ if (pds[index].out_flags
+ & _PR_POLL_WRITE_SYS_WRITE)
+ {
+ out_flags |= PR_POLL_WRITE;
+ }
+ }
+ if (syspoll[index].revents & POLLPRI)
+ out_flags |= PR_POLL_EXCEPT;
+ if (syspoll[index].revents & POLLERR)
+ out_flags |= PR_POLL_ERR;
+ if (syspoll[index].revents & POLLNVAL)
+ out_flags |= PR_POLL_NVAL;
+ if (syspoll[index].revents & POLLHUP)
+ out_flags |= PR_POLL_HUP;
+ }
+ }
+ pds[index].out_flags = out_flags;
+ }
+ }
+ }
+
+ 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 *bottom)
+{
+ PRInt32 osfd = -1;
+ bottom = (NULL == bottom) ?
+ NULL : PR_GetIdentitiesLayer(bottom, PR_NSPR_IO_LAYER);
+ if (NULL == bottom) PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ else osfd = bottom->secret->md.osfd;
+ return osfd;
+} /* 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 -1;
+
+ if (-1 == stat(name, buf)) {
+ pt_MapError(_PR_MD_MAP_STAT_ERROR, errno);
+ return -1;
+ } else {
+ return 0;
+ }
+}
+#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..188a471a
--- /dev/null
+++ b/pr/src/pthreads/ptmisc.c
@@ -0,0 +1,44 @@
+/* -*- 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")}
+
+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..b49cdc3f
--- /dev/null
+++ b/pr/src/pthreads/ptsynch.c
@@ -0,0 +1,741 @@
+/* -*- 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_mutexattr_t _pt_mattr;
+static pthread_condattr_t _pt_cvar_attr;
+
+#if defined(DEBUG)
+extern PTDebug pt_debug; /* this is shared between several modules */
+
+#if 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 /* defined(_PR_DCETHREADS) */
+#endif /* defined(DEBUG) */
+
+/**************************************************************/
+/**************************************************************/
+/*****************************LOCKS****************************/
+/**************************************************************/
+/**************************************************************/
+
+void _PR_InitLocks(void)
+{
+ int rv;
+ rv = PTHREAD_MUTEXATTR_INIT(&_pt_mattr);
+ PR_ASSERT(0 == rv);
+
+#if defined(AIX)
+ rv = pthread_mutexattr_setkind_np(&_pt_mattr, MUTEX_FAST_NP);
+ PR_ASSERT(0 == rv);
+#endif
+
+ 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)
+ {
+ PRCondVar *cv = notified->cv[index].cv;
+ PR_ASSERT(NULL != cv);
+ PR_ASSERT(0 != notified->cv[index].times);
+ if (-1 == notified->cv[index].times)
+ {
+ rv = pthread_cond_broadcast(&cv->cv);
+ PR_ASSERT(0 == rv);
+ }
+ else
+ {
+ while (notified->cv[index].times-- > 0)
+ {
+ rv = pthread_cond_signal(&cv->cv);
+ PR_ASSERT((0 == rv) || (EINVAL == rv));
+ }
+ }
+#if defined(DEBUG)
+ pt_debug.cvars_notified += 1;
+ if (0 > PR_AtomicDecrement(&cv->notify_pending))
+ {
+ pt_debug.delayed_cv_deletes += 1;
+ PR_DestroyCondVar(cv);
+ }
+#else /* defined(DEBUG) */
+ if (0 > PR_AtomicDecrement(&cv->notify_pending))
+ PR_DestroyCondVar(cv);
+#endif /* defined(DEBUG) */
+ }
+ 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)
+ {
+ rv = PTHREAD_MUTEX_INIT(lock->mutex, _pt_mattr);
+ PR_ASSERT(0 == rv);
+ }
+#if defined(DEBUG)
+ pt_debug.locks_created += 1;
+#endif
+ 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));
+ pt_debug.locks_destroyed += 1;
+#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);
+#if defined(DEBUG)
+ pt_debug.locks_acquired += 1;
+#endif
+} /* 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);
+
+#if defined(DEBUG)
+ pt_debug.locks_released += 1;
+#endif
+ 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 ... */
+ (void)GETTIMEOFDAY(&now);
+ /* 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 */
+ (void)PR_AtomicIncrement(&cvar->notify_pending);
+ 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;
+ cv->notify_pending = 0;
+#if defined(DEBUG)
+ pt_debug.cvars_created += 1;
+#endif
+ }
+ return cv;
+} /* PR_NewCondVar */
+
+PR_IMPLEMENT(void) PR_DestroyCondVar(PRCondVar *cvar)
+{
+ if (0 > PR_AtomicDecrement(&cvar->notify_pending))
+ {
+ PRIntn rv = pthread_cond_destroy(&cvar->cv); PR_ASSERT(0 == rv);
+#if defined(DEBUG)
+ memset(cvar, 0xaf, sizeof(PRCondVar));
+ pt_debug.cvars_destroyed += 1;
+#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 *mon;
+ PRCondVar *cvar;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ cvar = PR_NEWZAP(PRCondVar);
+ if (NULL == cvar)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+ mon = PR_NEWZAP(PRMonitor);
+ if (mon != NULL)
+ {
+ int rv;
+ rv = PTHREAD_MUTEX_INIT(mon->lock.mutex, _pt_mattr);
+ PR_ASSERT(0 == rv);
+
+ mon->cvar = cvar;
+ rv = PTHREAD_COND_INIT(mon->cvar->cv, _pt_cvar_attr);
+ PR_ASSERT(0 == rv);
+ mon->entryCount = 0;
+ mon->cvar->lock = &mon->lock;
+ if (0 != rv)
+ {
+ PR_DELETE(mon);
+ PR_DELETE(cvar);
+ mon = NULL;
+ }
+ }
+ return mon;
+} /* 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);
+ PR_DestroyCondVar(mon->cvar);
+ 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..b20f7c60
--- /dev/null
+++ b/pr/src/pthreads/ptthread.c
@@ -0,0 +1,1399 @@
+/* -*- 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 <unistd.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.
+ */
+
+static PRIntn pt_schedpriv = 0;
+extern PRLock *_pr_sleeplock;
+
+static struct _PT_Bookeeping
+{
+ PRLock *ml; /* a lock to protect ourselves */
+ PRCondVar *cv; /* used to signal global things */
+ PRInt32 system, user; /* a count of the two different types */
+ PRUintn this_many; /* number of threads allowed for exit */
+ pthread_key_t key; /* private private data key */
+ pthread_key_t highwater; /* ordinal value of next key to be allocated */
+ PRThread *first, *last; /* list of threads we know about */
+#if defined(_PR_DCETHREADS) || defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
+ PRInt32 minPrio, maxPrio; /* range of scheduling priorities */
+#endif
+} pt_book = {0};
+
+static void _pt_thread_death(void *arg);
+static void init_pthread_gc_support(void);
+
+#if defined(_PR_DCETHREADS) || defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
+static PRIntn pt_PriorityMap(PRThreadPriority pri)
+{
+ return pt_book.minPrio +
+ pri * (pt_book.maxPrio - pt_book.minPrio) / PR_PRIORITY_LAST;
+}
+#endif
+
+/*
+** 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);
+
+ /*
+ * Here we set the pthread's backpointer to the PRThread to NULL.
+ * Otherwise the desctructor would get called eagerly as the thread
+ * returns to the pthread runtime. The joining thread would them be
+ * the proud possessor of a dangling reference. However, this is the
+ * last chance to delete the object if the thread is detached, so
+ * just let the destuctor do the work.
+ */
+ if (PR_FALSE == detached)
+ {
+ rv = pthread_setspecific(pt_book.key, NULL);
+ PR_ASSERT(0 == rv);
+ }
+
+ return NULL;
+} /* _pt_root */
+
+static PRThread* pt_AttachThread(void)
+{
+ 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.
+ * We can't call any function that calls PR_GetCurrentThread()
+ * either (e.g., PR_SetError()) as that will result in infinite
+ * recursion.
+ */
+ if (!_pr_initialized) return NULL;
+
+ /* PR_NEWZAP must not call PR_GetCurrentThread() */
+ thred = PR_NEWZAP(PRThread);
+ if (NULL != thred)
+ {
+ int rv;
+
+ thred->priority = PR_PRIORITY_NORMAL;
+ thred->id = pthread_self();
+ rv = pthread_setspecific(pt_book.key, thred);
+ PR_ASSERT(0 == rv);
+
+ thred->state = PT_THREAD_GLOBAL | PT_THREAD_FOREIGN;
+ PR_Lock(pt_book.ml);
+
+ /* 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 */
+} /* pt_AttachThread */
+
+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(_POSIX_THREAD_PRIORITY_SCHEDULING)
+ struct sched_param schedule;
+#endif
+
+#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
+ 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(_POSIX_THREAD_PRIORITY_SCHEDULING)
+ 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) */
+
+#if defined(IRIX)
+ if ((32 * 1024) > stackSize) stackSize = (32 * 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 (NULL == thred)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, errno);
+ goto done;
+ }
+ else
+ {
+ 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) {
+ PRIntn oserr = errno;
+ PR_Free(thred); /* all that work ... poof! */
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, oserr);
+ 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 (PR_SYSTEM_THREAD == type)
+ 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.
+ */
+ rv = PTHREAD_CREATE(&id, tattr, _pt_root, thred);
+
+#if !defined(_PR_DCETHREADS)
+ if (EPERM == rv)
+ {
+ /* Remember that we don't have thread scheduling privilege. */
+ pt_schedpriv = EPERM;
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("_PR_CreateThread: no thread scheduling privilege"));
+ /* Try creating the thread again without setting priority. */
+ rv = pthread_attr_setinheritsched(&tattr, PTHREAD_INHERIT_SCHED);
+ PR_ASSERT(0 == rv);
+ rv = PTHREAD_CREATE(&id, tattr, _pt_root, thred);
+ }
+#endif
+
+ if (0 != rv)
+ {
+#if defined(_PR_DCETHREADS)
+ PRIntn oserr = errno;
+#else
+ PRIntn oserr = rv;
+#endif
+ 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_Free(thred->stack);
+ PR_Free(thred); /* all that work ... poof! */
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, oserr);
+ 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)
+{
+ return PR_GetCurrentThread();
+} /* 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 == (PT_THREAD_DETACHED & thred->state))
+ || (PT_THREAD_FOREIGN == (PT_THREAD_FOREIGN & 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_INVALID_ARGUMENT_ERROR, 0);
+ PR_LogPrint(
+ "PR_JoinThread: 0x%X not joinable | already smashed\n", thred);
+
+ PR_ASSERT(!"Illegal thread join attempt");
+ }
+ else
+ {
+ pthread_t id = thred->id;
+ rv = pthread_join(id, &result);
+ PR_ASSERT(rv == 0 && result == NULL);
+ if (0 == rv)
+ {
+ _pt_thread_death(thred);
+ }
+ else
+ {
+ PRErrorCode prerror;
+ switch (rv)
+ {
+ case EINVAL: /* not a joinable thread */
+ case ESRCH: /* no thread with given ID */
+ prerror = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ case EDEADLK: /* a thread joining with itself */
+ prerror = PR_DEADLOCK_ERROR;
+ break;
+ default:
+ prerror = PR_UNKNOWN_ERROR;
+ break;
+ }
+ PR_SetError(prerror, rv);
+ }
+ }
+ return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
+} /* PR_JoinThread */
+
+PR_IMPLEMENT(void) PR_DetachThread() { } /* PR_DetachThread */
+
+PR_IMPLEMENT(PRThread*) PR_GetCurrentThread()
+{
+ void *thred;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ PTHREAD_GETSPECIFIC(pt_book.key, thred);
+ if (NULL == thred) thred = pt_AttachThread();
+ PR_ASSERT(NULL != thred);
+ return (PRThread*)thred;
+} /* PR_GetCurrentThread */
+
+PR_IMPLEMENT(PRThreadScope) PR_GetThreadScope(const PRThread *thred)
+{
+ return PR_GLOBAL_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(_POSIX_THREAD_PRIORITY_SCHEDULING)
+ 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 || EPERM == rv);
+ if (EPERM == rv)
+ {
+ pt_schedpriv = EPERM;
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("PR_SetThreadPriority: no thread scheduling privilege"));
+ }
+ }
+#endif
+
+ thred->priority = newPri;
+} /* PR_SetThreadPriority */
+
+#if 0
+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 */
+#endif
+
+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.
+ **
+ ** I don't know why I thought this would work. Must have
+ ** been one of those weaker momements after I'd been
+ ** smelling the vapors.
+ **
+ ** Even with the followng changes it is possible that
+ ** the pointer to the condition variable is pointing
+ ** at a bogus value. Will the unerlying code detect
+ ** that?
+ */
+ PRCondVar *cv;
+ PR_ASSERT(NULL != thred);
+ if (NULL == thred) return PR_FAILURE;
+
+ thred->state |= PT_THREAD_ABORTED;
+
+ cv = thred->waiting;
+ if (NULL != cv)
+ {
+ PRIntn rv = pthread_cond_broadcast(&cv->cv);
+ PR_ASSERT(0 == rv);
+ }
+ 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;
+ PRIntervalTime timein;
+
+ timein = PR_IntervalNow();
+ cv = PR_NewCondVar(_pr_sleeplock);
+ PR_ASSERT(cv != NULL);
+ PR_Lock(_pr_sleeplock);
+ do
+ {
+ PRIntervalTime now = PR_IntervalNow();
+ PRIntervalTime delta = now - timein;
+ if (delta > ticks) break;
+ rv = PR_WaitCondVar(cv, ticks - delta);
+ } while (PR_SUCCESS == rv);
+ PR_Unlock(_pr_sleeplock);
+ PR_DestroyCondVar(cv);
+ }
+ return rv;
+} /* PR_Sleep */
+
+static void _pt_thread_death(void *arg)
+{
+ PRThread *thred = (PRThread*)arg;
+
+ if (thred->state & PT_THREAD_FOREIGN)
+ {
+ PR_Lock(pt_book.ml);
+ 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);
+ }
+ _PR_DestroyThreadPrivate(thred);
+ if (NULL != thred->errorString)
+ PR_Free(thred->errorString);
+ if (NULL != thred->io_cv)
+ PR_DestroyCondVar(thred->io_cv);
+ PR_Free(thred->stack);
+#if defined(DEBUG)
+ memset(thred, 0xaf, sizeof(PRThread));
+#endif /* defined(DEBUG) */
+ PR_Free(thred);
+} /* _pt_thread_death */
+
+void _PR_InitThreads(
+ PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs)
+{
+ int rv;
+ PRThread *thred;
+
+#if defined(_PR_DCETHREADS) || defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
+ /*
+ ** These might be function evaluations
+ */
+ pt_book.minPrio = PT_PRIO_MIN;
+ pt_book.maxPrio = PT_PRIO_MAX;
+#endif
+
+ 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.
+ * NBB: Oh really? I'm going to give it a spin - AOF 19 June 1998.
+ * More info - the problem is that pthreads calls the destructor
+ * eagerly as the thread returns from its root, rather than lazily
+ * after the thread is joined. Therefore, threads that are joining
+ * and holding PRThread references are actually holding pointers to
+ * nothing.
+ */
+ rv = PTHREAD_KEY_CREATE(&pt_book.key, _pt_thread_death);
+ PR_ASSERT(0 == rv);
+ rv = pthread_setspecific(pt_book.key, thred);
+ PR_ASSERT(0 == rv);
+ 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(LINUX) && !defined(FREEBSD) && !defined(NETBSD) && !defined(OPENBSD)
+ 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);
+
+ _PR_LogCleanup();
+ /* Close all the fd's before calling _PR_CleanupFdCache */
+ _PR_CleanupFdCache();
+
+ /*
+ * 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;
+ }
+ _pt_thread_death(me);
+ _pr_initialized = PR_FALSE;
+ return PR_SUCCESS;
+ }
+ return PR_FAILURE;
+} /* PR_Cleanup */
+
+PR_IMPLEMENT(void) PR_ProcessExit(PRIntn status)
+{
+ _exit(status);
+}
+
+PR_IMPLEMENT(PRUint32) PR_GetThreadID(PRThread *thred)
+{
+#if defined(_PR_DCETHREADS)
+ return (PRUint32)&thred->id; /* this is really a sham! */
+#else
+ return (PRUint32)thred->id; /* and I don't know what they will do with it */
+#endif
+}
+
+/*
+ * $$$
+ * 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) && !defined(NETBSD) && !defined(OPENBSD) /*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;
+
+#ifdef DEBUG
+ suspendAllOn = PR_TRUE;
+#endif
+ 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);
+#ifdef DEBUG
+ suspendAllOn = PR_FALSE;
+#endif
+
+ 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..755d9928
--- /dev/null
+++ b/pr/src/threads/Makefile
@@ -0,0 +1,61 @@
+#
+# 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 \
+ prtpd.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..e50e9702
--- /dev/null
+++ b/pr/src/threads/combined/prucpu.c
@@ -0,0 +1,308 @@
+/* -*- 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 = NULL;
+
+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->cpu = cpu;
+
+ 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;
+}
+
+#if !defined(_PR_GLOBAL_THREADS_ONLY) && !defined(_PR_LOCAL_THREADS_ONLY)
+/*
+** 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);
+ }
+}
+#endif
+
+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_MD_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..41eb62f2
--- /dev/null
+++ b/pr/src/threads/combined/prucv.c
@@ -0,0 +1,633 @@
+/* -*- 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 _PR_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)
+{
+ PRIntn 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 (_PR_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_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 _PR_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));
+ _PR_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));
+ _PR_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..2ac56c57
--- /dev/null
+++ b/pr/src/threads/combined/prulock.c
@@ -0,0 +1,428 @@
+/* -*- 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.
+ */
+ q = lock->waitQ.next;
+ if (q == &lock->waitQ || _PR_THREAD_CONDQ_PTR(q)->priority ==
+ _PR_THREAD_CONDQ_PTR(lock->waitQ.prev)->priority) {
+ /*
+ * If all the threads in the lock waitQ have the same priority,
+ * then avoid scanning the list: insert the element at the end.
+ */
+ q = &lock->waitQ;
+ } else {
+ /* Sort thread into lock's waitQ at appropriate point */
+ /* 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..4e0053f6
--- /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 = (char*)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..bcc2e650
--- /dev/null
+++ b/pr/src/threads/combined/pruthr.c
@@ -0,0 +1,1818 @@
+/* -*- 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;
+PRInt32 _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);
+static void _PR_UserRunThread(void);
+
+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_DestroyThread(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_DestroyThread(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_MD_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);
+ }
+}
+
+static 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_MD_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) {
+ _PRCPU *cpu = thread->cpu;
+
+ 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);
+
+}
+
+#if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX)
+static PRThread *get_thread(_PRCPU *cpu, PRBool *wakeup_cpus)
+{
+ 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
+ * Waking up all CPU's need happen only once.
+ */
+
+ *wakeup_cpus = PR_TRUE;
+ continue;
+ } else if (thread->flags & _PR_BOUND_THREAD) {
+ /*
+ * Thread bound to cpu 0
+ */
+
+ thread = NULL;
+#ifdef IRIX
+ _PR_MD_WAKEUP_PRIMORDIAL_CPU();
+#endif
+ 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. This is because
+ * the cpu's ioq is accessed without lock protection and scheduling
+ * the thread on a different cpu would preclude this optimization.
+ */
+ 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);
+}
+#endif /* !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX) */
+
+/*
+** 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 = _PR_MD_CURRENT_CPU();
+ PRIntn pri;
+ PRUint32 r;
+ PRCList *qp;
+ PRIntn priMin, priMax;
+#if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX)
+ PRBool wakeup_cpus;
+#endif
+
+ /* 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)
+
+ wakeup_cpus = PR_FALSE;
+ _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), &wakeup_cpus))
+ != NULL) {
+ thread->cpu = cpu;
+ _PR_CPU_LIST_UNLOCK();
+ if (wakeup_cpus == PR_TRUE)
+ _PR_MD_WAKEUP_CPUS();
+ goto found_thread;
+ }
+ }
+ }
+ _PR_CPU_LIST_UNLOCK();
+ if (wakeup_cpus == PR_TRUE)
+ _PR_MD_WAKEUP_CPUS();
+
+#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
+ */
+ PR_ASSERT(thread->cpu == _PR_MD_CURRENT_CPU());
+ 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_DestroyThread(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;
+ thread->flags = (flags | _PR_GLOBAL_SCOPE);
+ if (type == PR_SYSTEM_THREAD)
+ {
+ thread->flags |= _PR_SYSTEM;
+ PR_AtomicIncrement(&_pr_systemActive);
+ }
+ else PR_AtomicIncrement(&_pr_userActive);
+
+ 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;
+ }
+
+ /* 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
+ stack->thr = thread;
+ 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 */
+ 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.
+*/
+PRThread* _PRI_AttachThread(PRThreadType type,
+ PRThreadPriority priority, PRThreadStack *stack, PRUint32 flags)
+{
+ PRThread *thread;
+
+ if ((thread = _PR_MD_GET_ATTACHED_THREAD()) != NULL) {
+ return thread;
+ }
+ _PR_MD_SET_CURRENT_THREAD(NULL);
+
+ /* 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_DestroyThread(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_DestroyThread(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 PR_GetCurrentThread();
+}
+
+PR_IMPLEMENT(void) PR_DetachThread(void)
+{
+}
+
+void _PRI_DetachThread(void)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (me->flags & _PR_PRIMORDIAL) {
+ /*
+ * ignore, if primordial thread
+ */
+ return;
+ }
+ PR_ASSERT(me->flags & _PR_ATTACHED);
+ PR_ASSERT(_PR_IS_NATIVE_THREAD(me));
+ _PR_CleanupThread(me);
+ PR_DELETE(me->privateData);
+
+ _PR_DecrActiveThreadCount(me);
+
+ _PR_MD_CLEAN_THREAD(me);
+ _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) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ 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;
+ 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..1d9fd809
--- /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"
+
+extern PRLock *_pr_sleeplock; /* allocated and initialized in prinit */
+/*
+** 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)
+{
+ PRStatus rv = PR_SUCCESS;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ 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.
+ */
+ PRCondVar *cv;
+ PRIntervalTime timein;
+
+ timein = PR_IntervalNow();
+ cv = PR_NewCondVar(_pr_sleeplock);
+ PR_ASSERT(cv != NULL);
+ PR_Lock(_pr_sleeplock);
+ do
+ {
+ PRIntervalTime delta = PR_IntervalNow() - timein;
+ if (delta > timeout) break;
+ rv = PR_WaitCondVar(cv, timeout - delta);
+ } while (rv == PR_SUCCESS);
+ PR_Unlock(_pr_sleeplock);
+ 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)
+{
+ /* $$$$ not sure how to finese this one */
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+}
+
+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..de8939c3
--- /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));
+}
+
+PR_IMPLEMENT(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..54ce8027
--- /dev/null
+++ b/pr/src/threads/prtpd.c
@@ -0,0 +1,284 @@
+/* -*- 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 PRInt32 _pr_tpd_length = 0; /* current length of destructor vector */
+static PRInt32 _pr_tpd_highwater = 0; /* next TPD key to be assigned */
+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 */
+
+/*
+** Initialize the thread private data manipulation
+*/
+void _PR_InitTPD()
+{
+ _pr_tpd_destructors = (PRThreadPrivateDTOR*)
+ PR_CALLOC(_PR_TPD_LIMIT * sizeof(PRThreadPrivateDTOR*));
+ PR_ASSERT(NULL != _pr_tpd_destructors);
+ _pr_tpd_length = _PR_TPD_LIMIT;
+}
+
+/*
+** Clean up the thread private data manipulation
+*/
+void _PR_CleanupTPD(void)
+{
+} /* _PR_CleanupTPD */
+
+/*
+** 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;
+ PRInt32 index;
+
+ if (!_pr_initialized) _PR_ImplicitInitialization();
+
+ PR_ASSERT(NULL != newIndex);
+ PR_ASSERT(NULL != _pr_tpd_destructors);
+
+ index = PR_AtomicIncrement(&_pr_tpd_highwater); /* allocate index */
+ if (_PR_TPD_LIMIT < index)
+ {
+ PR_SetError(PR_TPD_RANGE_ERROR, 0);
+ rv = PR_FAILURE; /* that's just wrong */
+ }
+ else
+ {
+ _pr_tpd_destructors[index] = dtor; /* record destructor @index */
+ *newIndex = (PRIntn)index; /* copy into client's location */
+ rv = PR_SUCCESS; /* that's okay */
+ }
+
+ 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 his, void *priv)
+{
+ PRStatus rv = PR_SUCCESS;
+ PRInt32 index = (PRInt32)his;
+ 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_SetError(PR_TPD_RANGE_ERROR, 0);
+ rv = PR_FAILURE;
+ }
+ else
+ {
+ 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 = (void**)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_tpd_destructors[index])(data);
+ }
+
+ /*
+ ** 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;
+ }
+
+ 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(PRUintn his)
+{
+ PRInt32 index = (PRInt32)his;
+ PRThread *self = PR_GetCurrentThread();
+ void *tpd = ((NULL == self->privateData) || (index >= self->tpdLength)) ?
+ NULL : self->privateData[index];
+
+ return tpd;
+}
+
+/*
+** Destroy the thread's private data, if any exists. This is called at
+** thread termination time only. There should be no threading issues
+** since this is being called by the thread itself.
+*/
+void _PR_DestroyThreadPrivate(PRThread* self)
+{
+ if (NULL != self->privateData) /* we have some */
+ {
+ PRBool clean = PR_TRUE;
+ PRInt32 index, passes = 4;
+ PR_ASSERT(0 != self->tpdLength);
+ do
+ {
+ for (index = 0; index < self->tpdLength; ++index)
+ {
+ void *priv = self->privateData[index]; /* extract */
+ if (NULL != priv) /* we have data at this index */
+ {
+ if (NULL != _pr_tpd_destructors[index])
+ {
+ self->privateData[index] = NULL; /* precondition */
+ (*_pr_tpd_destructors[index])(priv); /* destroy */
+ clean = PR_FALSE; /* unknown side effects */
+ }
+ }
+ }
+ } while ((passes-- > 0) && !clean); /* limit # of passes */
+ PR_DELETE(self->privateData); /* that's just this thread's vector */
+ }
+} /* _PR_DestroyThreadPrivate */
+
+#if 0
+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;
+}
+
+#endif
diff --git a/pr/tests/Makefile b/pr/tests/Makefile
new file mode 100644
index 00000000..a004d214
--- /dev/null
+++ b/pr/tests/Makefile
@@ -0,0 +1,481 @@
+#
+# 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)
+OS_CFLAGS = $(OS_EXE_CFLAGS)
+W16STDIO = $(MOD_DEPTH)/pr/src/md/windows/$(OBJDIR)/w16stdio.$(OBJ_SUFFIX)
+endif
+
+ifeq ($(OS_TARGET),WIN16)
+DIRS = dll w16gui
+else
+DIRS = dll
+endif
+
+ifeq ($(OS_TARGET),OS2)
+OS_CFLAGS = $(OS_EXE_CFLAGS)
+endif
+
+CSRCS = \
+ accept.c \
+ alarm.c \
+ atomic.c \
+ attach.c \
+ bigfile.c \
+ cleanup.c \
+ cltsrv.c \
+ concur.c \
+ cvar.c \
+ cvar2.c \
+ dceemu.c \
+ dlltest.c \
+ dtoa.c \
+ exit.c \
+ fileio.c \
+ foreign.c \
+ forktest.c \
+ fsync.c \
+ getproto.c \
+ i2l.c \
+ initclk.c \
+ inrval.c \
+ instrumt.c \
+ intrupt.c \
+ io_timeout.c \
+ ioconthr.c \
+ ipv6.c \
+ join.c \
+ joinkk.c \
+ joinku.c \
+ joinuk.c \
+ joinuu.c \
+ layer.c \
+ lazyinit.c \
+ lltest.c \
+ lock.c \
+ lockfile.c \
+ logger.c \
+ multiwait.c \
+ many_cv.c \
+ nbconn.c \
+ nblayer.c \
+ nonblock.c \
+ op_2long.c \
+ op_filnf.c \
+ op_filok.c \
+ op_noacc.c \
+ op_nofil.c \
+ parent.c \
+ perf.c \
+ poll_er.c \
+ poll_nm.c \
+ poll_to.c \
+ pollable.c \
+ prftest1.c \
+ prftest2.c \
+ priotest.c \
+ provider.c \
+ ranfile.c \
+ sel_spd.c \
+ selct_er.c \
+ selct_nm.c \
+ selct_to.c \
+ select2.c \
+ sem.c \
+ servr_kk.c \
+ servr_ku.c \
+ servr_uk.c \
+ short_thread.c \
+ sigpipe.c \
+ socket.c \
+ sockopt.c \
+ sprintf.c \
+ sproc_ch.c \
+ sproc_p.c \
+ stack.c \
+ stdio.c \
+ strod.c \
+ suspend.c \
+ switch.c \
+ system.c \
+ testbit.c \
+ testfile.c \
+ threads.c \
+ thruput.c \
+ timemac.c \
+ timetest.c \
+ tmoacc.c \
+ tmocon.c \
+ tpd.c \
+ version.c \
+ udpsrv.c \
+ writev.c \
+ xnotify.c \
+ $(NULL)
+
+ifeq ($(OS_ARCH), WINNT)
+PROG_SUFFIX = .exe
+else
+PROG_SUFFIX =
+endif
+
+PROGS = $(addprefix $(OBJDIR)/, $(CSRCS:.c=$(PROG_SUFFIX)))
+
+TARGETS = $(PROGS)
+
+INCLUDES = -I$(DIST)/include/obsolete -I$(DIST)/include/private -I$(DIST)/include
+
+# Setting the variables LDOPTS and LIBPR. We first initialize
+# them to the default values, then adjust them for some platforms.
+LDOPTS = -L$(DIST)/lib
+LIBPR = -lnspr$(MOD_VERSION)
+LIBPLC = -lplc$(MOD_VERSION)
+
+ifeq ($(OS_ARCH), WINNT)
+ifeq ($(OS_TARGET), WIN16)
+ LIBPR = $(DIST)/lib/nspr$(MOD_VERSION).lib
+ LIBPLC= $(DIST)/lib/plc$(MOD_VERSION).lib
+else
+ifeq ($(OS_TARGET), OS2)
+ LDOPTS = -NOE -DEBUG -nologo -PMTYPE:VIO
+ LIBPR = $(DIST)/lib/nspr$(MOD_VERSION).lib
+ LIBPLC= $(DIST)/lib/plc$(MOD_VERSION).lib
+else
+ LDOPTS = -NOLOGO -DEBUG -DEBUGTYPE:CV -INCREMENTAL:NO
+ LIBPR = $(DIST)/lib/libnspr$(MOD_VERSION).$(LIB_SUFFIX)
+ LIBPLC= $(DIST)/lib/libplc$(MOD_VERSION).$(LIB_SUFFIX)
+endif
+endif
+endif
+
+ifneq ($(OS_ARCH), WINNT)
+PWD = $(shell pwd)
+endif
+
+ifeq ($(OS_ARCH), IRIX)
+ ifeq ($(USE_CPLUS), 1)
+ CC = CC
+ endif
+ LDOPTS += -rpath $(PWD)/$(DIST)/lib -rdata_shared
+# For 6.x machines, include this flag
+ ifeq ($(basename $(OS_RELEASE)),6)
+ ifeq ($(USE_N32),1)
+ LDOPTS += -n32
+ else
+ LDOPTS += -32
+ endif
+
+ ifeq ($(USE_PTHREADS), 1)
+ ifeq ($(OS_RELEASE), 6.2)
+ LDOPTS += -Wl,-woff,85
+ endif
+ endif
+ endif
+endif
+
+ifeq ($(OS_ARCH), OSF1)
+ ifeq ($(USE_CPLUS), 1)
+ CC = cxx
+ endif
+# I haven't figured out how to pass -rpath to cc on OSF1 V3.2, so
+# we do static linking.
+ ifeq ($(OS_RELEASE), V3.2)
+ LIBPR = $(DIST)/lib/libnspr$(MOD_VERSION).a
+ LIBPLC = $(DIST)/lib/libplc$(MOD_VERSION).a
+ EXTRA_LIBS = -lc_r
+ else
+ LDOPTS += -rpath $(PWD)/$(DIST)/lib
+ endif
+endif
+
+ifeq ($(OS_ARCH), HP-UX)
+ LDOPTS += -z -Wl,+s,+b,$(PWD)/$(DIST)/lib
+ ifeq ($(USE_64),1)
+ LDOPTS += -L/usr/lib/pa20_64 -lpthread +DD64
+ endif
+endif
+
+# AIX
+ifeq ($(OS_ARCH),AIX)
+ ifeq ($(CLASSIC_NSPR),1)
+ LDOPTS += -blibpath:.:$(PWD)/$(DIST)/lib:/usr/lpp/xlC/lib:/usr/lib:/lib
+ else
+ LDOPTS += -blibpath:.:$(PWD)/$(DIST)/lib:/usr/lib/threads:/usr/lpp/xlC/lib:/usr/lib:/lib
+ endif
+ ifeq ($(OS_ARCH)$(OS_RELEASE),AIX4.1)
+ LIBPR = -lnspr$(MOD_VERSION)_shr
+ LIBPLC = -lplc$(MOD_VERSION)_shr
+ else
+ LDOPTS += -brtl
+ EXTRA_LIBS = -ldl
+ endif
+endif
+
+# Solaris
+ifeq ($(OS_ARCH), SunOS)
+ ifneq ($(OS_RELEASE), 4.1.3_U1)
+ ifdef NS_USE_GCC
+ LDOPTS += -Xlinker -R -Xlinker $(PWD)/$(DIST)/lib
+ else
+ ifeq ($(USE_CPLUS), 1)
+ CC = CC
+ endif
+ LDOPTS += -R $(PWD)/$(DIST)/lib
+ endif
+ endif
+
+ ifneq ($(LOCAL_THREADS_ONLY),1)
+# SunOS 5.4 and 5.5 need to link with -lthread or -lpthread,
+# even though we already linked with these system libraries
+# when we built libnspr.so.
+ ifeq ($(OS_RELEASE), 5.4)
+ EXTRA_LIBS = -lthread
+ endif
+
+ ifeq ($(OS_RELEASE), 5.5)
+ ifdef USE_PTHREADS
+ EXTRA_LIBS = -lpthread
+ else
+ EXTRA_LIBS = -lthread
+ endif
+ endif
+ endif # LOCAL_THREADS_ONLY
+endif # SunOS
+
+ifeq ($(OS_ARCH), NEC)
+ EXTRA_LIBS = $(OS_LIBS)
+# This hardcodes in the executable programs the directory to find
+# libnspr.so etc. at program startup. Equivalent to the -R or -rpath
+# option for ld on other platforms.
+ export LD_RUN_PATH = $(PWD)/$(DIST)/lib
+endif
+
+ifeq ($(OS_ARCH), NCR)
+# NCR needs to link against -lsocket -lnsl -ldl (and -lc, which is
+# linked implicitly by $(CC)). Note that we did not link with these
+# system libraries when we built libnspr.so.
+ EXTRA_LIBS = -lsocket -lnsl -ldl
+# This hardcodes in the executable programs the directory to find
+# libnspr.so etc. at program startup. Equivalent to the -R or -rpath
+# option for ld on other platforms.
+ export LD_RUN_PATH = $(PWD)/$(DIST)/lib
+endif
+
+ifeq ($(OS_ARCH), NEWS-OS)
+# This hardcodes in the executable programs the directory to find
+# libnspr.so etc. at program startup. Equivalent to the -R or -rpath
+# option for ld on other platforms.
+#export LD_RUN_PATH = $(PWD)/$(DIST)/lib
+ LIBPR = $(DIST)/lib/libnspr$(MOD_VERSION).a
+ LIBPLC = $(DIST)/lib/libplc$(MOD_VERSION).a
+ EXTRA_LIBS = -lsocket -lnsl -lgen -lresolv
+endif
+
+ifeq ($(OS_ARCH), Linux)
+ ifeq ($(OS_RELEASE), 1.2)
+ EXTRA_LIBS = -ldl
+ else
+ LDOPTS += -Xlinker -rpath $(PWD)/$(DIST)/lib
+ ifeq ($(USE_PTHREADS),1)
+ EXTRA_LIBS = -lpthread
+ endif
+ endif
+endif
+
+ifeq ($(OS_ARCH), SCOOS)
+# SCO Unix needs to link against -lsocket again even though we
+# already linked with these system libraries when we built libnspr.so.
+EXTRA_LIBS = -lsocket
+# This hardcodes in the executable programs the directory to find
+# libnspr.so etc. at program startup. Equivalent to the -R or -rpath
+# option for ld on other platforms.
+export LD_RUN_PATH = $(PWD)/$(DIST)/lib
+endif
+
+ifeq ($(OS_ARCH),SINIX)
+EXTRA_LIBS = -lsocket -lnsl -lresolv -ldl
+# This hardcodes in the executable programs the directory to find
+# libnspr.so etc. at program startup. Equivalent to the -R or -rpath
+# option for ld on other platforms.
+export LD_RUN_PATH = $(PWD)/$(DIST)/lib
+endif
+
+ifeq ($(OS_ARCH), UNIXWARE)
+export LD_RUN_PATH = $(PWD)/$(DIST)/lib
+endif
+
+ifeq ($(OS_ARCH),FreeBSD)
+ifeq ($(USE_PTHREADS),1)
+EXTRA_LIBS = -lc_r
+endif
+LDOPTS += -Xlinker -R $(PWD)/$(DIST)/lib
+endif
+
+ifeq ($(OS_ARCH),BSD_OS)
+EXTRA_LIBS = -ldl
+endif
+
+ifeq ($(USE_PTHREADS),1)
+ifeq ($(OS_ARCH),AIX)
+LIBPTHREAD = -lpthreads
+else
+ ifeq ($(OS_ARCH),FreeBSD)
+ LIBPTHREAD = -lc_r
+ else
+ ifeq ($(OS_ARCH)$(basename $(OS_RELEASE)),HP-UXB.10)
+ LIBPTHREAD = -ldce
+ else
+ LIBPTHREAD = -lpthread
+ endif
+ endif
+endif
+endif
+
+#####################################################
+#
+# The rules
+#
+#####################################################
+
+include $(MOD_DEPTH)/config/rules.mk
+
+AIX_PRE_4_2 = 0
+ifeq ($(OS_ARCH),AIX)
+ifeq ($(OS_RELEASE),4.1)
+ifneq ($(USE_PTHREADS), 1)
+#AIX_PRE_4_2 = 1
+endif
+endif
+endif
+
+ifeq ($(AIX_PRE_4_2),1)
+
+# AIX releases prior to 4.2 need a special two-step linking hack
+# in order to both override the system select() and be able to
+# get at the original system select().
+#
+# We use a pattern rule in ns/nspr20/config/rules.mk to generate
+# the .$(OBJ_SUFFIX) file from the .c source file, then do the
+# two-step linking hack below.
+
+$(OBJDIR)/%: $(OBJDIR)/%.$(OBJ_SUFFIX)
+ @$(MAKE_OBJDIR)
+ rm -f $@ $(AIX_TMP)
+ $(CC) $(AIX_LINK_OPTS) -o $(AIX_TMP) $< $(DIST)/lib/libnspr$(MOD_VERSION).a
+ $(CC) -o $@ $(AIX_TMP) $(AIX_WRAP)
+ rm -f $(AIX_TMP)
+
+else
+
+# All platforms that are not AIX pre-4.2.
+
+
+ifeq ($(USE_PTHREADS), 1)
+$(OBJDIR)/attach: $(OBJDIR)/attach.$(OBJ_SUFFIX)
+ @$(MAKE_OBJDIR)
+ $(CC) $< $(LDOPTS) $(LIBPLC) $(LIBPR) $(LIBPTHREAD) -o $@
+endif
+
+
+$(OBJDIR)/%$(PROG_SUFFIX): $(OBJDIR)/%.$(OBJ_SUFFIX)
+ @$(MAKE_OBJDIR)
+ifeq ($(OS_ARCH), WINNT)
+ifeq ($(OS_TARGET),WIN16)
+ echo system windows >w16link
+ echo name $@ >>w16link
+ echo option map >>w16link
+# echo option CASEEXACT >>w16link
+ echo option stack=16K >>w16link
+ echo debug $(DEBUGTYPE) all >>w16link
+ echo file >>w16link
+ echo $< , >>w16link
+ echo $(W16STDIO) >>w16link
+ echo library >>w16link
+ echo $(LIBPR), >>w16link
+ echo $(LIBPLC), >>w16link
+ echo winsock.lib >>w16link
+ wlink @w16link.
+else
+ifeq ($(OS_TARGET),OS2)
+ $(LINK) $(LDOPTS) $< $(LIBPLC) $(LIBPR) so32dll.lib tcp32dll.lib -MAP:$(@:.exe=.map) -out:$@
+else
+ link $(LDOPTS) $< $(LIBPLC) $(LIBPR) wsock32.lib -out:$@
+endif
+endif
+else
+ $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBPR) $(EXTRA_LIBS) -o $@
+endif
+
+endif
+
+export:: $(TARGETS)
+install:: export
+clean::
+ rm -f $(TARGETS)
+
+ifeq ($(USE_PTHREADS), 1)
+$(OBJDIR)/foreign: $(OBJDIR)/foreign.o
+ $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBPR) $(EXTRA_LIBS) $(LIBPTHREAD) -o $@
+$(OBJDIR)/provider: $(OBJDIR)/provider.o
+ $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBPR) $(EXTRA_LIBS) $(LIBPTHREAD) -o $@
+endif
+
+#
+# Run the test programs with no arguments
+#
+# Test output goes to the file pointed to by the environment variable
+# NSPR_TEST_LOGFILE, if set, else to /dev/null
+#
+ECHO = echo
+PROGRAMS = $(notdir $(PROGS))
+ifdef NSPR_TEST_LOGFILE
+LOGFILE = $(NSPR_TEST_LOGFILE)
+else
+ifeq ($(OS_ARCH), WINNT)
+LOGFILE = nul
+else
+LOGFILE = /dev/null
+endif
+endif
+
+ifeq ($(OS_TARGET),Linux)
+ECHO = /bin/echo
+endif
+
+ALWAYS:
+
+runtests:: $(PROGS) ALWAYS
+ @$(ECHO) "\nNSPR Test Results - $(OBJDIR)\n"
+ @$(ECHO) "BEGIN\t\t\t`date`"
+ @$(ECHO) "NSPR_TEST_LOGFILE\t$(LOGFILE)\n"
+ @$(ECHO) "Test\t\t\tResult\n"
+ @cd $(OBJDIR); for i in $(PROGRAMS); do \
+ $(ECHO) "$$i\c"; \
+ ./$$i >> $(LOGFILE) 2>&1 ; \
+ if [ 0 = $$? ] ; then \
+ $(ECHO) "\t\t\tPassed"; \
+ else \
+ $(ECHO) "\t\t\tFAILED"; \
+ fi; \
+ done
+ @$(ECHO) "\nEND\t\t`date`\n"
diff --git a/pr/tests/accept.c b/pr/tests/accept.c
new file mode 100644
index 00000000..69be2c01
--- /dev/null
+++ b/pr/tests/accept.c
@@ -0,0 +1,505 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+** 1996 - Netscape Communications Corporation
+**
+** Name: accept.c
+**
+** Description: Run accept() sucessful connection tests.
+**
+** Modification History:
+** 04-Jun-97 AGarcia - Reconvert test file to return a 0 for PASS and a 1 for FAIL
+** 13-May-97 AGarcia- Converted the test to accomodate the debug_mode
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+** 12-June-97 Revert to return code 0 and 1.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+
+#include "nspr.h"
+#include "prpriv.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "plgetopt.h"
+#include "plerror.h"
+
+#define BASE_PORT 10000
+
+#define CLIENT_DATA 128
+
+#define ACCEPT_NORMAL 0x1
+#define ACCEPT_FAST 0x2
+#define ACCEPT_READ 0x3
+#define ACCEPT_READ_FAST 0x4
+#define ACCEPT_READ_FAST_CB 0x5
+
+#define CLIENT_NORMAL 0x1
+#define CLIENT_TIMEOUT_ACCEPT 0x2
+#define CLIENT_TIMEOUT_SEND 0x3
+
+#define SERVER_MAX_BIND_COUNT 100
+
+#if defined(XP_MAC) || defined(XP_OS2)
+#define TIMEOUTSECS 10
+#else
+#define TIMEOUTSECS 2
+#endif
+PRIntervalTime timeoutTime;
+
+static PRInt32 count = 1;
+static PRFileDesc *output;
+static PRNetAddr serverAddr;
+static PRThreadScope thread_scope = PR_LOCAL_THREAD;
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+void Test_Assert(const char *msg, const char *file, PRIntn line)
+{
+ failed_already=1;
+ if (debug_mode) {
+ PR_fprintf(output, "@%s:%d ", file, line);
+ PR_fprintf(output, msg);
+ }
+} /* Test_Assert */
+
+#define TEST_ASSERT(expr) \
+ if (!(expr)) Test_Assert(#expr, __FILE__, __LINE__)
+
+#ifdef WINNT
+#define CALLBACK_MAGIC 0x12345678
+
+void timeout_callback(void *magic)
+{
+ TEST_ASSERT(magic == (void *)CALLBACK_MAGIC);
+ if (debug_mode)
+ PR_fprintf(output, "timeout callback called okay\n");
+}
+#endif
+
+
+static void PR_CALLBACK
+ClientThread(void *_action)
+{
+ PRInt32 action = * (PRInt32 *) _action;
+ PRInt32 iterations = count;
+ PRFileDesc *sock = NULL;
+
+ serverAddr.inet.family = PR_AF_INET;
+ serverAddr.inet.port = PR_htons(BASE_PORT);
+ serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+
+ for (; iterations--;) {
+ PRInt32 rv;
+ char buf[CLIENT_DATA];
+
+ sock = PR_NewTCPSocket();
+ if (!sock) {
+ if (!debug_mode)
+ failed_already=1;
+ else
+ PR_fprintf(output, "client: unable to create socket\n");
+ return;
+ }
+
+ if (action != CLIENT_TIMEOUT_ACCEPT) {
+
+ if ((rv = PR_Connect(sock, &serverAddr,
+ timeoutTime)) < 0) {
+ if (!debug_mode)
+ failed_already=1;
+ else
+ PR_fprintf(output,
+ "client: unable to connect to server (%ld, %ld, %ld, %ld)\n",
+ iterations, rv, PR_GetError(), PR_GetOSError());
+ goto ErrorExit;
+ }
+
+ if (action != CLIENT_TIMEOUT_SEND) {
+ if ((rv = PR_Send(sock, buf, CLIENT_DATA,
+ 0, timeoutTime))< 0) {
+ if (!debug_mode)
+ failed_already=1;
+ else
+ PR_fprintf(output,
+ "client: unable to send to server (%d, %ld, %ld)\n",
+ CLIENT_DATA, rv, PR_GetError());
+ goto ErrorExit;
+ }
+ } else {
+ PR_Sleep(PR_SecondsToInterval(TIMEOUTSECS + 1));
+ }
+ } else {
+ PR_Sleep(PR_SecondsToInterval(TIMEOUTSECS + 1));
+ }
+ if (debug_mode)
+ PR_fprintf(output, ".");
+ PR_Close(sock);
+ sock = NULL;
+ }
+ if (debug_mode)
+ PR_fprintf(output, "\n");
+
+ErrorExit:
+ if (sock != NULL)
+ PR_Close(sock);
+}
+
+
+static PRInt32 clientCommand;
+static PRInt32 iterations;
+static PRStatus rv;
+static PRFileDesc *listenSock;
+static PRFileDesc *clientSock = NULL;
+static PRNetAddr listenAddr;
+static PRNetAddr clientAddr;
+static PRThread *clientThread;
+static PRNetAddr *raddr;
+static char buf[4096 + 64];
+static PRInt32 status;
+static PRInt32 bytesRead;
+
+static void
+RunTest(PRInt32 acceptType, PRInt32 clientAction)
+{
+int i;
+
+ /* First bind to the socket */
+ listenSock = PR_NewTCPSocket();
+ if (!listenSock) {
+ failed_already=1;
+ if (debug_mode)
+ PR_fprintf(output, "unable to create listen socket\n");
+ return;
+ }
+ memset(&listenAddr, 0 , sizeof(listenAddr));
+ listenAddr.inet.family = PR_AF_INET;
+ listenAddr.inet.port = PR_htons(BASE_PORT);
+ listenAddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ /*
+ * try a few times to bind server's address, if addresses are in
+ * use
+ */
+ i = 0;
+ while (PR_Bind(listenSock, &listenAddr) == PR_FAILURE) {
+ if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) {
+ listenAddr.inet.port += 2;
+ if (i++ < SERVER_MAX_BIND_COUNT)
+ continue;
+ }
+ failed_already=1;
+ if (debug_mode)
+ PR_fprintf(output,"accept: ERROR - PR_Bind failed\n");
+ return;
+ }
+
+
+ rv = PR_Listen(listenSock, 100);
+ if (rv == PR_FAILURE) {
+ failed_already=1;
+ if (debug_mode)
+ PR_fprintf(output, "unable to listen\n");
+ return;
+ }
+
+ clientCommand = clientAction;
+ clientThread = PR_CreateThread(PR_USER_THREAD, ClientThread,
+ (void *)&clientCommand, PR_PRIORITY_NORMAL, thread_scope,
+ PR_JOINABLE_THREAD, 0);
+ if (!clientThread) {
+ failed_already=1;
+ if (debug_mode)
+ PR_fprintf(output, "error creating client thread\n");
+ return;
+ }
+
+ iterations = count;
+ for (;iterations--;) {
+ switch (acceptType) {
+ case ACCEPT_NORMAL:
+ clientSock = PR_Accept(listenSock, &clientAddr,
+ timeoutTime);
+ switch(clientAction) {
+ case CLIENT_TIMEOUT_ACCEPT:
+ TEST_ASSERT(clientSock == 0);
+ TEST_ASSERT(PR_GetError() == PR_IO_TIMEOUT_ERROR);
+ break;
+ case CLIENT_NORMAL:
+ TEST_ASSERT(clientSock);
+ bytesRead = PR_Recv(clientSock,
+ buf, CLIENT_DATA, 0, timeoutTime);
+ TEST_ASSERT(bytesRead == CLIENT_DATA);
+ break;
+ case CLIENT_TIMEOUT_SEND:
+ TEST_ASSERT(clientSock);
+ bytesRead = PR_Recv(clientSock,
+ buf, CLIENT_DATA, 0, timeoutTime);
+ TEST_ASSERT(bytesRead == -1);
+ TEST_ASSERT(PR_GetError() == PR_IO_TIMEOUT_ERROR);
+ break;
+ }
+ break;
+ case ACCEPT_READ:
+ status = PR_AcceptRead(listenSock, &clientSock,
+ &raddr, buf, CLIENT_DATA, timeoutTime);
+ switch(clientAction) {
+ case CLIENT_TIMEOUT_ACCEPT:
+ /* Invalid test case */
+ TEST_ASSERT(0);
+ break;
+ case CLIENT_NORMAL:
+ TEST_ASSERT(clientSock);
+ TEST_ASSERT(status == CLIENT_DATA);
+ break;
+ case CLIENT_TIMEOUT_SEND:
+ TEST_ASSERT(status == -1);
+ TEST_ASSERT(PR_GetError() == PR_IO_TIMEOUT_ERROR);
+ break;
+ }
+ break;
+#ifdef WINNT
+ case ACCEPT_FAST:
+ clientSock = PR_NTFast_Accept(listenSock,
+ &clientAddr, timeoutTime);
+ switch(clientAction) {
+ case CLIENT_TIMEOUT_ACCEPT:
+ TEST_ASSERT(clientSock == 0);
+ if (debug_mode)
+ PR_fprintf(output, "PR_GetError is %ld\n", PR_GetError());
+ TEST_ASSERT(PR_GetError() == PR_IO_TIMEOUT_ERROR);
+ break;
+ case CLIENT_NORMAL:
+ TEST_ASSERT(clientSock);
+ bytesRead = PR_Recv(clientSock,
+ buf, CLIENT_DATA, 0, timeoutTime);
+ TEST_ASSERT(bytesRead == CLIENT_DATA);
+ break;
+ case CLIENT_TIMEOUT_SEND:
+ TEST_ASSERT(clientSock);
+ bytesRead = PR_Recv(clientSock,
+ buf, CLIENT_DATA, 0, timeoutTime);
+ TEST_ASSERT(bytesRead == -1);
+ TEST_ASSERT(PR_GetError() == PR_IO_TIMEOUT_ERROR);
+ break;
+ }
+ break;
+ break;
+ case ACCEPT_READ_FAST:
+ status = PR_NTFast_AcceptRead(listenSock,
+ &clientSock, &raddr, buf, 4096, timeoutTime);
+ switch(clientAction) {
+ case CLIENT_TIMEOUT_ACCEPT:
+ /* Invalid test case */
+ TEST_ASSERT(0);
+ break;
+ case CLIENT_NORMAL:
+ TEST_ASSERT(clientSock);
+ TEST_ASSERT(status == CLIENT_DATA);
+ break;
+ case CLIENT_TIMEOUT_SEND:
+ TEST_ASSERT(clientSock == NULL);
+ TEST_ASSERT(status == -1);
+ TEST_ASSERT(PR_GetError() == PR_IO_TIMEOUT_ERROR);
+ break;
+ }
+ break;
+ case ACCEPT_READ_FAST_CB:
+ status = PR_NTFast_AcceptRead_WithTimeoutCallback(
+ listenSock, &clientSock, &raddr, buf, 4096,
+ timeoutTime, timeout_callback, (void *)CALLBACK_MAGIC);
+ switch(clientAction) {
+ case CLIENT_TIMEOUT_ACCEPT:
+ /* Invalid test case */
+ TEST_ASSERT(0);
+ break;
+ case CLIENT_NORMAL:
+ TEST_ASSERT(clientSock);
+ TEST_ASSERT(status == CLIENT_DATA);
+ break;
+ case CLIENT_TIMEOUT_SEND:
+ if (debug_mode)
+ PR_fprintf(output, "clientSock = 0x%8.8lx\n", clientSock);
+ TEST_ASSERT(clientSock == NULL);
+ TEST_ASSERT(status == -1);
+ TEST_ASSERT(PR_GetError() == PR_IO_TIMEOUT_ERROR);
+ break;
+ }
+ break;
+#endif
+ }
+ if (clientSock != NULL) {
+ PR_Close(clientSock);
+ clientSock = NULL;
+ }
+ }
+ PR_Close(listenSock);
+
+ PR_JoinThread(clientThread);
+}
+
+
+void AcceptUpdatedTest(void)
+{
+ RunTest(ACCEPT_NORMAL, CLIENT_NORMAL);
+}
+void AcceptNotUpdatedTest(void)
+{
+ RunTest(ACCEPT_FAST, CLIENT_NORMAL);
+}
+void AcceptReadTest(void)
+{
+ RunTest(ACCEPT_READ, CLIENT_NORMAL);
+}
+void AcceptReadNotUpdatedTest(void)
+{
+ RunTest(ACCEPT_READ_FAST, CLIENT_NORMAL);
+}
+void AcceptReadCallbackTest(void)
+{
+ RunTest(ACCEPT_READ_FAST_CB, CLIENT_NORMAL);
+}
+
+void TimeoutAcceptUpdatedTest(void)
+{
+ RunTest(ACCEPT_NORMAL, CLIENT_TIMEOUT_ACCEPT);
+}
+void TimeoutAcceptNotUpdatedTest(void)
+{
+ RunTest(ACCEPT_FAST, CLIENT_TIMEOUT_ACCEPT);
+}
+void TimeoutAcceptReadCallbackTest(void)
+{
+ RunTest(ACCEPT_READ_FAST_CB, CLIENT_TIMEOUT_ACCEPT);
+}
+
+void TimeoutReadUpdatedTest(void)
+{
+ RunTest(ACCEPT_NORMAL, CLIENT_TIMEOUT_SEND);
+}
+void TimeoutReadNotUpdatedTest(void)
+{
+ RunTest(ACCEPT_FAST, CLIENT_TIMEOUT_SEND);
+}
+void TimeoutReadReadTest(void)
+{
+ RunTest(ACCEPT_READ, CLIENT_TIMEOUT_SEND);
+}
+void TimeoutReadReadNotUpdatedTest(void)
+{
+ RunTest(ACCEPT_READ_FAST, CLIENT_TIMEOUT_SEND);
+}
+void TimeoutReadReadCallbackTest(void)
+{
+ RunTest(ACCEPT_READ_FAST_CB, CLIENT_TIMEOUT_SEND);
+}
+
+/************************************************************************/
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+ if (debug_mode)
+ PR_fprintf(output, "%40s: %6.2f usec\n", msg, d / count);
+
+}
+
+int main(int argc, char **argv)
+{
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name [-d] [-c n]
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "Gdc:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'G': /* global threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ case 'c': /* loop counter */
+ count = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ output = PR_STDERR;
+ PR_STDIO_INIT();
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("accept.log");
+ debug_mode = 1;
+#endif
+
+ timeoutTime = PR_SecondsToInterval(TIMEOUTSECS);
+ if (debug_mode)
+ PR_fprintf(output, "\nRun accept() sucessful connection tests\n");
+
+ Measure(AcceptUpdatedTest, "PR_Accept()");
+ Measure(AcceptReadTest, "PR_AcceptRead()");
+#ifdef WINNT
+ Measure(AcceptNotUpdatedTest, "PR_NTFast_Accept()");
+ Measure(AcceptReadNotUpdatedTest, "PR_NTFast_AcceptRead()");
+ Measure(AcceptReadCallbackTest, "PR_NTFast_AcceptRead_WithTimeoutCallback()");
+#endif
+ if (debug_mode)
+ PR_fprintf(output, "\nRun accept() timeout in the accept tests\n");
+#ifdef WINNT
+ Measure(TimeoutReadReadCallbackTest, "PR_NTFast_AcceptRead_WithTimeoutCallback()");
+#endif
+ Measure(TimeoutReadUpdatedTest, "PR_Accept()");
+ if (debug_mode)
+ PR_fprintf(output, "\nRun accept() timeout in the read tests\n");
+ Measure(TimeoutReadReadTest, "PR_AcceptRead()");
+#ifdef WINNT
+ Measure(TimeoutReadNotUpdatedTest, "PR_NTFast_Accept()");
+ Measure(TimeoutReadReadNotUpdatedTest, "PR_NTFast_AcceptRead()");
+ Measure(TimeoutReadReadCallbackTest, "PR_NTFast_AcceptRead_WithTimeoutCallback()");
+#endif
+ PR_fprintf(output, "%s\n", (failed_already) ? "FAIL" : "PASS");
+ return failed_already;
+}
diff --git a/pr/tests/alarm.c b/pr/tests/alarm.c
new file mode 100644
index 00000000..730e9de8
--- /dev/null
+++ b/pr/tests/alarm.c
@@ -0,0 +1,546 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+** 1996 - Netscape Communications Corporation
+**
+** Name: alarmtst.c
+**
+** Description: Test alarms
+**
+** Modification History:
+** 13-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+
+#include "prlog.h"
+#include "prinit.h"
+#ifdef XP_MAC
+#include "pralarm.h"
+#else
+#include "obsolete/pralarm.h"
+#endif
+#include "prlock.h"
+#include "prlong.h"
+#include "prcvar.h"
+#include "prinrval.h"
+#include "prtime.h"
+
+/* Used to get the command line option */
+#include "plgetopt.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(XP_UNIX)
+#include <sys/time.h>
+#endif
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+#endif
+
+static PRIntn debug_mode;
+static PRIntn failed_already=0;
+static PRThreadScope thread_scope = PR_LOCAL_THREAD;
+
+typedef struct notifyData {
+ PRLock *ml;
+ PRCondVar *child;
+ PRCondVar *parent;
+ PRBool pending;
+ PRUint32 counter;
+} NotifyData;
+
+static void Notifier(void *arg)
+{
+ NotifyData *notifyData = (NotifyData*)arg;
+ PR_Lock(notifyData->ml);
+ while (notifyData->counter > 0)
+ {
+ while (!notifyData->pending)
+ PR_WaitCondVar(notifyData->child, PR_INTERVAL_NO_TIMEOUT);
+ notifyData->counter -= 1;
+ notifyData->pending = PR_FALSE;
+ PR_NotifyCondVar(notifyData->parent);
+ }
+ PR_Unlock(notifyData->ml);
+} /* Notifier */
+/***********************************************************************
+** PRIVATE FUNCTION: ConditionNotify
+** DESCRIPTION:
+**
+** INPUTS: loops
+** OUTPUTS: None
+** RETURN: overhead
+** SIDE EFFECTS:
+**
+** RESTRICTIONS:
+** None
+** MEMORY: NA
+** ALGORITHM:
+**
+***********************************************************************/
+
+
+static PRIntervalTime ConditionNotify(PRUint32 loops)
+{
+ PRThread *thread;
+ NotifyData notifyData;
+ PRIntervalTime timein, overhead;
+
+ timein = PR_IntervalNow();
+
+ notifyData.counter = loops;
+ notifyData.ml = PR_NewLock();
+ notifyData.child = PR_NewCondVar(notifyData.ml);
+ notifyData.parent = PR_NewCondVar(notifyData.ml);
+ thread = PR_CreateThread(
+ PR_USER_THREAD, Notifier, &notifyData,
+ PR_GetThreadPriority(PR_GetCurrentThread()),
+ thread_scope, PR_JOINABLE_THREAD, 0);
+
+ overhead = PR_IntervalNow() - timein; /* elapsed so far */
+
+ PR_Lock(notifyData.ml);
+ while (notifyData.counter > 0)
+ {
+ notifyData.pending = PR_TRUE;
+ PR_NotifyCondVar(notifyData.child);
+ while (notifyData.pending)
+ PR_WaitCondVar(notifyData.parent, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(notifyData.ml);
+
+ timein = PR_IntervalNow();
+
+ (void)PR_JoinThread(thread);
+ PR_DestroyCondVar(notifyData.child);
+ PR_DestroyCondVar(notifyData.parent);
+ PR_DestroyLock(notifyData.ml);
+
+ overhead += (PR_IntervalNow() - timein); /* more overhead */
+
+ return overhead;
+} /* ConditionNotify */
+
+static PRIntervalTime ConditionTimeout(PRUint32 loops)
+{
+ PRUintn count;
+ PRIntervalTime overhead, timein = PR_IntervalNow();
+
+ PRLock *ml = PR_NewLock();
+ PRCondVar *cv = PR_NewCondVar(ml);
+ PRIntervalTime interval = PR_MillisecondsToInterval(50);
+
+ overhead = PR_IntervalNow() - timein;
+
+ PR_Lock(ml);
+ for (count = 0; count < loops; ++count)
+ {
+ overhead += interval;
+ PR_ASSERT(PR_WaitCondVar(cv, interval) == PR_SUCCESS);
+ }
+ PR_Unlock(ml);
+
+ timein = PR_IntervalNow();
+ PR_DestroyCondVar(cv);
+ PR_DestroyLock(ml);
+ overhead += (PR_IntervalNow() - timein);
+
+ return overhead;
+} /* ConditionTimeout */
+
+typedef struct AlarmData {
+ PRLock *ml;
+ PRCondVar *cv;
+ PRUint32 rate, late, times;
+ PRIntervalTime duration, timein, period;
+} AlarmData;
+
+static PRBool AlarmFn1(PRAlarmID *id, void *clientData, PRUint32 late)
+{
+ PRStatus rv = PR_SUCCESS;
+ PRBool keepGoing, resetAlarm;
+ PRIntervalTime interval, now = PR_IntervalNow();
+ AlarmData *ad = (AlarmData*)clientData;
+
+ PR_Lock(ad->ml);
+ ad->late += late;
+ ad->times += 1;
+ keepGoing = ((PRIntervalTime)(now - ad->timein) < ad->duration) ?
+ PR_TRUE : PR_FALSE;
+ if (!keepGoing)
+ rv = PR_NotifyCondVar(ad->cv);
+ resetAlarm = ((ad->times % 31) == 0) ? PR_TRUE : PR_FALSE;
+
+ interval = (ad->period + ad->rate - 1) / ad->rate;
+ if (!late && (interval > 10))
+ {
+ interval &= (now & 0x03) + 1;
+ PR_WaitCondVar(ad->cv, interval);
+ }
+
+ PR_Unlock(ad->ml);
+
+ if (rv != PR_SUCCESS) {
+ if (!debug_mode) failed_already=1;
+ else
+ printf("AlarmFn: notify status: FAIL\n");
+
+ }
+
+ if (resetAlarm)
+ {
+ ad->rate += 3;
+ ad->late = ad->times = 0;
+ if (PR_ResetAlarm(id, ad->period, ad->rate) != PR_SUCCESS)
+ {
+ if (!debug_mode)
+ failed_already=1;
+ else
+ printf("AlarmFn: Resetting alarm status: FAIL\n");
+
+ keepGoing = PR_FALSE;
+ }
+
+ }
+
+ return keepGoing;
+} /* AlarmFn1 */
+
+static PRIntervalTime Alarms1(PRUint32 loops)
+{
+ PRAlarm *alarm;
+ AlarmData ad;
+ PRIntervalTime overhead, timein = PR_IntervalNow();
+ PRIntervalTime duration = PR_SecondsToInterval(3);
+
+ PRLock *ml = PR_NewLock();
+ PRCondVar *cv = PR_NewCondVar(ml);
+
+ ad.ml = ml;
+ ad.cv = cv;
+ ad.rate = 1;
+ ad.times = loops;
+ ad.late = ad.times = 0;
+ ad.duration = duration;
+ ad.timein = PR_IntervalNow();
+ ad.period = PR_SecondsToInterval(1);
+
+ alarm = PR_CreateAlarm();
+
+ (void)PR_SetAlarm(
+ alarm, ad.period, ad.rate, AlarmFn1, &ad);
+
+ overhead = PR_IntervalNow() - timein;
+
+ PR_Lock(ml);
+ while ((PRIntervalTime)(PR_IntervalNow() - ad.timein) < duration)
+ PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT);
+ PR_Unlock(ml);
+
+ timein = PR_IntervalNow();
+ (void)PR_DestroyAlarm(alarm);
+ PR_DestroyCondVar(cv);
+ PR_DestroyLock(ml);
+ overhead += (PR_IntervalNow() - timein);
+
+ return duration + overhead;
+} /* Alarms1 */
+
+static PRBool AlarmFn2(PRAlarmID *id, void *clientData, PRUint32 late)
+{
+#if defined(XP_MAC)
+#pragma unused (id)
+#endif
+
+ PRBool keepGoing;
+ PRStatus rv = PR_SUCCESS;
+ AlarmData *ad = (AlarmData*)clientData;
+ PRIntervalTime interval, now = PR_IntervalNow();
+
+ PR_Lock(ad->ml);
+ ad->times += 1;
+ keepGoing = ((PRIntervalTime)(now - ad->timein) < ad->duration) ?
+ PR_TRUE : PR_FALSE;
+ interval = (ad->period + ad->rate - 1) / ad->rate;
+
+ if (!late && (interval > 10))
+ {
+ interval &= (now & 0x03) + 1;
+ PR_WaitCondVar(ad->cv, interval);
+ }
+
+ if (!keepGoing) rv = PR_NotifyCondVar(ad->cv);
+
+ PR_Unlock(ad->ml);
+
+
+ if (rv != PR_SUCCESS)
+ failed_already=1;;
+
+ return keepGoing;
+} /* AlarmFn2 */
+
+static PRIntervalTime Alarms2(PRUint32 loops)
+{
+ PRStatus rv;
+ PRAlarm *alarm;
+ PRIntervalTime overhead, timein = PR_IntervalNow();
+ AlarmData ad;
+ PRIntervalTime duration = PR_SecondsToInterval(30);
+
+ PRLock *ml = PR_NewLock();
+ PRCondVar *cv = PR_NewCondVar(ml);
+
+ ad.ml = ml;
+ ad.cv = cv;
+ ad.rate = 1;
+ ad.times = loops;
+ ad.late = ad.times = 0;
+ ad.duration = duration;
+ ad.timein = PR_IntervalNow();
+ ad.period = PR_SecondsToInterval(1);
+
+ alarm = PR_CreateAlarm();
+
+ (void)PR_SetAlarm(
+ alarm, ad.period, ad.rate, AlarmFn2, &ad);
+
+ overhead = PR_IntervalNow() - timein;
+
+ PR_Lock(ml);
+ while ((PRIntervalTime)(PR_IntervalNow() - ad.timein) < duration)
+ PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT);
+ PR_Unlock(ml);
+
+ timein = PR_IntervalNow();
+
+ rv = PR_DestroyAlarm(alarm);
+ if (rv != PR_SUCCESS)
+ if (!debug_mode)
+ failed_already=1;
+ else
+ printf("***Destroying alarm status: FAIL\n");
+
+
+ PR_DestroyCondVar(cv);
+ PR_DestroyLock(ml);
+
+ overhead += (PR_IntervalNow() - timein);
+
+ return duration + overhead;
+} /* Alarms2 */
+
+static PRIntervalTime Alarms3(PRUint32 loops)
+{
+ PRIntn i;
+ PRStatus rv;
+ PRAlarm *alarm;
+ AlarmData ad[3];
+ PRIntervalTime duration = PR_SecondsToInterval(30);
+ PRIntervalTime overhead, timein = PR_IntervalNow();
+
+ PRLock *ml = PR_NewLock();
+ PRCondVar *cv = PR_NewCondVar(ml);
+
+ for (i = 0; i < 3; ++i)
+ {
+ ad[i].ml = ml;
+ ad[i].cv = cv;
+ ad[i].rate = 1;
+ ad[i].times = loops;
+ ad[i].duration = duration;
+ ad[i].late = ad[i].times = 0;
+ ad[i].timein = PR_IntervalNow();
+ ad[i].period = PR_SecondsToInterval(1);
+
+ /* more loops, faster rate => same elapsed time */
+ ad[i].times = (i + 1) * loops;
+ ad[i].rate = (i + 1) * 10;
+ }
+
+ alarm = PR_CreateAlarm();
+
+ for (i = 0; i < 3; ++i)
+ {
+ (void)PR_SetAlarm(
+ alarm, ad[i].period, ad[i].rate,
+ AlarmFn2, &ad[i]);
+ }
+
+ overhead = PR_IntervalNow() - timein;
+
+ PR_Lock(ml);
+ for (i = 0; i < 3; ++i)
+ {
+ while ((PRIntervalTime)(PR_IntervalNow() - ad[i].timein) < duration)
+ PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(ml);
+
+ timein = PR_IntervalNow();
+
+ if (debug_mode)
+ printf
+ ("Alarms3 finished at %u, %u, %u\n",
+ ad[0].timein, ad[1].timein, ad[2].timein);
+
+ rv = PR_DestroyAlarm(alarm);
+ if (rv != PR_SUCCESS) {
+ if (!debug_mode)
+ failed_already=1;
+ else
+ printf("***Destroying alarm status: FAIL\n");
+ }
+ PR_DestroyCondVar(cv);
+ PR_DestroyLock(ml);
+
+ overhead += (duration / 3);
+ overhead += (PR_IntervalNow() - timein);
+
+ return overhead;
+} /* Alarms3 */
+
+static PRUint32 TimeThis(
+ const char *msg, PRUint32 (*func)(PRUint32 loops), PRUint32 loops)
+{
+ PRUint32 overhead, usecs;
+ PRIntervalTime predicted, timein, timeout, ticks;
+
+ if (debug_mode)
+ printf("Testing %s ...", msg);
+
+ timein = PR_IntervalNow();
+ predicted = func(loops);
+ timeout = PR_IntervalNow();
+
+ if (debug_mode)
+ printf(" done\n");
+
+ ticks = timeout - timein;
+ usecs = PR_IntervalToMicroseconds(ticks);
+ overhead = PR_IntervalToMicroseconds(predicted);
+
+ if(ticks < predicted)
+ {
+ if (debug_mode) {
+ printf("\tFinished in negative time\n");
+ printf("\tpredicted overhead was %d usecs\n", overhead);
+ printf("\ttest completed in %d usecs\n\n", usecs);
+ }
+ }
+ else
+ {
+ if (debug_mode)
+ printf(
+ "\ttotal: %d usecs\n\toverhead: %d usecs\n\tcost: %6.3f usecs\n\n",
+ usecs, overhead, ((double)(usecs - overhead) / (double)loops));
+ }
+
+ return overhead;
+} /* TimeThis */
+
+int prmain(int argc, char** argv)
+{
+ PRUint32 cpu, cpus = 0, loops = 0;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name [-d]
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "Gdl:c:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'G': /* GLOBAL threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ case 'l': /* loop count */
+ loops = atoi(opt->value);
+ break;
+ case 'c': /* concurrency limit */
+ cpus = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+
+ if (cpus == 0) cpus = 1;
+ if (loops == 0) loops = 4;
+
+ if (debug_mode)
+ printf("Alarm: Using %d loops\n", loops);
+
+ if (debug_mode)
+ printf("Alarm: Using %d cpu(s)\n", cpus);
+#ifdef XP_MAC
+ SetupMacPrintfLog("alarm.log");
+ debug_mode = 1;
+#endif
+
+ for (cpu = 1; cpu <= cpus; ++cpu)
+ {
+ if (debug_mode)
+ printf("\nAlarm: Using %d CPU(s)\n", cpu);
+
+ PR_SetConcurrency(cpu);
+
+ /* some basic time test */
+ (void)TimeThis("ConditionNotify", ConditionNotify, loops);
+ (void)TimeThis("ConditionTimeout", ConditionTimeout, loops);
+ (void)TimeThis("Alarms1", Alarms1, loops);
+ (void)TimeThis("Alarms2", Alarms2, loops);
+ (void)TimeThis("Alarms3", Alarms3, loops);
+ }
+ return 0;
+}
+
+int main(int argc, char** argv)
+{
+ PR_Initialize(prmain, argc, argv, 0);
+ PR_STDIO_INIT();
+ if (failed_already) return 1;
+ else return 0;
+
+} /* main */
+
+
+/* alarmtst.c */
diff --git a/pr/tests/atomic.c b/pr/tests/atomic.c
new file mode 100644
index 00000000..a2ba2044
--- /dev/null
+++ b/pr/tests/atomic.c
@@ -0,0 +1,103 @@
+/* -*- 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 "prio.h"
+#include "prprf.h"
+#include "pratom.h"
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PRInt32 rv, oldval, test, result = 0;
+ PRFileDesc *output = PR_GetSpecialFD(PR_StandardOutput);
+
+ test = -2;
+ rv = PR_AtomicIncrement(&test);
+ result = result | ((rv < 0) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_AtomicIncrement(%d) == %d: %s\n",
+ test, rv, (rv < 0) ? "PASSED" : "FAILED");
+ rv = PR_AtomicIncrement(&test);
+ result = result | ((rv == 0) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_AtomicIncrement(%d) == %d: %s\n",
+ test, rv, (rv == 0) ? "PASSED" : "FAILED");
+ rv = PR_AtomicIncrement(&test);
+ result = result | ((rv > 0) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_AtomicIncrement(%d) == %d: %s\n",
+ test, rv, (rv > 0) ? "PASSED" : "FAILED");
+
+ oldval = test = -2;
+ rv = PR_AtomicAdd(&test,1);
+ result = result | ((rv == -1) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_AtomicAdd(%d,%d) == %d: %s\n",
+ oldval, 1, rv, (rv == -1) ? "PASSED" : "FAILED");
+ oldval = test;
+ rv = PR_AtomicAdd(&test, 4);
+ result = result | ((rv == 3) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_AtomicAdd(%d,%d) == %d: %s\n",
+ oldval, 4, rv, (rv == 3) ? "PASSED" : "FAILED");
+ oldval = test;
+ rv = PR_AtomicAdd(&test, -6);
+ result = result | ((rv == -3) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_AtomicAdd(%d,%d) == %d: %s\n",
+ oldval, -6, rv, (rv == -3) ? "PASSED" : "FAILED");
+
+ test = 2;
+ rv = PR_AtomicDecrement(&test);
+ result = result | ((rv > 0) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_AtomicDecrement(%d) == %d: %s\n",
+ test, rv, (rv > 0) ? "PASSED" : "FAILED");
+ rv = PR_AtomicDecrement(&test);
+ result = result | ((rv == 0) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_AtomicDecrement(%d) == %d: %s\n",
+ test, rv, (rv == 0) ? "PASSED" : "FAILED");
+ rv = PR_AtomicDecrement(&test);
+ result = result | ((rv < 0) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_AtomicDecrement(%d) == %d: %s\n",
+ test, rv, (rv < 0) ? "PASSED" : "FAILED");
+
+ /* set to a different value */
+ test = -2;
+ rv = PR_AtomicSet(&test, 2);
+ result = result | (((rv == -2) && (test == 2)) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_AtomicSet(%d) == %d: %s\n",
+ test, rv, ((rv == -2) && (test == 2)) ? "PASSED" : "FAILED");
+
+ /* set to the same value */
+ test = -2;
+ rv = PR_AtomicSet(&test, -2);
+ result = result | (((rv == -2) && (test == -2)) ? 0 : 1);
+ PR_fprintf(
+ output, "PR_AtomicSet(%d) == %d: %s\n",
+ test, rv, ((rv == -2) && (test == -2)) ? "PASSED" : "FAILED");
+
+ PR_fprintf(
+ output, "Atomic operations test %s\n",
+ (result == 0) ? "PASSED" : "FAILED");
+ return result;
+} /* main */
+
+/* atomic.c */
diff --git a/pr/tests/attach.c b/pr/tests/attach.c
new file mode 100644
index 00000000..79a5d3d8
--- /dev/null
+++ b/pr/tests/attach.c
@@ -0,0 +1,336 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+** 1996 - Netscape Communications Corporation
+**
+** Name: attach.c
+**
+** Description: Platform-specific code to create a native thread. The native thread will
+** repeatedly call PR_AttachThread and PR_DetachThread. The
+** primordial thread waits for this new thread to finish.
+**
+** Modification History:
+** 13-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+** 12-June-97 Revert to return code 0 and 1.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+
+/* Used to get the command line option */
+#include "nspr.h"
+#include "pprthred.h"
+#include "plgetopt.h"
+
+#include <stdio.h>
+
+#ifdef WIN32
+#include <windows.h>
+#include <process.h>
+#elif defined(_PR_PTHREADS)
+#include <pthread.h>
+#include "md/_pth.h"
+#elif defined(IRIX)
+#include <sys/types.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <errno.h>
+#elif defined(SOLARIS)
+#include <thread.h>
+#elif defined(OS2)
+#define INCL_DOS
+#define INCL_ERRORS
+#include <os2.h>
+#endif
+
+#define DEFAULT_COUNT 1000
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+
+int count;
+
+
+static void
+AttachDetach(void)
+{
+ PRThread *me;
+ PRInt32 index;
+
+ for (index=0;index<count; index++) {
+ me = PR_AttachThread(PR_USER_THREAD,
+ PR_PRIORITY_NORMAL,
+ NULL);
+
+ if (!me) {
+ fprintf(stderr, "Error attaching thread %d: PR_AttachThread failed\n",
+ count);
+ failed_already = 1;
+ return;
+ }
+ PR_DetachThread();
+ }
+}
+
+/************************************************************************/
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+ if (debug_mode)
+ printf("%40s: %6.2f usec\n", msg, d / count);
+}
+
+#ifdef WIN32
+static unsigned __stdcall threadStartFunc(void *arg)
+#elif defined(IRIX) && !defined(_PR_PTHREADS)
+static void threadStartFunc(void *arg)
+#else
+static void * threadStartFunc(void *arg)
+#endif
+{
+#ifdef _PR_DCETHREADS
+ {
+ int rv;
+ pthread_t self = pthread_self();
+ rv = pthread_detach(&self);
+ if (debug_mode) PR_ASSERT(0 == rv);
+ else if (0 != rv) failed_already=1;
+ }
+#endif
+
+ Measure(AttachDetach, "Attach/Detach");
+
+#ifndef IRIX
+ return 0;
+#endif
+}
+
+int main(int argc, char **argv)
+{
+#ifdef _PR_PTHREADS
+ int rv;
+ pthread_t threadID;
+ pthread_attr_t attr;
+#elif defined(SOLARIS)
+ int rv;
+ thread_t threadID;
+#elif defined(WIN32)
+ DWORD rv;
+ unsigned threadID;
+ HANDLE hThread;
+#elif defined(IRIX)
+ int rv;
+ int threadID;
+#elif defined(OS2)
+ int rv;
+ TID threadID;
+#endif
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name [-d] [-c n]
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dc:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ case 'c': /* loop count */
+ count = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+#if defined(WIN16)
+ printf("attach: This test is not valid for Win16\n");
+ goto exit_now;
+#endif
+
+ if(0 == count) count = DEFAULT_COUNT;
+
+ /*
+ * To force the implicit initialization of nspr20
+ */
+ PR_SetError(0, 0);
+ PR_STDIO_INIT();
+
+ /*
+ * Platform-specific code to create a native thread. The native
+ * thread will repeatedly call PR_AttachThread and PR_DetachThread.
+ * The primordial thread waits for this new thread to finish.
+ */
+
+#ifdef _PR_PTHREADS
+
+ rv = PTHREAD_ATTR_INIT(&attr);
+ if (debug_mode) PR_ASSERT(0 == rv);
+ else if (0 != rv) {
+ failed_already=1;
+ goto exit_now;
+ }
+
+#ifndef _PR_DCETHREADS
+ rv = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+ if (debug_mode) PR_ASSERT(0 == rv);
+ else if (0 != rv) {
+ failed_already=1;
+ goto exit_now;
+ }
+#endif /* !_PR_DCETHREADS */
+ rv = PTHREAD_CREATE(&threadID, attr, threadStartFunc, NULL);
+ if (rv != 0) {
+ fprintf(stderr, "thread creation failed: error code %d\n", rv);
+ failed_already=1;
+ goto exit_now;
+ }
+ else {
+ if (debug_mode)
+ printf ("thread creation succeeded \n");
+
+ }
+ rv = PTHREAD_ATTR_DESTROY(&attr);
+ if (debug_mode) PR_ASSERT(0 == rv);
+ else if (0 != rv) {
+ failed_already=1;
+ goto exit_now;
+ }
+ rv = pthread_join(threadID, NULL);
+ if (debug_mode) PR_ASSERT(0 == rv);
+ else if (0 != rv) {
+ failed_already=1;
+ goto exit_now;
+ }
+
+#elif defined(SOLARIS)
+
+ rv = thr_create(NULL, 0, threadStartFunc, NULL, 0, &threadID);
+ if (rv != 0) {
+ if(!debug_mode) {
+ failed_already=1;
+ goto exit_now;
+ } else
+ fprintf(stderr, "thread creation failed: error code %d\n", rv);
+ }
+ rv = thr_join(threadID, NULL, NULL);
+ if (debug_mode) PR_ASSERT(0 == rv);
+ else if (0 != rv)
+ {
+ failed_already=1;
+ goto exit_now;
+ }
+
+
+#elif defined(WIN32)
+
+ hThread = (HANDLE) _beginthreadex(NULL, 0, threadStartFunc, NULL,
+ 0, &threadID);
+ if (hThread == 0) {
+ fprintf(stderr, "thread creation failed: error code %d\n",
+ GetLastError());
+ failed_already=1;
+ goto exit_now;
+ }
+ rv = WaitForSingleObject(hThread, INFINITE);
+ if (debug_mode)PR_ASSERT(rv != WAIT_FAILED);
+ else if (rv == WAIT_FAILED) {
+ failed_already=1;
+ goto exit_now;
+ }
+
+#elif defined(IRIX)
+
+ threadID = sproc(threadStartFunc, PR_SALL, NULL);
+ if (threadID == -1) {
+
+ fprintf(stderr, "thread creation failed: error code %d\n",
+ errno);
+ failed_already=1;
+ goto exit_now;
+
+ }
+ else {
+ if (debug_mode)
+ printf ("thread creation succeeded \n");
+ sleep(3);
+ goto exit_now;
+ }
+ rv = waitpid(threadID, NULL, 0);
+ if (debug_mode) PR_ASSERT(rv != -1);
+ else if (rv != -1) {
+ failed_already=1;
+ goto exit_now;
+ }
+
+#elif defined(OS2)
+
+ threadID = (TID) _beginthread((void(* _Optlink)(void*))threadStartFunc, NULL,
+ 32768, NULL);
+ if (threadID == -1) {
+ fprintf(stderr, "thread creation failed: error code %d\n", errno);
+ failed_already=1;
+ goto exit_now;
+ }
+ rv = DosWaitThread(&threadID, DCWW_WAIT);
+ if (debug_mode)PR_ASSERT(rv == NO_ERROR);
+ else if (rv == NO_ERROR) {
+ failed_already=1;
+ goto exit_now;
+ }
+
+#else
+ if (!debug_mode)
+ failed_already=1;
+ else
+ printf("The attach test does not apply to this platform because\n"
+ "either this platform does not have native threads or the\n"
+ "test needs to be written for this platform.\n");
+ goto exit_now;
+#endif
+
+exit_now:
+ if(failed_already)
+ return 1;
+ else
+ return 0;
+}
diff --git a/pr/tests/bigfile.c b/pr/tests/bigfile.c
new file mode 100644
index 00000000..50db2fb5
--- /dev/null
+++ b/pr/tests/bigfile.c
@@ -0,0 +1,299 @@
+/* -*- 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 "prio.h"
+#include "prmem.h"
+#include "prprf.h"
+#include "prinit.h"
+#include "prerror.h"
+#include "prthread.h"
+
+#include "plerror.h"
+#include "plgetopt.h"
+
+#define DEFAULT_COUNT 10
+#define DEFAULT_FILESIZE 1
+#define BUFFER_SIZE 1000000
+
+typedef enum {v_silent, v_whisper, v_shout} Verbosity;
+static void Verbose(Verbosity, const char*, const char*, PRIntn);
+
+#define VERBOSE(_l, _m) Verbose(_l, _m, __FILE__, __LINE__)
+
+static PRIntn test_result = 2;
+static PRFileDesc *output = NULL;
+static PRIntn verbose = v_silent;
+static PRIntn filesize = DEFAULT_FILESIZE;
+
+static PRIntn Usage(void)
+{
+ PR_fprintf(output, "Bigfile test usage:\n");
+ PR_fprintf(output, ">bigfile [-G] [-d] [-v[*v]] [-s <n>] <filename>\n");
+ PR_fprintf(output, "\td\tdebug mode (equivalent to -vvv)\t(false)\n");
+ PR_fprintf(output, "\tv\tAdditional levels of output\t(none)\n");
+ PR_fprintf(output, "\tk\tKeep data file after exit\t(false)\n");
+ PR_fprintf(output, "\ts <n>\tFile size in megabytes\t\t(1 megabyte)\n");
+ PR_fprintf(output, "\t<filename>\tName of test file\t(none)\n");
+ return 2; /* nothing happened */
+} /* Usage */
+
+static PRStatus DeleteIfFound(const char *filename)
+{
+ PRStatus rv;
+ VERBOSE(v_shout, "Checking for existing file");
+ rv = PR_Access(filename, PR_ACCESS_WRITE_OK);
+ if (PR_SUCCESS == rv)
+ {
+ VERBOSE(v_shout, "Deleting existing file");
+ rv = PR_Delete(filename);
+ if (PR_FAILURE == rv) VERBOSE(v_shout, "Cannot delete big file");
+ }
+ else if (PR_FILE_NOT_FOUND_ERROR != PR_GetError())
+ VERBOSE(v_shout, "Cannot access big file");
+ else rv = PR_SUCCESS;
+ return rv;
+} /* DeleteIfFound */
+
+static PRIntn Error(const char *msg, const char *filename)
+{
+ PRInt32 error = PR_GetError();
+ if (NULL != msg)
+ {
+ if (0 == error) PR_fprintf(output, msg);
+ else PL_FPrintError(output, msg);
+ }
+ (void)DeleteIfFound(filename);
+ if (v_shout == verbose) PR_Abort();
+ return 1;
+} /* Error */
+
+static void Verbose(
+ Verbosity level, const char *msg, const char *file, PRIntn line)
+{
+ if (level <= verbose)
+ PR_fprintf(output, "[%s : %d]: %s\n", file, line, msg);
+} /* Verbose */
+
+static void PrintInfo(PRFileInfo64 *info, const char *filename)
+{
+ PRExplodedTime tm;
+ char ctime[40], mtime[40];
+ static const char *types[] = {"FILE", "DIRECTORY", "OTHER"};
+ PR_fprintf(
+ output, "[%s : %d]: File info for %s\n",
+ __FILE__, __LINE__, filename);
+ PR_fprintf(
+ output, " type: %s, size: %llu bytes,\n",
+ types[info->type - 1], info->size);
+
+ PR_ExplodeTime(info->creationTime, PR_GMTParameters, &tm);
+ (void)PR_FormatTime(ctime, sizeof(ctime), "%c GMT", &tm);
+ PR_ExplodeTime(info->modifyTime, PR_GMTParameters, &tm);
+ (void)PR_FormatTime(mtime, sizeof(mtime), "%c GMT", &tm);
+
+ PR_fprintf(
+ output, " creation: %s,\n modify: %s\n", ctime, mtime);
+} /* PrintInfo */
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PRStatus rv;
+ char *buffer;
+ PLOptStatus os;
+ PRInt32 loop, bytes;
+ PRFileInfo small_info;
+ PRFileInfo64 big_info;
+ PRBool keep = PR_FALSE;
+ PRFileDesc *file = NULL;
+ const char *filename = NULL;
+ PRIntn count = DEFAULT_COUNT;
+ PRInt64 filesize64, big_answer, big_size, one_meg, zero_meg, big_fragment;
+ PRInt64 sevenFox = LL_INIT(0,0x7fffffff);
+
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dtvhs:");
+
+ output = PR_GetSpecialFD(PR_StandardError);
+ PR_STDIO_INIT();
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 0:
+ filename = opt->value;
+ break;
+ case 'd': /* debug mode */
+ verbose = v_shout;
+ break;
+ case 'k': /* keep file */
+ keep = PR_TRUE;
+ break;
+ case 'v': /* verbosity */
+ if (v_shout > verbose) verbose += 1;
+ break;
+ case 'c': /* loop counter */
+ count = atoi(opt->value);
+ break;
+ case 's': /* filesize */
+ filesize = atoi(opt->value);
+ break;
+ case 'h': /* confused */
+ default:
+ return Usage();
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (0 == count) count = DEFAULT_COUNT;
+ if (0 == filesize) filesize = DEFAULT_FILESIZE;
+ if (NULL == filename)
+ {
+ if (DEFAULT_FILESIZE != filesize) return Usage();
+ else filename = "bigfile.dat";
+ }
+
+ if (PR_FAILURE == DeleteIfFound(filename)) return 1;
+
+ test_result = 0;
+
+ LL_I2L(zero_meg, 0);
+ LL_I2L(one_meg, 1000000);
+ LL_I2L(filesize64, filesize);
+ buffer = (char*)PR_MALLOC(BUFFER_SIZE);
+ LL_I2L(big_fragment, BUFFER_SIZE);
+ LL_MUL(filesize64, filesize64, one_meg);
+
+ for (loop = 0; loop < BUFFER_SIZE; ++loop) buffer[loop] = (char)loop;
+
+ VERBOSE(v_whisper, "Creating big file");
+ file = PR_Open(filename, PR_CREATE_FILE | PR_WRONLY, 0666);
+ if (NULL == file) return Error("PR_Open()", filename);
+
+ VERBOSE(v_whisper, "Testing available space in empty file");
+ big_answer = file->methods->available64(file);
+ if (!LL_IS_ZERO(big_answer)) return Error("empty available64()", filename);
+
+ LL_SUB(big_size, filesize64, one_meg);
+ VERBOSE(v_whisper, "Creating sparce big file by seeking to end");
+ big_answer = file->methods->seek64(file, big_size, PR_SEEK_SET);
+ if (!LL_EQ(big_answer, big_size)) return Error("seek", filename);
+
+ VERBOSE(v_whisper, "Writing block at end of sparce file");
+ bytes = file->methods->write(file, buffer, BUFFER_SIZE);
+ if (bytes != BUFFER_SIZE) return Error("write", filename);
+
+ VERBOSE(v_whisper, "Testing available space at end of sparce file");
+ big_answer = file->methods->available64(file);
+ if (!LL_IS_ZERO(big_answer)) return Error("eof available64()", filename);
+
+ VERBOSE(v_whisper, "Getting big info on sparce big file");
+ rv = file->methods->fileInfo64(file, &big_info);
+ if (PR_FAILURE == rv) return Error("fileInfo64()", filename);
+ if (v_shout <= verbose) PrintInfo(&big_info, filename);
+
+ VERBOSE(v_whisper, "Getting small info on sparce big file");
+ rv = file->methods->fileInfo(file, &small_info);
+ if (LL_CMP(sevenFox, <, filesize64) && (PR_SUCCESS == rv))
+ {
+ VERBOSE(v_whisper, "Should have failed and didn't");
+ return Error("fileInfo()", filename);
+ }
+ else if (LL_CMP(sevenFox, >, filesize64) && (PR_FAILURE == rv))
+ {
+ VERBOSE(v_whisper, "Should have succeeded and didn't");
+ return Error("fileInfo()", filename);
+ }
+
+ VERBOSE(v_whisper, "Rewinding big file");
+ big_answer = file->methods->seek64(file, zero_meg, PR_SEEK_SET);
+ if (!LL_IS_ZERO(big_answer)) return Error("rewind seek64()", filename);
+
+ VERBOSE(v_whisper, "Establishing available space in rewound file");
+ big_answer = file->methods->available64(file);
+ if (LL_NE(filesize64, big_answer))
+ return Error("bof available64()", filename);
+
+ VERBOSE(v_whisper, "Closing big file");
+ rv = file->methods->close(file);
+ if (PR_FAILURE == rv) return Error("close()", filename);
+
+ VERBOSE(v_whisper, "Reopening big file");
+ file = PR_Open(filename, PR_RDWR, 0666);
+ if (NULL == file) return Error("open failed", filename);
+
+ VERBOSE(v_whisper, "Checking available data in reopened file");
+ big_answer = file->methods->available64(file);
+ if (LL_NE(filesize64, big_answer))
+ return Error("reopened available64()", filename);
+
+ big_answer = zero_meg;
+ VERBOSE(v_whisper, "Rewriting every byte of big file data");
+ do
+ {
+ bytes = file->methods->write(file, buffer, BUFFER_SIZE);
+ if (bytes != BUFFER_SIZE)
+ return Error("write", filename);
+ LL_ADD(big_answer, big_answer, big_fragment);
+ } while (LL_CMP(big_answer, <, filesize64));
+
+ VERBOSE(v_whisper, "Checking position at eof");
+ big_answer = file->methods->seek64(file, zero_meg, PR_SEEK_CUR);
+ if (LL_NE(big_answer, filesize64))
+ return Error("file size error", filename);
+
+ VERBOSE(v_whisper, "Testing available space at eof");
+ big_answer = file->methods->available64(file);
+ if (!LL_IS_ZERO(big_answer))
+ return Error("eof available64()", filename);
+
+ VERBOSE(v_whisper, "Rewinding full file");
+ big_answer = file->methods->seek64(file, zero_meg, PR_SEEK_SET);
+ if (!LL_IS_ZERO(big_answer)) return Error("bof seek64()", filename);
+
+ VERBOSE(v_whisper, "Testing available space in rewound file");
+ big_answer = file->methods->available64(file);
+ if (LL_NE(big_answer, filesize64)) return Error("bof available64()", filename);
+
+ VERBOSE(v_whisper, "Seeking to end of big file");
+ big_answer = file->methods->seek64(file, filesize64, PR_SEEK_SET);
+ if (LL_NE(big_answer, filesize64)) return Error("eof seek64()", filename);
+
+ VERBOSE(v_whisper, "Getting info on big file while it's open");
+ rv = file->methods->fileInfo64(file, &big_info);
+ if (PR_FAILURE == rv) return Error("fileInfo64()", filename);
+ if (v_shout <= verbose) PrintInfo(&big_info, filename);
+
+ VERBOSE(v_whisper, "Closing big file");
+ rv = file->methods->close(file);
+ if (PR_FAILURE == rv) return Error("close()", filename);
+
+ VERBOSE(v_whisper, "Getting info on big file after it's closed");
+ rv = PR_GetFileInfo64(filename, &big_info);
+ if (PR_FAILURE == rv) return Error("fileInfo64()", filename);
+ if (v_shout <= verbose) PrintInfo(&big_info, filename);
+
+ VERBOSE(v_whisper, "Deleting big file");
+ rv = PR_Delete(filename);
+ if (PR_FAILURE == rv) return Error("PR_Delete()", filename);
+
+ PR_DELETE(buffer);
+ return test_result;
+} /* main */
+
+/* bigfile.c */
diff --git a/pr/tests/bug1test.c b/pr/tests/bug1test.c
new file mode 100644
index 00000000..eafb2a2b
--- /dev/null
+++ b/pr/tests/bug1test.c
@@ -0,0 +1,238 @@
+/* -*- 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.
+ */
+
+/*
+Attached is a test program that uses the nspr1 to demonstrate a bug
+under NT4.0. The fix has already been mentioned (add a ResetEvent just
+before leaving the critical section in _PR_CondWait in hwmon.c).
+*/
+
+#include "prthread.h"
+#include "prtypes.h"
+#include "prinit.h"
+#include "prmon.h"
+#include "prlog.h"
+
+typedef struct Arg_s
+{
+ PRInt32 a, b;
+} Arg_t;
+
+PRMonitor* gMonitor; // the monitor
+PRInt32 gReading; // number of read locks
+PRInt32 gWriteWaiting; // number of threads waiting for write lock
+PRInt32 gReadWaiting; // number of threads waiting for read lock
+
+PRInt32 gCounter; // a counter
+
+ // stats
+PRInt32 gReads; // number of successful reads
+PRInt32 gMaxReads; // max number of simultaneous reads
+PRInt32 gMaxWriteWaits; // max number of writes that waited for read
+PRInt32 gMaxReadWaits; // max number of reads that waited for write wait
+
+
+void spin (PRInt32 aDelay)
+{
+ PRInt32 index;
+ PRInt32 delay = aDelay * 1000;
+
+ PR_Sleep(0);
+
+ // randomize delay a bit
+ delay = (delay / 2) + (PRInt32)((float)delay *
+ ((float)rand () / (float)RAND_MAX));
+
+ for (index = 0; index < delay * 10; index++)
+ // consume a bunch of cpu cycles
+ ;
+ PR_Sleep(0);
+}
+
+void doWriteThread (void* arg)
+{
+ PRInt32 last;
+ Arg_t *args = (Arg_t*)arg;
+ PRInt32 aWorkDelay = args->a, aWaitDelay = args->b;
+ PR_Sleep(0);
+
+ while (1)
+ {
+ // -- enter write lock
+ PR_EnterMonitor (gMonitor);
+
+ if (0 < gReading) // wait for read locks to go away
+ {
+ PRIntervalTime fiveSecs = PR_SecondsToInterval(5);
+
+ gWriteWaiting++;
+ if (gWriteWaiting > gMaxWriteWaits) // stats
+ gMaxWriteWaits = gWriteWaiting;
+ while (0 < gReading)
+ PR_Wait (gMonitor, fiveSecs);
+ gWriteWaiting--;
+ }
+ // -- write lock entered
+
+ last = gCounter;
+ gCounter++;
+
+ spin (aWorkDelay);
+
+ PR_ASSERT (gCounter == (last + 1)); // test invariance
+
+ // -- exit write lock
+// if (0 < gReadWaiting) // notify waiting reads (do it anyway to show off the CondWait bug)
+ PR_NotifyAll (gMonitor);
+
+ PR_ExitMonitor (gMonitor);
+ // -- write lock exited
+
+ spin (aWaitDelay);
+ }
+}
+
+void doReadThread (void* arg)
+{
+ PRInt32 last;
+ Arg_t *args = (Arg_t*)arg;
+ PRInt32 aWorkDelay = args->a, aWaitDelay = args->b;
+ PR_Sleep(0);
+
+ while (1)
+ {
+ // -- enter read lock
+ PR_EnterMonitor (gMonitor);
+
+ if (0 < gWriteWaiting) // give up the monitor to waiting writes
+ {
+ PRIntervalTime fiveSecs = PR_SecondsToInterval(5);
+
+ gReadWaiting++;
+ if (gReadWaiting > gMaxReadWaits) // stats
+ gMaxReadWaits = gReadWaiting;
+ while (0 < gWriteWaiting)
+ PR_Wait (gMonitor, fiveSecs);
+ gReadWaiting--;
+ }
+
+ gReading++;
+
+ gReads++; // stats
+ if (gReading > gMaxReads) // stats
+ gMaxReads = gReading;
+
+ PR_ExitMonitor (gMonitor);
+ // -- read lock entered
+
+ last = gCounter;
+
+ spin (aWorkDelay);
+
+ PR_ASSERT (gCounter == last); // test invariance
+
+ // -- exit read lock
+ PR_EnterMonitor (gMonitor); // read unlock
+ gReading--;
+
+// if ((0 == gReading) && (0 < gWriteWaiting)) // notify waiting writes (do it anyway to show off the CondWait bug)
+ PR_NotifyAll (gMonitor);
+ PR_ExitMonitor (gMonitor);
+ // -- read lock exited
+
+ spin (aWaitDelay);
+ }
+}
+
+
+void fireThread (
+ char* aName, void (*aProc)(void *arg), Arg_t *aArg)
+{
+ PRThread *thread = PR_CreateThread(
+ PR_USER_THREAD, aProc, aArg, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, 0);
+}
+
+int pseudoMain (int argc, char** argv, char *pad)
+{
+ PRInt32 lastWriteCount = gCounter;
+ PRInt32 lastReadCount = gReads;
+ Arg_t a1 = {500, 250};
+ Arg_t a2 = {500, 500};
+ Arg_t a3 = {250, 500};
+ Arg_t a4 = {750, 250};
+ Arg_t a5 = {100, 750};
+ Arg_t a6 = {100, 500};
+ Arg_t a7 = {100, 750};
+
+ gMonitor = PR_NewMonitor ();
+
+ fireThread ("R1", doReadThread, &a1);
+ fireThread ("R2", doReadThread, &a2);
+ fireThread ("R3", doReadThread, &a3);
+ fireThread ("R4", doReadThread, &a4);
+
+ fireThread ("W1", doWriteThread, &a5);
+ fireThread ("W2", doWriteThread, &a6);
+ fireThread ("W3", doWriteThread, &a7);
+
+ fireThread ("R5", doReadThread, &a1);
+ fireThread ("R6", doReadThread, &a2);
+ fireThread ("R7", doReadThread, &a3);
+ fireThread ("R8", doReadThread, &a4);
+
+ fireThread ("W4", doWriteThread, &a5);
+ fireThread ("W5", doWriteThread, &a6);
+ fireThread ("W6", doWriteThread, &a7);
+
+ while (1)
+ {
+ PRInt32 writeCount, readCount;
+ PRIntervalTime fiveSecs = PR_SecondsToInterval(5);
+ PR_Sleep (fiveSecs); // get out of the way
+
+ // print some stats, not threadsafe, informative only
+ writeCount = gCounter;
+ readCount = gReads;
+ printf ("\ntick %d writes (+%d), %d reads (+%d) [max %d, %d, %d]",
+ writeCount, writeCount - lastWriteCount,
+ readCount, readCount - lastReadCount,
+ gMaxReads, gMaxWriteWaits, gMaxReadWaits);
+ lastWriteCount = writeCount;
+ lastReadCount = readCount;
+ gMaxReads = gMaxWriteWaits = gMaxReadWaits = 0;
+ }
+ return 0;
+}
+
+
+static void padStack (int argc, char** argv)
+{
+ char pad[512]; /* Work around bug in nspr on windoze */
+ pseudoMain (argc, argv, pad);
+}
+
+void main (int argc, char **argv)
+{
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+ padStack (argc, argv);
+}
+
+
+/* bug1test.c */
diff --git a/pr/tests/cleanup.c b/pr/tests/cleanup.c
new file mode 100644
index 00000000..8200ba48
--- /dev/null
+++ b/pr/tests/cleanup.c
@@ -0,0 +1,112 @@
+/* -*- 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 "prprf.h"
+#include "prio.h"
+#include "prinit.h"
+#include "prthread.h"
+#include "prinrval.h"
+
+#include "plgetopt.h"
+
+#include <stdlib.h>
+
+static void PR_CALLBACK Thread(void *sleep)
+{
+ PR_Sleep(PR_SecondsToInterval((PRUint32)sleep));
+ printf("Thread exiting\n");
+}
+
+static void Help(void)
+{
+ PRFileDesc *err = PR_GetSpecialFD(PR_StandardError);
+ PR_fprintf(err, "Cleanup usage: [-g] [-s n] [-t n] [-c n] [-h]\n");
+ PR_fprintf(err, "\t-c Call cleanup before exiting (default: false)\n");
+ PR_fprintf(err, "\t-G Use global threads only (default: local)\n");
+ PR_fprintf(err, "\t-t n Number of threads involved (default: 1)\n");
+ PR_fprintf(err, "\t-s n Seconds thread(s) should dally (defaut: 10)\n");
+ PR_fprintf(err, "\t-S n Seconds main() should dally (defaut: 5)\n");
+ PR_fprintf(err, "\t-C n Value to set concurrency (default 1)\n");
+ PR_fprintf(err, "\t-h This message and nothing else\n");
+} /* Help */
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PLOptStatus os;
+ PRBool cleanup = PR_FALSE;
+ PRThreadScope type = PR_LOCAL_THREAD;
+ PRFileDesc *err = PR_GetSpecialFD(PR_StandardError);
+ PLOptState *opt = PL_CreateOptState(argc, argv, "Ghs:S:t:cC:");
+ PRIntn concurrency = 1, child_sleep = 10, main_sleep = 5, threads = 1;
+
+ PR_STDIO_INIT();
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'c': /* call PR_Cleanup() before exiting */
+ cleanup = PR_TRUE;
+ break;
+ case 'G': /* local vs global threads */
+ type = PR_GLOBAL_THREAD;
+ break;
+ case 's': /* time to sleep */
+ child_sleep = atoi(opt->value);
+ break;
+ case 'S': /* time to sleep */
+ main_sleep = atoi(opt->value);
+ break;
+ case 'C': /* number of cpus to create */
+ concurrency = atoi(opt->value);
+ break;
+ case 't': /* number of threads to create */
+ threads = atoi(opt->value);
+ break;
+ case 'h': /* user wants some guidance */
+ Help(); /* so give him an earful */
+ return 2; /* but not a lot else */
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_fprintf(err, "Cleanup settings\n");
+ PR_fprintf(err, "\tThread type: %s\n",
+ (PR_LOCAL_THREAD == type) ? "LOCAL" : "GLOBAL");
+ PR_fprintf(err, "\tConcurrency: %d\n", concurrency);
+ PR_fprintf(err, "\tNumber of threads: %d\n", threads);
+ PR_fprintf(err, "\tThread sleep: %d\n", child_sleep);
+ PR_fprintf(err, "\tMain sleep: %d\n", main_sleep);
+ PR_fprintf(err, "\tCleanup will %sbe called\n\n", (cleanup) ? "" : "NOT ");
+
+ PR_SetConcurrency(concurrency);
+
+ while (threads-- > 0)
+ (void)PR_CreateThread(
+ PR_USER_THREAD, Thread, (void*)child_sleep, PR_PRIORITY_NORMAL,
+ type, PR_UNJOINABLE_THREAD, 0);
+ PR_Sleep(PR_SecondsToInterval(main_sleep));
+
+ if (cleanup) PR_Cleanup();
+
+ PR_fprintf(err, "main() exiting\n");
+ return 0;
+} /* main */
diff --git a/pr/tests/cltsrv.c b/pr/tests/cltsrv.c
new file mode 100644
index 00000000..a66076de
--- /dev/null
+++ b/pr/tests/cltsrv.c
@@ -0,0 +1,1197 @@
+/* -*- 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.
+ */
+
+/*
+ *
+ * Notes:
+ * [1] lth. The call to Sleep() is a hack to get the test case to run
+ * on Windows 95. Without it, the test case fails with an error
+ * WSAECONNRESET following a recv() call. The error is caused by the
+ * server side thread termination without a shutdown() or closesocket()
+ * call. Windows docmunentation suggests that this is predicted
+ * behavior; that other platforms get away with it is ... serindipity.
+ * The test case should shutdown() or closesocket() before
+ * thread termination. I didn't have time to figure out where or how
+ * to do it. The Sleep() call inserts enough delay to allow the
+ * client side to recv() all his data before the server side thread
+ * terminates. Whew! ...
+ *
+ ** Modification History:
+ * 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+ * The debug mode will print all of the printfs associated with this test.
+ * The regress mode will be the default mode. Since the regress tool limits
+ * the output to a one line status:PASS or FAIL,all of the printf statements
+ * have been handled with an if (debug_mode) statement.
+ */
+
+#include "prclist.h"
+#include "prcvar.h"
+#include "prerror.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prio.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prtime.h"
+#include "prmem.h"
+#include "prnetdb.h"
+#include "prprf.h"
+#include "prthread.h"
+
+#include "pprio.h"
+#include "primpl.h"
+
+#include "plstr.h"
+#include "plerror.h"
+#include "plgetopt.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+
+#if defined(XP_UNIX)
+#include <math.h>
+#endif
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+#endif
+
+/*
+** This is the beginning of the test
+*/
+
+#define RECV_FLAGS 0
+#define SEND_FLAGS 0
+#define DEFAULT_LOW 0
+#define DEFAULT_HIGH 0
+#define BUFFER_SIZE 1024
+#define DEFAULT_BACKLOG 5
+#define DEFAULT_PORT 12848
+#define DEFAULT_CLIENTS 1
+#define ALLOWED_IN_ACCEPT 1
+#define DEFAULT_CLIPPING 1000
+#define DEFAULT_WORKERS_MIN 1
+#define DEFAULT_WORKERS_MAX 1
+#define DEFAULT_SERVER "localhost"
+#define DEFAULT_EXECUTION_TIME 10
+#define DEFAULT_CLIENT_TIMEOUT 4000
+#define DEFAULT_SERVER_TIMEOUT 4000
+#define DEFAULT_SERVER_PRIORITY PR_PRIORITY_HIGH
+
+typedef enum CSState_e {cs_init, cs_run, cs_stop, cs_exit} CSState_t;
+
+static void PR_CALLBACK Worker(void *arg);
+typedef struct CSPool_s CSPool_t;
+typedef struct CSWorker_s CSWorker_t;
+typedef struct CSServer_s CSServer_t;
+typedef enum Verbosity
+{
+ TEST_LOG_ALWAYS,
+ TEST_LOG_ERROR,
+ TEST_LOG_WARNING,
+ TEST_LOG_NOTICE,
+ TEST_LOG_INFO,
+ TEST_LOG_STATUS,
+ TEST_LOG_VERBOSE
+} Verbosity;
+
+static PRInt32 domain = AF_INET;
+static PRInt32 protocol = 6; /* TCP */
+static PRFileDesc *debug_out = NULL;
+static PRBool debug_mode = PR_FALSE;
+static PRBool pthread_stats = PR_FALSE;
+static Verbosity verbosity = TEST_LOG_ALWAYS;
+static PRThreadScope thread_scope = PR_LOCAL_THREAD;
+
+struct CSWorker_s
+{
+ PRCList element; /* list of the server's workers */
+
+ PRThread *thread; /* this worker objects thread */
+ CSServer_t *server; /* back pointer to server structure */
+};
+
+struct CSPool_s
+{
+ PRCondVar *exiting;
+ PRCondVar *acceptComplete;
+ PRUint32 accepting, active, workers;
+};
+
+struct CSServer_s
+{
+ PRCList list; /* head of worker list */
+
+ PRLock *ml;
+ PRThread *thread; /* the main server thread */
+ PRCondVar *stateChange;
+
+ PRUint16 port; /* port we're listening on */
+ PRUint32 backlog; /* size of our listener backlog */
+ PRFileDesc *listener; /* the fd accepting connections */
+
+ CSPool_t pool; /* statistics on worker threads */
+ CSState_t state; /* the server's state */
+ struct /* controlling worker counts */
+ {
+ PRUint32 minimum, maximum, accepting;
+ } workers;
+
+ /* statistics */
+ PRIntervalTime started, stopped;
+ PRUint32 operations, bytesTransferred;
+};
+
+typedef struct CSDescriptor_s
+{
+ PRInt32 size; /* size of transfer */
+ char filename[60]; /* filename, null padded */
+} CSDescriptor_t;
+
+typedef struct CSClient_s
+{
+ PRLock *ml;
+ PRThread *thread;
+ PRCondVar *stateChange;
+ PRNetAddr serverAddress;
+
+ CSState_t state;
+
+ /* statistics */
+ PRIntervalTime started, stopped;
+ PRUint32 operations, bytesTransferred;
+} CSClient_t;
+
+#define TEST_LOG(l, p, a) \
+ do { \
+ if (debug_mode || (p <= verbosity)) printf a; \
+ } while (0)
+
+PRLogModuleInfo *cltsrv_log_file = NULL;
+
+#define MY_ASSERT(_expr) \
+ ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__))
+
+#define TEST_ASSERT(_expr) \
+ ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__))
+
+static void _MY_Assert(const char *s, const char *file, PRIntn ln)
+{
+ PL_PrintError(NULL);
+#if DEBUG
+ PR_Assert(s, file, ln);
+#endif
+} /* _MW_Assert */
+
+static PRBool Aborted(PRStatus rv)
+{
+ return ((PR_FAILURE == rv) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) ?
+ PR_TRUE : PR_FALSE;
+}
+
+static void TimeOfDayMessage(const char *msg, PRThread* me)
+{
+ char buffer[100];
+ PRExplodedTime tod;
+ PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &tod);
+ (void)PR_FormatTime(buffer, sizeof(buffer), "%T", &tod);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("%s(0x%p): %s\n", msg, me, buffer));
+} /* TimeOfDayMessage */
+
+
+static void PR_CALLBACK Client(void *arg)
+{
+ PRStatus rv;
+ PRIntn index;
+ char buffer[1024];
+ PRFileDesc *fd = NULL;
+ PRUintn clipping = DEFAULT_CLIPPING;
+ PRThread *me = PR_CurrentThread();
+ CSClient_t *client = (CSClient_t*)arg;
+ CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t);
+ PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_CLIENT_TIMEOUT);
+
+
+ for (index = 0; index < sizeof(buffer); ++index)
+ buffer[index] = (char)index;
+
+ client->started = PR_IntervalNow();
+
+ PR_Lock(client->ml);
+ client->state = cs_run;
+ PR_NotifyCondVar(client->stateChange);
+ PR_Unlock(client->ml);
+
+ TimeOfDayMessage("Client started at", me);
+
+ while (cs_run == client->state)
+ {
+ PRInt32 bytes, descbytes, filebytes, netbytes;
+
+ (void)PR_NetAddrToString(&client->serverAddress, buffer, sizeof(buffer));
+ TEST_LOG(cltsrv_log_file, TEST_LOG_INFO,
+ ("\tClient(0x%p): connecting to server at %s\n", me, buffer));
+
+ fd = PR_Socket(domain, SOCK_STREAM, protocol);
+ TEST_ASSERT(NULL != fd);
+ rv = PR_Connect(fd, &client->serverAddress, timeout);
+ if (PR_FAILURE == rv)
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): conection failed\n", me));
+ goto aborted;
+ }
+
+ memset(descriptor, 0, sizeof(*descriptor));
+ descriptor->size = PR_htonl(descbytes = rand() % clipping);
+ PR_snprintf(
+ descriptor->filename, sizeof(descriptor->filename),
+ "CS%p%p-%p.dat", client->started, me, client->operations);
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tClient(0x%p): sending descriptor for %u bytes\n", me, descbytes));
+ bytes = PR_Send(
+ fd, descriptor, sizeof(*descriptor), SEND_FLAGS, timeout);
+ if (sizeof(CSDescriptor_t) != bytes)
+ {
+ if (Aborted(PR_FAILURE)) goto aborted;
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): send descriptor timeout\n", me));
+ goto retry;
+ }
+ }
+ TEST_ASSERT(sizeof(*descriptor) == bytes);
+
+ netbytes = 0;
+ while (netbytes < descbytes)
+ {
+ filebytes = sizeof(buffer);
+ if ((descbytes - netbytes) < filebytes)
+ filebytes = descbytes - netbytes;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tClient(0x%p): sending %d bytes\n", me, filebytes));
+ bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout);
+ if (filebytes != bytes)
+ {
+ if (Aborted(PR_FAILURE)) goto aborted;
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): send data timeout\n", me));
+ goto retry;
+ }
+ }
+ TEST_ASSERT(bytes == filebytes);
+ netbytes += bytes;
+ }
+ filebytes = 0;
+ while (filebytes < descbytes)
+ {
+ netbytes = sizeof(buffer);
+ if ((descbytes - filebytes) < netbytes)
+ netbytes = descbytes - filebytes;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tClient(0x%p): receiving %d bytes\n", me, netbytes));
+ bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout);
+ if (-1 == bytes)
+ {
+ if (Aborted(PR_FAILURE))
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): receive data aborted\n", me));
+ goto aborted;
+ }
+ else if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): receive data timeout\n", me));
+ else
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): receive error (%d, %d)\n",
+ me, PR_GetError(), PR_GetOSError()));
+ goto retry;
+ }
+ if (0 == bytes)
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tClient(0x%p): unexpected end of stream\n",
+ PR_CurrentThread()));
+ break;
+ }
+ filebytes += bytes;
+ }
+
+ rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
+ if (Aborted(rv)) goto aborted;
+ TEST_ASSERT(PR_SUCCESS == rv);
+retry:
+ (void)PR_Close(fd); fd = NULL;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_INFO,
+ ("\tClient(0x%p): disconnected from server\n", me));
+
+ PR_Lock(client->ml);
+ client->operations += 1;
+ client->bytesTransferred += 2 * descbytes;
+ rv = PR_WaitCondVar(client->stateChange, rand() % clipping);
+ PR_Unlock(client->ml);
+ if (Aborted(rv)) break;
+ }
+
+aborted:
+ client->stopped = PR_IntervalNow();
+
+ PR_ClearInterrupt();
+ if (NULL != fd) rv = PR_Close(fd);
+
+ PR_Lock(client->ml);
+ client->state = cs_exit;
+ PR_NotifyCondVar(client->stateChange);
+ PR_Unlock(client->ml);
+ PR_DELETE(descriptor);
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("\tClient(0x%p): stopped after %u operations and %u bytes\n",
+ PR_CurrentThread(), client->operations, client->bytesTransferred));
+
+} /* Client */
+
+static PRStatus ProcessRequest(PRFileDesc *fd, CSServer_t *server)
+{
+ PRStatus drv, rv;
+ char buffer[1024];
+ PRFileDesc *file = NULL;
+ PRThread * me = PR_CurrentThread();
+ PRInt32 bytes, descbytes, netbytes, filebytes = 0;
+ CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t);
+ PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_SERVER_TIMEOUT);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tProcessRequest(0x%p): receiving desciptor\n", me));
+ bytes = PR_Recv(
+ fd, descriptor, sizeof(*descriptor), RECV_FLAGS, timeout);
+ if (-1 == bytes)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) goto exit;
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tProcessRequest(0x%p): receive timeout\n", me));
+ }
+ goto exit;
+ }
+ if (0 == bytes)
+ {
+ rv = PR_FAILURE;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tProcessRequest(0x%p): unexpected end of file\n", me));
+ goto exit;
+ }
+ descbytes = PR_ntohl(descriptor->size);
+ TEST_ASSERT(sizeof(*descriptor) == bytes);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tProcessRequest(0x%p): read descriptor {%d, %s}\n",
+ me, descbytes, descriptor->filename));
+
+ file = PR_Open(
+ descriptor->filename, (PR_CREATE_FILE | PR_WRONLY), 0666);
+ if (NULL == file)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) goto aborted;
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tProcessRequest(0x%p): open file timeout\n", me));
+ goto aborted;
+ }
+ }
+ TEST_ASSERT(NULL != file);
+
+ filebytes = 0;
+ while (filebytes < descbytes)
+ {
+ netbytes = sizeof(buffer);
+ if ((descbytes - filebytes) < netbytes)
+ netbytes = descbytes - filebytes;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tProcessRequest(0x%p): receive %d bytes\n", me, netbytes));
+ bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout);
+ if (-1 == bytes)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) goto aborted;
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): receive data timeout\n", me));
+ goto aborted;
+ }
+ /*
+ * XXX: I got (PR_CONNECT_RESET_ERROR, ERROR_NETNAME_DELETED)
+ * on NT here. This is equivalent to ECONNRESET on Unix.
+ * -wtc
+ */
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_WARNING,
+ ("\t\tProcessRequest(0x%p): unexpected error (%d, %d)\n",
+ me, PR_GetError(), PR_GetOSError()));
+ goto aborted;
+ }
+ if(0 == bytes)
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_WARNING,
+ ("\t\tProcessRequest(0x%p): unexpected end of stream\n", me));
+ rv = PR_FAILURE;
+ goto aborted;
+ }
+ filebytes += bytes;
+ netbytes = bytes;
+ /* The byte count for PR_Write should be positive */
+ MY_ASSERT(netbytes > 0);
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tProcessRequest(0x%p): write %d bytes to file\n", me, netbytes));
+ bytes = PR_Write(file, buffer, netbytes);
+ if (netbytes != bytes)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) goto aborted;
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): write file timeout\n", me));
+ goto aborted;
+ }
+ }
+ TEST_ASSERT(bytes > 0);
+ }
+
+ PR_Lock(server->ml);
+ server->operations += 1;
+ server->bytesTransferred += filebytes;
+ PR_Unlock(server->ml);
+
+ rv = PR_Close(file);
+ if (Aborted(rv)) goto aborted;
+ TEST_ASSERT(PR_SUCCESS == rv);
+ file = NULL;
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tProcessRequest(0x%p): opening %s\n", me, descriptor->filename));
+ file = PR_Open(descriptor->filename, PR_RDONLY, 0);
+ if (NULL == file)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) goto aborted;
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): open file timeout\n",
+ PR_CurrentThread()));
+ goto aborted;
+ }
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): other file open error (%u, %u)\n",
+ me, PR_GetError(), PR_GetOSError()));
+ goto aborted;
+ }
+ TEST_ASSERT(NULL != file);
+
+ netbytes = 0;
+ while (netbytes < descbytes)
+ {
+ filebytes = sizeof(buffer);
+ if ((descbytes - netbytes) < filebytes)
+ filebytes = descbytes - netbytes;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tProcessRequest(0x%p): read %d bytes from file\n", me, filebytes));
+ bytes = PR_Read(file, buffer, filebytes);
+ if (filebytes != bytes)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) goto aborted;
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): read file timeout\n", me));
+ else
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): other file error (%d, %d)\n",
+ me, PR_GetError(), PR_GetOSError()));
+ goto aborted;
+ }
+ TEST_ASSERT(bytes > 0);
+ netbytes += bytes;
+ filebytes = bytes;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tProcessRequest(0x%p): sending %d bytes\n", me, filebytes));
+ bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout);
+ if (filebytes != bytes)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) goto aborted;
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): send data timeout\n", me));
+ goto aborted;
+ }
+ break;
+ }
+ TEST_ASSERT(bytes > 0);
+ }
+
+ PR_Lock(server->ml);
+ server->bytesTransferred += filebytes;
+ PR_Unlock(server->ml);
+
+ rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
+ if (Aborted(rv)) goto aborted;
+
+ rv = PR_Close(file);
+ if (Aborted(rv)) goto aborted;
+ TEST_ASSERT(PR_SUCCESS == rv);
+ file = NULL;
+
+aborted:
+ PR_ClearInterrupt();
+ if (NULL != file) PR_Close(file);
+ drv = PR_Delete(descriptor->filename);
+ TEST_ASSERT(PR_SUCCESS == drv);
+exit:
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tProcessRequest(0x%p): Finished\n", me));
+
+ PR_DELETE(descriptor);
+
+#if defined(WIN95)
+ PR_Sleep(PR_MillisecondsToInterval(200)); /* lth. see note [1] */
+#endif
+ return rv;
+} /* ProcessRequest */
+
+static PRStatus CreateWorker(CSServer_t *server, CSPool_t *pool)
+{
+ CSWorker_t *worker = PR_NEWZAP(CSWorker_t);
+ worker->server = server;
+ PR_INIT_CLIST(&worker->element);
+ worker->thread = PR_CreateThread(
+ PR_USER_THREAD, Worker, worker,
+ DEFAULT_SERVER_PRIORITY, thread_scope,
+ PR_UNJOINABLE_THREAD, 0);
+ if (NULL == worker->thread)
+ {
+ PR_DELETE(worker);
+ return PR_FAILURE;
+ }
+
+ TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS,
+ ("\tCreateWorker(0x%p): create new worker (0x%p)\n",
+ PR_CurrentThread(), worker->thread));
+
+ return PR_SUCCESS;
+} /* CreateWorker */
+
+static void PR_CALLBACK Worker(void *arg)
+{
+ PRStatus rv;
+ PRNetAddr from;
+ PRFileDesc *fd = NULL;
+ PRThread *me = PR_CurrentThread();
+ CSWorker_t *worker = (CSWorker_t*)arg;
+ CSServer_t *server = worker->server;
+ CSPool_t *pool = &server->pool;
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("\t\tWorker(0x%p): started [%u]\n", me, pool->workers + 1));
+
+ PR_Lock(server->ml);
+ PR_APPEND_LINK(&worker->element, &server->list);
+ pool->workers += 1; /* define our existance */
+
+ while (cs_run == server->state)
+ {
+ while (pool->accepting >= server->workers.accepting)
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tWorker(0x%p): waiting for accept slot[%d]\n",
+ me, pool->accepting));
+ rv = PR_WaitCondVar(pool->acceptComplete, PR_INTERVAL_NO_TIMEOUT);
+ if (Aborted(rv) || (cs_run != server->state))
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("\tWorker(0x%p): has been %s\n",
+ me, (Aborted(rv) ? "interrupted" : "stopped")));
+ goto exit;
+ }
+ }
+ pool->accepting += 1; /* how many are really in accept */
+ PR_Unlock(server->ml);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tWorker(0x%p): calling accept\n", me));
+ fd = PR_Accept(server->listener, &from, PR_INTERVAL_NO_TIMEOUT);
+
+ PR_Lock(server->ml);
+ pool->accepting -= 1;
+ PR_NotifyCondVar(pool->acceptComplete);
+
+ if ((NULL == fd) && Aborted(PR_FAILURE))
+ {
+ if (NULL != server->listener)
+ {
+ PR_Close(server->listener);
+ server->listener = NULL;
+ }
+ goto exit;
+ }
+
+ if (NULL != fd)
+ {
+ /*
+ ** Create another worker of the total number of workers is
+ ** less than the minimum specified or we have none left in
+ ** accept() AND we're not over the maximum.
+ ** This sort of presumes that the number allowed in accept
+ ** is at least as many as the minimum. Otherwise we'll keep
+ ** creating new threads and deleting them soon after.
+ */
+ PRBool another =
+ ((pool->workers < server->workers.minimum) ||
+ ((0 == pool->accepting)
+ && (pool->workers < server->workers.maximum))) ?
+ PR_TRUE : PR_FALSE;
+ pool->active += 1;
+ PR_Unlock(server->ml);
+
+ if (another) (void)CreateWorker(server, pool);
+
+ rv = ProcessRequest(fd, server);
+ if (PR_SUCCESS != rv)
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tWorker(0x%p): server process ended abnormally\n", me));
+ (void)PR_Close(fd); fd = NULL;
+
+ PR_Lock(server->ml);
+ pool->active -= 1;
+ }
+ }
+
+exit:
+ PR_ClearInterrupt();
+ PR_Unlock(server->ml);
+
+ if (NULL != fd)
+ {
+ (void)PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
+ (void)PR_Close(fd);
+ }
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("\t\tWorker(0x%p): exiting [%u]\n", PR_CurrentThread(), pool->workers));
+
+ PR_Lock(server->ml);
+ pool->workers -= 1; /* undefine our existance */
+ PR_REMOVE_AND_INIT_LINK(&worker->element);
+ PR_NotifyCondVar(pool->exiting);
+ PR_Unlock(server->ml);
+
+ PR_DELETE(worker); /* destruction of the "worker" object */
+
+} /* Worker */
+
+static void PR_CALLBACK Server(void *arg)
+{
+ PRStatus rv;
+ PRNetAddr serverAddress;
+ PRThread *me = PR_CurrentThread();
+ CSServer_t *server = (CSServer_t*)arg;
+
+ server->listener = PR_Socket(domain, SOCK_STREAM, protocol);
+
+ rv = PR_InitializeNetAddr(PR_IpAddrAny, DEFAULT_PORT, &serverAddress);
+
+ rv = PR_Bind(server->listener, &serverAddress);
+ TEST_ASSERT(PR_SUCCESS == rv);
+
+ rv = PR_Listen(server->listener, server->backlog);
+ TEST_ASSERT(PR_SUCCESS == rv);
+
+ server->started = PR_IntervalNow();
+ TimeOfDayMessage("Server started at", me);
+
+ PR_Lock(server->ml);
+ server->state = cs_run;
+ PR_NotifyCondVar(server->stateChange);
+ PR_Unlock(server->ml);
+
+ /*
+ ** Create the first worker (actually, a thread that accepts
+ ** connections and then processes the work load as needed).
+ ** From this point on, additional worker threads are created
+ ** as they are needed by existing worker threads.
+ */
+ rv = CreateWorker(server, &server->pool);
+ TEST_ASSERT(PR_SUCCESS == rv);
+
+ /*
+ ** From here on this thread is merely hanging around as the contact
+ ** point for the main test driver. It's just waiting for the driver
+ ** to declare the test complete.
+ */
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tServer(0x%p): waiting for state change\n", me));
+
+ PR_Lock(server->ml);
+ while ((cs_run == server->state) && !Aborted(rv))
+ {
+ rv = PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(server->ml);
+ PR_ClearInterrupt();
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_INFO,
+ ("\tServer(0x%p): shutting down workers\n", me));
+
+ /*
+ ** Get all the worker threads to exit. They know how to
+ ** clean up after themselves, so this is just a matter of
+ ** waiting for clorine in the pool to take effect. During
+ ** this stage we're ignoring interrupts.
+ */
+ server->workers.minimum = server->workers.maximum = 0;
+
+ PR_Lock(server->ml);
+ while (!PR_CLIST_IS_EMPTY(&server->list))
+ {
+ PRCList *head = PR_LIST_HEAD(&server->list);
+ CSWorker_t *worker = (CSWorker_t*)head;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tServer(0x%p): interrupting worker(0x%p)\n", me, worker));
+ rv = PR_Interrupt(worker->thread);
+ TEST_ASSERT(PR_SUCCESS == rv);
+ PR_REMOVE_AND_INIT_LINK(head);
+ }
+
+ while (server->pool.workers > 0)
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("\tServer(0x%p): waiting for %u workers to exit\n",
+ me, server->pool.workers));
+ (void)PR_WaitCondVar(server->pool.exiting, PR_INTERVAL_NO_TIMEOUT);
+ }
+
+ server->state = cs_exit;
+ PR_NotifyCondVar(server->stateChange);
+ PR_Unlock(server->ml);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("\tServer(0x%p): stopped after %u operations and %u bytes\n",
+ me, server->operations, server->bytesTransferred));
+
+ if (NULL != server->listener) PR_Close(server->listener);
+ server->stopped = PR_IntervalNow();
+
+} /* Server */
+
+static void WaitForCompletion(PRIntn execution)
+{
+ while (execution > 0)
+ {
+ PRIntn dally = (execution > 30) ? 30 : execution;
+ PR_Sleep(PR_SecondsToInterval(dally));
+ if (pthread_stats) PT_FPrintStats(debug_out, "\nPThread Statistics\n");
+ execution -= dally;
+ }
+} /* WaitForCompletion */
+
+static void Help(void)
+{
+ PR_fprintf(debug_out, "cltsrv test program usage:\n");
+ PR_fprintf(debug_out, "\t-a <n> threads allowed in accept (5)\n");
+ PR_fprintf(debug_out, "\t-b <n> backlock for listen (5)\n");
+ PR_fprintf(debug_out, "\t-c <threads> number of clients to create (1)\n");
+ PR_fprintf(debug_out, "\t-f <low> low water mark for fd caching (0)\n");
+ PR_fprintf(debug_out, "\t-F <high> high water mark for fd caching (0)\n");
+ PR_fprintf(debug_out, "\t-w <threads> minimal number of server threads (1)\n");
+ PR_fprintf(debug_out, "\t-W <threads> maximum number of server threads (1)\n");
+ PR_fprintf(debug_out, "\t-e <seconds> duration of the test in seconds (10)\n");
+ PR_fprintf(debug_out, "\t-s <string> dsn name of server (localhost)\n");
+ PR_fprintf(debug_out, "\t-G use GLOBAL threads (LOCAL)\n");
+ PR_fprintf(debug_out, "\t-X use XTP as transport (TCP)\n");
+#ifdef _PR_INET6
+ PR_fprintf(debug_out, "\t-6 Use IPv6 (IPv4)\n");
+#endif /* _PR_INET6 */
+ PR_fprintf(debug_out, "\t-v verbosity (accumulative) (0)\n");
+ PR_fprintf(debug_out, "\t-p pthread statistics (FALSE)\n");
+ PR_fprintf(debug_out, "\t-d debug mode (FALSE)\n");
+ PR_fprintf(debug_out, "\t-h this message\n");
+} /* Help */
+
+static Verbosity IncrementVerbosity(void)
+{
+ PRIntn verboge = (PRIntn)verbosity + 1;
+ return (Verbosity)verboge;
+} /* IncrementVerbosity */
+
+PRIntn main(PRIntn argc, char** argv)
+{
+ PRUintn index;
+ PRBool boolean;
+ CSClient_t *client;
+ PRStatus rv, joinStatus;
+ CSServer_t *server = NULL;
+
+ PRUintn backlog = DEFAULT_BACKLOG;
+ PRUintn clients = DEFAULT_CLIENTS;
+ const char *serverName = DEFAULT_SERVER;
+ PRBool serverIsLocal = PR_TRUE;
+ PRUintn accepting = ALLOWED_IN_ACCEPT;
+ PRUintn workersMin = DEFAULT_WORKERS_MIN;
+ PRUintn workersMax = DEFAULT_WORKERS_MAX;
+ PRIntn execution = DEFAULT_EXECUTION_TIME;
+ PRIntn low = DEFAULT_LOW, high = DEFAULT_HIGH;
+
+ /*
+ * -G use global threads
+ * -a <n> threads allowed in accept
+ * -b <n> backlock for listen
+ * -c <threads> number of clients to create
+ * -f <low> low water mark for caching FDs
+ * -F <high> high water mark for caching FDs
+ * -w <threads> minimal number of server threads
+ * -W <threads> maximum number of server threads
+ * -e <seconds> duration of the test in seconds
+ * -s <string> dsn name of server (implies no server here)
+ * -v verbosity
+ */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "GX6b:a:c:f:F:w:W:e:s:vdhp");
+
+ debug_out = PR_GetSpecialFD(PR_StandardError);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'G': /* use global threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'X': /* use XTP as transport */
+ protocol = 36;
+ break;
+#ifdef _PR_INET6
+ case '6': /* Use IPv6 */
+ domain = AF_INET6;
+ PR_SetIPv6Enable(PR_TRUE);
+ break;
+#endif /* _PR_INET6 */
+ case 'a': /* the value for accepting */
+ accepting = atoi(opt->value);
+ break;
+ case 'b': /* the value for backlock */
+ backlog = atoi(opt->value);
+ break;
+ case 'c': /* number of client threads */
+ clients = atoi(opt->value);
+ break;
+ case 'f': /* low water fd cache */
+ low = atoi(opt->value);
+ break;
+ case 'F': /* low water fd cache */
+ high = atoi(opt->value);
+ break;
+ case 'w': /* minimum server worker threads */
+ workersMin = atoi(opt->value);
+ break;
+ case 'W': /* maximum server worker threads */
+ workersMax = atoi(opt->value);
+ break;
+ case 'e': /* program execution time in seconds */
+ execution = atoi(opt->value);
+ break;
+ case 's': /* server's address */
+ serverName = opt->value;
+ break;
+ case 'v': /* verbosity */
+ verbosity = IncrementVerbosity();
+ break;
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'p': /* pthread mode */
+ pthread_stats = PR_TRUE;
+ break;
+ case 'h':
+ default:
+ Help();
+ return 2;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (0 != PL_strcmp(serverName, DEFAULT_SERVER)) serverIsLocal = PR_FALSE;
+ if (0 == execution) execution = DEFAULT_EXECUTION_TIME;
+ if (0 == workersMax) workersMax = DEFAULT_WORKERS_MAX;
+ if (0 == workersMin) workersMin = DEFAULT_WORKERS_MIN;
+ if (0 == accepting) accepting = ALLOWED_IN_ACCEPT;
+ if (0 == backlog) backlog = DEFAULT_BACKLOG;
+
+ if (workersMin > accepting) accepting = workersMin;
+
+ PR_STDIO_INIT();
+ TimeOfDayMessage("Client/Server started at", PR_CurrentThread());
+
+ cltsrv_log_file = PR_NewLogModule("cltsrv_log");
+ MY_ASSERT(NULL != cltsrv_log_file);
+ boolean = PR_SetLogFile("cltsrv.log");
+ MY_ASSERT(boolean);
+
+#ifdef XP_MAC
+ debug_mode = PR_TRUE;
+#endif
+
+ rv = PR_SetFDCacheSize(low, high);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ if (serverIsLocal)
+ {
+ /* Establish the server */
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_INFO,
+ ("main(0x%p): starting server\n", PR_CurrentThread()));
+
+ server = PR_NEWZAP(CSServer_t);
+ PR_INIT_CLIST(&server->list);
+ server->state = cs_init;
+ server->ml = PR_NewLock();
+ server->backlog = backlog;
+ server->port = DEFAULT_PORT;
+ server->workers.minimum = workersMin;
+ server->workers.maximum = workersMax;
+ server->workers.accepting = accepting;
+ server->stateChange = PR_NewCondVar(server->ml);
+ server->pool.exiting = PR_NewCondVar(server->ml);
+ server->pool.acceptComplete = PR_NewCondVar(server->ml);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("main(0x%p): creating server thread\n", PR_CurrentThread()));
+
+ server->thread = PR_CreateThread(
+ PR_USER_THREAD, Server, server, PR_PRIORITY_HIGH,
+ thread_scope, PR_JOINABLE_THREAD, 0);
+ TEST_ASSERT(NULL != server->thread);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("main(0x%p): waiting for server init\n", PR_CurrentThread()));
+
+ PR_Lock(server->ml);
+ while (server->state == cs_init)
+ PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
+ PR_Unlock(server->ml);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("main(0x%p): server init complete (port #%d)\n",
+ PR_CurrentThread(), server->port));
+ }
+
+ if (clients != 0)
+ {
+ /* Create all of the clients */
+ PRHostEnt host;
+ char buffer[BUFFER_SIZE];
+ client = (CSClient_t*)PR_CALLOC(clients * sizeof(CSClient_t));
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("main(0x%p): creating %d client threads\n",
+ PR_CurrentThread(), clients));
+
+ if (!serverIsLocal)
+ {
+ rv = PR_GetHostByName(serverName, buffer, BUFFER_SIZE, &host);
+ if (PR_SUCCESS != rv)
+ {
+ PL_FPrintError(PR_STDERR, "PR_GetHostByName");
+ return 2;
+ }
+ }
+
+ for (index = 0; index < clients; ++index)
+ {
+ client[index].state = cs_init;
+ client[index].ml = PR_NewLock();
+ if (serverIsLocal)
+ {
+ (void)PR_InitializeNetAddr(
+ PR_IpAddrLoopback, DEFAULT_PORT,
+ &client[index].serverAddress);
+ }
+ else
+ {
+ (void)PR_EnumerateHostEnt(
+ 0, &host, DEFAULT_PORT, &client[index].serverAddress);
+ }
+ client[index].stateChange = PR_NewCondVar(client[index].ml);
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_INFO,
+ ("main(0x%p): creating client threads\n", PR_CurrentThread()));
+ client[index].thread = PR_CreateThread(
+ PR_USER_THREAD, Client, &client[index], PR_PRIORITY_NORMAL,
+ thread_scope, PR_JOINABLE_THREAD, 0);
+ TEST_ASSERT(NULL != client[index].thread);
+ PR_Lock(client[index].ml);
+ while (cs_init == client[index].state)
+ PR_WaitCondVar(client[index].stateChange, PR_INTERVAL_NO_TIMEOUT);
+ PR_Unlock(client[index].ml);
+ }
+ }
+
+ /* Then just let them go at it for a bit */
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("main(0x%p): waiting for execution interval (%d seconds)\n",
+ PR_CurrentThread(), execution));
+
+ WaitForCompletion(execution);
+
+ TimeOfDayMessage("Shutting down", PR_CurrentThread());
+
+ if (clients != 0)
+ {
+ for (index = 0; index < clients; ++index)
+ {
+ TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS,
+ ("main(0x%p): notifying client(0x%p) to stop\n",
+ PR_CurrentThread(), client[index].thread));
+
+ PR_Lock(client[index].ml);
+ if (cs_run == client[index].state)
+ {
+ client[index].state = cs_stop;
+ PR_Interrupt(client[index].thread);
+ while (cs_stop == client[index].state)
+ PR_WaitCondVar(
+ client[index].stateChange, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(client[index].ml);
+
+ TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("main(0x%p): joining client(0x%p)\n",
+ PR_CurrentThread(), client[index].thread));
+
+ joinStatus = PR_JoinThread(client[index].thread);
+ TEST_ASSERT(PR_SUCCESS == joinStatus);
+ PR_DestroyCondVar(client[index].stateChange);
+ PR_DestroyLock(client[index].ml);
+ }
+ PR_DELETE(client);
+ }
+
+ if (NULL != server)
+ {
+ /* All clients joined - retrieve the server */
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("main(0x%p): notifying server(0x%p) to stop\n",
+ PR_CurrentThread(), server->thread));
+
+ PR_Lock(server->ml);
+ server->state = cs_stop;
+ PR_Interrupt(server->thread);
+ while (cs_exit != server->state)
+ PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
+ PR_Unlock(server->ml);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("main(0x%p): joining server(0x%p)\n",
+ PR_CurrentThread(), server->thread));
+ joinStatus = PR_JoinThread(server->thread);
+ TEST_ASSERT(PR_SUCCESS == joinStatus);
+
+ PR_DestroyCondVar(server->stateChange);
+ PR_DestroyCondVar(server->pool.exiting);
+ PR_DestroyCondVar(server->pool.acceptComplete);
+ PR_DestroyLock(server->ml);
+ PR_DELETE(server);
+ }
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("main(0x%p): test complete\n", PR_CurrentThread()));
+
+ PT_FPrintStats(debug_out, "\nPThread Statistics\n");
+
+ TimeOfDayMessage("Test exiting at", PR_CurrentThread());
+ PR_Cleanup();
+ return 0;
+} /* main */
+
+/* cltsrv.c */
diff --git a/pr/tests/concur.c b/pr/tests/concur.c
new file mode 100644
index 00000000..fa2bfa1f
--- /dev/null
+++ b/pr/tests/concur.c
@@ -0,0 +1,173 @@
+/* -*- 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: concur.c
+** Description: test of adding and removing concurrency options
+*/
+
+#include "prcvar.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prlock.h"
+#include "prprf.h"
+#include "prmem.h"
+#include "prlog.h"
+
+#include "plgetopt.h"
+
+#if defined(XP_MAC)
+#include "pprio.h"
+#else
+#include "private/pprio.h"
+#endif
+
+#include <stdlib.h>
+
+#define DEFAULT_RANGE 10
+#define DEFAULT_LOOPS 100
+
+static PRThreadScope thread_scope = PR_LOCAL_THREAD;
+
+typedef struct Context
+{
+ PRLock *ml;
+ PRCondVar *cv;
+ PRIntn want, have;
+} Context;
+
+
+/*
+** Make the instance of 'context' static (not on the stack)
+** for Win16 threads
+*/
+static Context context = {NULL, NULL, 0, 0};
+
+static void PR_CALLBACK Dull(void *arg)
+{
+ Context *context = (Context*)arg;
+ PR_Lock(context->ml);
+ context->have += 1;
+ while (context->want >= context->have)
+ PR_WaitCondVar(context->cv, PR_INTERVAL_NO_TIMEOUT);
+ context->have -= 1;
+ PR_Unlock(context->ml);
+} /* Dull */
+
+PRIntn PR_CALLBACK Concur(PRIntn argc, char **argv)
+{
+ PRUintn cpus;
+ PLOptStatus os;
+ PRThread **threads;
+ PRBool debug = PR_FALSE;
+ PRUintn range = DEFAULT_RANGE;
+ PRStatus rc;
+ PRUintn cnt;
+ PRUintn loops = DEFAULT_LOOPS;
+ PRIntervalTime hundredMills = PR_MillisecondsToInterval(100);
+ PLOptState *opt = PL_CreateOptState(argc, argv, "Gdl:r:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'G': /* GLOBAL threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'd': /* debug mode */
+ debug = PR_TRUE;
+ break;
+ case 'r': /* range limit */
+ range = atoi(opt->value);
+ break;
+ case 'l': /* loop counter */
+ loops = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (0 == range) range = DEFAULT_RANGE;
+ if (0 == loops) loops = DEFAULT_LOOPS;
+
+ context.ml = PR_NewLock();
+ context.cv = PR_NewCondVar(context.ml);
+
+ if (debug)
+ PR_fprintf(
+ PR_STDERR, "Testing with %d CPUs and %d interations\n", range, loops);
+
+ threads = (PRThread**) PR_CALLOC(sizeof(PRThread*) * range);
+ while (--loops > 0)
+ {
+ for (cpus = 1; cpus <= range; ++cpus)
+ {
+ PR_SetConcurrency(cpus);
+ context.want = cpus;
+
+ threads[cpus - 1] = PR_CreateThread(
+ PR_USER_THREAD, Dull, &context, PR_PRIORITY_NORMAL,
+ thread_scope, PR_JOINABLE_THREAD, 0);
+ }
+
+ PR_Sleep(hundredMills);
+
+ for (cpus = range; cpus > 0; cpus--)
+ {
+ PR_SetConcurrency(cpus);
+ context.want = cpus - 1;
+
+ PR_Lock(context.ml);
+ PR_NotifyCondVar(context.cv);
+ PR_Unlock(context.ml);
+ }
+ for(cnt = 0; cnt < range; cnt++) {
+ rc = PR_JoinThread(threads[cnt]);
+ PR_ASSERT(rc == PR_SUCCESS);
+ }
+ }
+
+
+ if (debug)
+ PR_fprintf(
+ PR_STDERR, "Waiting for %d thread(s) to exit\n", context.have);
+
+ while (context.have > 0) PR_Sleep(hundredMills);
+
+ if (debug)
+ PR_fprintf(
+ PR_STDERR, "Finished [want: %d, have: %d]\n",
+ context.want, context.have);
+
+ PR_DestroyLock(context.ml);
+ PR_DestroyCondVar(context.cv);
+
+ PR_fprintf(PR_STDERR, "PASSED\n");
+
+ return 0;
+} /* Concur */
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PR_STDIO_INIT();
+ return PR_Initialize(Concur, argc, argv, 0);
+} /* main */
+
+/* concur.c */
diff --git a/pr/tests/cvar.c b/pr/tests/cvar.c
new file mode 100644
index 00000000..4238a32e
--- /dev/null
+++ b/pr/tests/cvar.c
@@ -0,0 +1,315 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+** 1996 - Netscape Communications Corporation
+**
+** Name: cvar.c
+**
+** Description: Tests Condition Variable Operations
+**
+** Modification History:
+** 13-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+** 12-June-97 Revert to return code 0 and 1.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+
+#include "nspr.h"
+
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+#endif
+
+PRMonitor *mon;
+#define DEFAULT_COUNT 1000
+PRInt32 count = 0;
+PRIntn debug_mode;
+
+#define kQSIZE 1
+
+typedef struct {
+ PRLock *bufLock;
+ int startIdx;
+ int numFull;
+ PRCondVar *notFull;
+ PRCondVar *notEmpty;
+ void *data[kQSIZE];
+} CircBuf;
+
+static PRBool failed = PR_FALSE;
+
+/*
+** NewCB creates and initializes a new circular buffer.
+*/
+static CircBuf* NewCB(void)
+{
+ CircBuf *cbp;
+
+ cbp = PR_NEW(CircBuf);
+ if (cbp == NULL)
+ return (NULL);
+
+ cbp->bufLock = PR_NewLock();
+ cbp->startIdx = 0;
+ cbp->numFull = 0;
+ cbp->notFull = PR_NewCondVar(cbp->bufLock);
+ cbp->notEmpty = PR_NewCondVar(cbp->bufLock);
+
+ return (cbp);
+}
+
+/*
+** DeleteCB frees a circular buffer.
+*/
+static void DeleteCB(CircBuf *cbp)
+{
+ PR_DestroyLock(cbp->bufLock);
+ PR_DestroyCondVar(cbp->notFull);
+ PR_DestroyCondVar(cbp->notEmpty);
+ PR_DELETE(cbp);
+}
+
+
+/*
+** PutCBData puts new data on the queue. If the queue is full, it waits
+** until there is room.
+*/
+static void PutCBData(CircBuf *cbp, void *data)
+{
+ PR_Lock(cbp->bufLock);
+ /* wait while the buffer is full */
+ while (cbp->numFull == kQSIZE)
+ PR_WaitCondVar(cbp->notFull,PR_INTERVAL_NO_TIMEOUT);
+ cbp->data[(cbp->startIdx + cbp->numFull) % kQSIZE] = data;
+ cbp->numFull += 1;
+
+ /* let a waiting reader know that there is data */
+ PR_NotifyCondVar(cbp->notEmpty);
+ PR_Unlock(cbp->bufLock);
+
+}
+
+
+/*
+** GetCBData gets the oldest data on the queue. If the queue is empty, it waits
+** until new data appears.
+*/
+static void* GetCBData(CircBuf *cbp)
+{
+ void *data;
+
+ PR_Lock(cbp->bufLock);
+ /* wait while the buffer is empty */
+ while (cbp->numFull == 0)
+ PR_WaitCondVar(cbp->notEmpty,PR_INTERVAL_NO_TIMEOUT);
+ data = cbp->data[cbp->startIdx];
+ cbp->startIdx =(cbp->startIdx + 1) % kQSIZE;
+ cbp->numFull -= 1;
+
+ /* let a waiting writer know that there is room */
+ PR_NotifyCondVar(cbp->notFull);
+ PR_Unlock(cbp->bufLock);
+
+ return (data);
+}
+
+
+/************************************************************************/
+
+static int alive;
+
+static void PR_CALLBACK CXReader(void *arg)
+{
+ CircBuf *cbp = (CircBuf *)arg;
+ PRInt32 i, n;
+ void *data;
+
+ n = count / 2;
+ for (i = 0; i < n; i++) {
+ data = GetCBData(cbp);
+ if ((int)data != i)
+ if (debug_mode) printf("data mismatch at for i = %d usec\n", i);
+ }
+
+ PR_EnterMonitor(mon);
+ --alive;
+ PR_Notify(mon);
+ PR_ExitMonitor(mon);
+}
+
+static void PR_CALLBACK CXWriter(void *arg)
+{
+ CircBuf *cbp = (CircBuf *)arg;
+ PRInt32 i, n;
+
+ n = count / 2;
+ for (i = 0; i < n; i++)
+ PutCBData(cbp, (void *)i);
+
+ PR_EnterMonitor(mon);
+ --alive;
+ PR_Notify(mon);
+ PR_ExitMonitor(mon);
+}
+
+static void CondWaitContextSwitch(PRThreadScope scope1, PRThreadScope scope2)
+{
+ PRThread *t1, *t2;
+ CircBuf *cbp;
+
+ PR_EnterMonitor(mon);
+
+ alive = 2;
+
+ cbp = NewCB();
+
+ t1 = PR_CreateThread(PR_USER_THREAD,
+ CXReader, cbp,
+ PR_PRIORITY_NORMAL,
+ scope1,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ PR_ASSERT(t1);
+ t2 = PR_CreateThread(PR_USER_THREAD,
+ CXWriter, cbp,
+ PR_PRIORITY_NORMAL,
+ scope2,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ PR_ASSERT(t2);
+
+ /* Wait for both of the threads to exit */
+ while (alive) {
+ PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
+ }
+
+ DeleteCB(cbp);
+
+ PR_ExitMonitor(mon);
+}
+
+static void CondWaitContextSwitchUU(void)
+{
+ CondWaitContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
+}
+
+static void CondWaitContextSwitchUK(void)
+{
+ CondWaitContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
+}
+
+static void CondWaitContextSwitchKK(void)
+{
+ CondWaitContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
+}
+
+/************************************************************************/
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+
+ if (debug_mode) printf("%40s: %6.2f usec\n", msg, d / count);
+
+ if (0 == d) failed = PR_TRUE;
+}
+
+static PRIntn PR_CALLBACK RealMain(int argc, char **argv)
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name [-d] [-c n]
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dc:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ case 'c': /* loop count */
+ count = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (0 == count) count = DEFAULT_COUNT;
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("cvar.log");
+ debug_mode = 1;
+#endif
+
+ mon = PR_NewMonitor();
+
+ Measure(CondWaitContextSwitchUU, "cond var wait context switch- user/user");
+ Measure(CondWaitContextSwitchUK, "cond var wait context switch- user/kernel");
+ Measure(CondWaitContextSwitchKK, "cond var wait context switch- kernel/kernel");
+
+ PR_DestroyMonitor(mon);
+
+ if (debug_mode) printf("%s\n", (failed) ? "FAILED" : "PASSED");
+
+ if(failed)
+ return 1;
+ else
+ return 0;
+}
+
+
+PRIntn main(PRIntn argc, char *argv[])
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
diff --git a/pr/tests/cvar2.c b/pr/tests/cvar2.c
new file mode 100644
index 00000000..f2508a7b
--- /dev/null
+++ b/pr/tests/cvar2.c
@@ -0,0 +1,989 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+** 1996 - Netscape Communications Corporation
+**
+** Name: cvar2.c
+**
+** Description: Simple test creates several local and global threads;
+** half use a single,shared condvar, and the
+** other half have their own condvar. The main thread then loops
+** notifying them to wakeup.
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+***********************************************************************/
+
+#include "nspr.h"
+#include "plerror.h"
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int _debug_on = 0;
+#define DPRINTF(arg) if (_debug_on) printf arg
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+#endif
+
+#define DEFAULT_COUNT 100
+#define DEFAULT_THREADS 5
+PRInt32 count = DEFAULT_COUNT;
+
+typedef struct threadinfo {
+ PRThread *thread;
+ PRInt32 id;
+ PRBool internal;
+ PRInt32 *tcount;
+ PRLock *lock;
+ PRCondVar *cvar;
+ PRIntervalTime timeout;
+ PRInt32 loops;
+
+ PRLock *exitlock;
+ PRCondVar *exitcvar;
+ PRInt32 *exitcount;
+} threadinfo;
+
+/*
+** Make exitcount, tcount static. for Win16.
+*/
+static PRInt32 exitcount=0;
+static PRInt32 tcount=0;
+
+
+/* Thread that gets notified; many threads share the same condvar */
+void PR_CALLBACK
+SharedCondVarThread(void *_info)
+{
+ threadinfo *info = (threadinfo *)_info;
+ PRInt32 index;
+
+ for (index=0; index<info->loops; index++) {
+ PR_Lock(info->lock);
+ if (*info->tcount == 0)
+ PR_WaitCondVar(info->cvar, info->timeout);
+#if 0
+ printf("shared thread %ld notified in loop %ld\n", info->id, index);
+#endif
+ (*info->tcount)--;
+ PR_Unlock(info->lock);
+
+ PR_Lock(info->exitlock);
+ (*info->exitcount)++;
+ PR_NotifyCondVar(info->exitcvar);
+ PR_Unlock(info->exitlock);
+ }
+#if 0
+ printf("shared thread %ld terminating\n", info->id);
+#endif
+}
+
+/* Thread that gets notified; no other threads use the same condvar */
+void PR_CALLBACK
+PrivateCondVarThread(void *_info)
+{
+ threadinfo *info = (threadinfo *)_info;
+ PRInt32 index;
+
+ for (index=0; index<info->loops; index++) {
+ PR_Lock(info->lock);
+ if (*info->tcount == 0) {
+ DPRINTF(("PrivateCondVarThread: thread 0x%lx waiting on cvar = 0x%lx\n",
+ PR_GetCurrentThread(), info->cvar));
+ PR_WaitCondVar(info->cvar, info->timeout);
+ }
+#if 0
+ printf("solo thread %ld notified in loop %ld\n", info->id, index);
+#endif
+ (*info->tcount)--;
+ PR_Unlock(info->lock);
+
+ PR_Lock(info->exitlock);
+ (*info->exitcount)++;
+ PR_NotifyCondVar(info->exitcvar);
+DPRINTF(("PrivateCondVarThread: thread 0x%lx notified exitcvar = 0x%lx cnt = %ld\n",
+ PR_GetCurrentThread(), info->exitcvar,(*info->exitcount)));
+ PR_Unlock(info->exitlock);
+ }
+#if 0
+ printf("solo thread %ld terminating\n", info->id);
+#endif
+}
+
+void
+CreateTestThread(threadinfo *info,
+ PRInt32 id,
+ PRLock *lock,
+ PRCondVar *cvar,
+ PRInt32 loops,
+ PRIntervalTime timeout,
+ PRInt32 *tcount,
+ PRLock *exitlock,
+ PRCondVar *exitcvar,
+ PRInt32 *exitcount,
+ PRBool shared,
+ PRThreadScope scope)
+{
+ info->id = id;
+ info->internal = (shared) ? PR_FALSE : PR_TRUE;
+ info->lock = lock;
+ info->cvar = cvar;
+ info->loops = loops;
+ info->timeout = timeout;
+ info->tcount = tcount;
+ info->exitlock = exitlock;
+ info->exitcvar = exitcvar;
+ info->exitcount = exitcount;
+ info->thread = PR_CreateThread(
+ PR_USER_THREAD,
+ shared?SharedCondVarThread:PrivateCondVarThread,
+ info,
+ PR_PRIORITY_NORMAL,
+ scope,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!info->thread)
+ PL_PrintError("error creating thread\n");
+}
+
+
+void
+CondVarTestSUU(void *_arg)
+{
+ PRInt32 arg = (PRInt32)_arg;
+ PRInt32 index, loops;
+ threadinfo *list;
+ PRLock *sharedlock;
+ PRCondVar *sharedcvar;
+ PRLock *exitlock;
+ PRCondVar *exitcvar;
+
+ exitcount=0;
+ tcount=0;
+ list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4));
+
+ sharedlock = PR_NewLock();
+ sharedcvar = PR_NewCondVar(sharedlock);
+ exitlock = PR_NewLock();
+ exitcvar = PR_NewCondVar(exitlock);
+
+ /* Create the threads */
+ for(index=0; index<arg; ) {
+ CreateTestThread(&list[index],
+ index,
+ sharedlock,
+ sharedcvar,
+ count,
+ PR_INTERVAL_NO_TIMEOUT,
+ &tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_TRUE,
+ PR_LOCAL_THREAD);
+ index++;
+ DPRINTF(("CondVarTestSUU: created thread 0x%lx\n",list[index].thread));
+ }
+
+ for (loops = 0; loops < count; loops++) {
+ /* Notify the threads */
+ for(index=0; index<(arg); index++) {
+ PR_Lock(list[index].lock);
+ (*list[index].tcount)++;
+ PR_NotifyCondVar(list[index].cvar);
+ PR_Unlock(list[index].lock);
+ DPRINTF(("PrivateCondVarThread: thread 0x%lx notified cvar = 0x%lx\n",
+ PR_GetCurrentThread(), list[index].cvar));
+ }
+
+ /* Wait for threads to finish */
+ PR_Lock(exitlock);
+ while(exitcount < arg)
+ PR_WaitCondVar(exitcvar, PR_SecondsToInterval(60));
+ PR_ASSERT(exitcount >= arg);
+ exitcount -= arg;
+ PR_Unlock(exitlock);
+ }
+
+ /* Join all the threads */
+ for(index=0; index<(arg); index++)
+ PR_JoinThread(list[index].thread);
+
+ PR_DestroyCondVar(sharedcvar);
+ PR_DestroyLock(sharedlock);
+ PR_DestroyCondVar(exitcvar);
+ PR_DestroyLock(exitlock);
+
+ PR_DELETE(list);
+}
+
+void
+CondVarTestSUK(void *_arg)
+{
+ PRInt32 arg = (PRInt32)_arg;
+ PRInt32 index, loops;
+ threadinfo *list;
+ PRLock *sharedlock;
+ PRCondVar *sharedcvar;
+ PRLock *exitlock;
+ PRCondVar *exitcvar;
+ exitcount=0;
+ tcount=0;
+
+ list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4));
+
+ sharedlock = PR_NewLock();
+ sharedcvar = PR_NewCondVar(sharedlock);
+ exitlock = PR_NewLock();
+ exitcvar = PR_NewCondVar(exitlock);
+
+ /* Create the threads */
+ for(index=0; index<arg; ) {
+ CreateTestThread(&list[index],
+ index,
+ sharedlock,
+ sharedcvar,
+ count,
+ PR_INTERVAL_NO_TIMEOUT,
+ &tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_TRUE,
+ PR_GLOBAL_THREAD);
+ index++;
+ }
+
+ for (loops = 0; loops < count; loops++) {
+ /* Notify the threads */
+ for(index=0; index<(arg); index++) {
+
+ PR_Lock(list[index].lock);
+ (*list[index].tcount)++;
+ PR_NotifyCondVar(list[index].cvar);
+ PR_Unlock(list[index].lock);
+ }
+
+#if 0
+ printf("wait for threads to be done\n");
+#endif
+ /* Wait for threads to finish */
+ PR_Lock(exitlock);
+ while(exitcount < arg)
+ PR_WaitCondVar(exitcvar, PR_SecondsToInterval(60));
+ PR_ASSERT(exitcount >= arg);
+ exitcount -= arg;
+ PR_Unlock(exitlock);
+#if 0
+ printf("threads ready\n");
+#endif
+ }
+
+ /* Join all the threads */
+ for(index=0; index<(arg); index++)
+ PR_JoinThread(list[index].thread);
+
+ PR_DestroyCondVar(sharedcvar);
+ PR_DestroyLock(sharedlock);
+ PR_DestroyCondVar(exitcvar);
+ PR_DestroyLock(exitlock);
+
+ PR_DELETE(list);
+}
+
+void
+CondVarTestPUU(void *_arg)
+{
+ PRInt32 arg = (PRInt32)_arg;
+ PRInt32 index, loops;
+ threadinfo *list;
+ PRLock *sharedlock;
+ PRCondVar *sharedcvar;
+ PRLock *exitlock;
+ PRCondVar *exitcvar;
+ PRInt32 *tcount, *saved_tcount;
+
+ exitcount=0;
+ list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4));
+ saved_tcount = tcount = (PRInt32 *)PR_CALLOC(sizeof(*tcount) * (arg * 4));
+
+ sharedlock = PR_NewLock();
+ sharedcvar = PR_NewCondVar(sharedlock);
+ exitlock = PR_NewLock();
+ exitcvar = PR_NewCondVar(exitlock);
+
+ /* Create the threads */
+ for(index=0; index<arg; ) {
+ list[index].lock = PR_NewLock();
+ list[index].cvar = PR_NewCondVar(list[index].lock);
+ CreateTestThread(&list[index],
+ index,
+ list[index].lock,
+ list[index].cvar,
+ count,
+ PR_INTERVAL_NO_TIMEOUT,
+ tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_FALSE,
+ PR_LOCAL_THREAD);
+
+ DPRINTF(("CondVarTestPUU: created thread 0x%lx\n",list[index].thread));
+ index++;
+ tcount++;
+ }
+
+ for (loops = 0; loops < count; loops++) {
+ /* Notify the threads */
+ for(index=0; index<(arg); index++) {
+
+ PR_Lock(list[index].lock);
+ (*list[index].tcount)++;
+ PR_NotifyCondVar(list[index].cvar);
+ PR_Unlock(list[index].lock);
+ }
+
+ PR_Lock(exitlock);
+ /* Wait for threads to finish */
+ while(exitcount < arg) {
+DPRINTF(("CondVarTestPUU: thread 0x%lx waiting on exitcvar = 0x%lx cnt = %ld\n",
+ PR_GetCurrentThread(), exitcvar, exitcount));
+ PR_WaitCondVar(exitcvar, PR_SecondsToInterval(60));
+ }
+ PR_ASSERT(exitcount >= arg);
+ exitcount -= arg;
+ PR_Unlock(exitlock);
+ }
+
+ /* Join all the threads */
+ for(index=0; index<(arg); index++) {
+ DPRINTF(("CondVarTestPUU: joining thread 0x%lx\n",list[index].thread));
+ PR_JoinThread(list[index].thread);
+ if (list[index].internal) {
+ PR_Lock(list[index].lock);
+ PR_DestroyCondVar(list[index].cvar);
+ PR_Unlock(list[index].lock);
+ PR_DestroyLock(list[index].lock);
+ }
+ }
+
+ PR_DestroyCondVar(sharedcvar);
+ PR_DestroyLock(sharedlock);
+ PR_DestroyCondVar(exitcvar);
+ PR_DestroyLock(exitlock);
+
+ PR_DELETE(list);
+ PR_DELETE(saved_tcount);
+}
+
+void
+CondVarTestPUK(void *_arg)
+{
+ PRInt32 arg = (PRInt32)_arg;
+ PRInt32 index, loops;
+ threadinfo *list;
+ PRLock *sharedlock;
+ PRCondVar *sharedcvar;
+ PRLock *exitlock;
+ PRCondVar *exitcvar;
+ PRInt32 *tcount, *saved_tcount;
+
+ exitcount=0;
+ list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4));
+ saved_tcount = tcount = (PRInt32 *)PR_CALLOC(sizeof(*tcount) * (arg * 4));
+
+ sharedlock = PR_NewLock();
+ sharedcvar = PR_NewCondVar(sharedlock);
+ exitlock = PR_NewLock();
+ exitcvar = PR_NewCondVar(exitlock);
+
+ /* Create the threads */
+ for(index=0; index<arg; ) {
+ list[index].lock = PR_NewLock();
+ list[index].cvar = PR_NewCondVar(list[index].lock);
+ CreateTestThread(&list[index],
+ index,
+ list[index].lock,
+ list[index].cvar,
+ count,
+ PR_INTERVAL_NO_TIMEOUT,
+ tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_FALSE,
+ PR_GLOBAL_THREAD);
+
+ index++;
+ tcount++;
+ }
+
+ for (loops = 0; loops < count; loops++) {
+ /* Notify the threads */
+ for(index=0; index<(arg); index++) {
+
+ PR_Lock(list[index].lock);
+ (*list[index].tcount)++;
+ PR_NotifyCondVar(list[index].cvar);
+ PR_Unlock(list[index].lock);
+ }
+
+ /* Wait for threads to finish */
+ PR_Lock(exitlock);
+ while(exitcount < arg)
+ PR_WaitCondVar(exitcvar, PR_SecondsToInterval(60));
+ PR_ASSERT(exitcount >= arg);
+ exitcount -= arg;
+ PR_Unlock(exitlock);
+ }
+
+ /* Join all the threads */
+ for(index=0; index<(arg); index++) {
+ PR_JoinThread(list[index].thread);
+ if (list[index].internal) {
+ PR_Lock(list[index].lock);
+ PR_DestroyCondVar(list[index].cvar);
+ PR_Unlock(list[index].lock);
+ PR_DestroyLock(list[index].lock);
+ }
+ }
+
+ PR_DestroyCondVar(sharedcvar);
+ PR_DestroyLock(sharedlock);
+ PR_DestroyCondVar(exitcvar);
+ PR_DestroyLock(exitlock);
+
+ PR_DELETE(list);
+ PR_DELETE(saved_tcount);
+}
+
+void
+CondVarTest(void *_arg)
+{
+ PRInt32 arg = (PRInt32)_arg;
+ PRInt32 index, loops;
+ threadinfo *list;
+ PRLock *sharedlock;
+ PRCondVar *sharedcvar;
+ PRLock *exitlock;
+ PRCondVar *exitcvar;
+ PRInt32 *ptcount, *saved_ptcount;
+
+ exitcount=0;
+ tcount=0;
+ list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4));
+ saved_ptcount = ptcount = (PRInt32 *)PR_CALLOC(sizeof(*ptcount) * (arg * 4));
+
+ sharedlock = PR_NewLock();
+ sharedcvar = PR_NewCondVar(sharedlock);
+ exitlock = PR_NewLock();
+ exitcvar = PR_NewCondVar(exitlock);
+
+ /* Create the threads */
+ for(index=0; index<arg*4; ) {
+ CreateTestThread(&list[index],
+ index,
+ sharedlock,
+ sharedcvar,
+ count,
+ PR_INTERVAL_NO_TIMEOUT,
+ &tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_TRUE,
+ PR_LOCAL_THREAD);
+
+ index++;
+ CreateTestThread(&list[index],
+ index,
+ sharedlock,
+ sharedcvar,
+ count,
+ PR_INTERVAL_NO_TIMEOUT,
+ &tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_TRUE,
+ PR_GLOBAL_THREAD);
+
+ index++;
+ list[index].lock = PR_NewLock();
+ list[index].cvar = PR_NewCondVar(list[index].lock);
+ CreateTestThread(&list[index],
+ index,
+ list[index].lock,
+ list[index].cvar,
+ count,
+ PR_INTERVAL_NO_TIMEOUT,
+ ptcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_FALSE,
+ PR_LOCAL_THREAD);
+ index++;
+ ptcount++;
+ list[index].lock = PR_NewLock();
+ list[index].cvar = PR_NewCondVar(list[index].lock);
+ CreateTestThread(&list[index],
+ index,
+ list[index].lock,
+ list[index].cvar,
+ count,
+ PR_INTERVAL_NO_TIMEOUT,
+ ptcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_FALSE,
+ PR_GLOBAL_THREAD);
+
+ index++;
+ ptcount++;
+ }
+
+ for (loops = 0; loops < count; loops++) {
+
+ /* Notify the threads */
+ for(index=0; index<(arg*4); index++) {
+ PR_Lock(list[index].lock);
+ (*list[index].tcount)++;
+ PR_NotifyCondVar(list[index].cvar);
+ PR_Unlock(list[index].lock);
+ }
+
+#if 0
+ printf("wait for threads done\n");
+#endif
+
+ /* Wait for threads to finish */
+ PR_Lock(exitlock);
+ while(exitcount < arg*4)
+ PR_WaitCondVar(exitcvar, PR_SecondsToInterval(60));
+ PR_ASSERT(exitcount >= arg*4);
+ exitcount -= arg*4;
+ PR_Unlock(exitlock);
+#if 0
+ printf("threads ready\n");
+#endif
+ }
+
+ /* Join all the threads */
+ for(index=0; index<(arg*4); index++) {
+ PR_JoinThread(list[index].thread);
+ if (list[index].internal) {
+ PR_Lock(list[index].lock);
+ PR_DestroyCondVar(list[index].cvar);
+ PR_Unlock(list[index].lock);
+ PR_DestroyLock(list[index].lock);
+ }
+ }
+
+ PR_DestroyCondVar(sharedcvar);
+ PR_DestroyLock(sharedlock);
+ PR_DestroyCondVar(exitcvar);
+ PR_DestroyLock(exitlock);
+
+ PR_DELETE(list);
+ PR_DELETE(saved_ptcount);
+}
+
+void
+CondVarTimeoutTest(void *_arg)
+{
+ PRInt32 arg = (PRInt32)_arg;
+ PRInt32 index, loops;
+ threadinfo *list;
+ PRLock *sharedlock;
+ PRCondVar *sharedcvar;
+ PRLock *exitlock;
+ PRCondVar *exitcvar;
+
+ list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4));
+
+ sharedlock = PR_NewLock();
+ sharedcvar = PR_NewCondVar(sharedlock);
+ exitlock = PR_NewLock();
+ exitcvar = PR_NewCondVar(exitlock);
+
+ /* Create the threads */
+ for(index=0; index<arg*4; ) {
+ CreateTestThread(&list[index],
+ index,
+ sharedlock,
+ sharedcvar,
+ count,
+ PR_MillisecondsToInterval(50),
+ &tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_TRUE,
+ PR_LOCAL_THREAD);
+ index++;
+ CreateTestThread(&list[index],
+ index,
+ sharedlock,
+ sharedcvar,
+ count,
+ PR_MillisecondsToInterval(50),
+ &tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_TRUE,
+ PR_GLOBAL_THREAD);
+ index++;
+ list[index].lock = PR_NewLock();
+ list[index].cvar = PR_NewCondVar(list[index].lock);
+ CreateTestThread(&list[index],
+ index,
+ list[index].lock,
+ list[index].cvar,
+ count,
+ PR_MillisecondsToInterval(50),
+ &tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_FALSE,
+ PR_LOCAL_THREAD);
+ index++;
+
+ list[index].lock = PR_NewLock();
+ list[index].cvar = PR_NewCondVar(list[index].lock);
+ CreateTestThread(&list[index],
+ index,
+ list[index].lock,
+ list[index].cvar,
+ count,
+ PR_MillisecondsToInterval(50),
+ &tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_FALSE,
+ PR_GLOBAL_THREAD);
+
+ index++;
+ }
+
+ for (loops = 0; loops < count; loops++) {
+
+ /* Wait for threads to finish */
+ PR_Lock(exitlock);
+ while(exitcount < arg*4)
+ PR_WaitCondVar(exitcvar, PR_SecondsToInterval(60));
+ PR_ASSERT(exitcount >= arg*4);
+ exitcount -= arg*4;
+ PR_Unlock(exitlock);
+ }
+
+
+ /* Join all the threads */
+ for(index=0; index<(arg*4); index++) {
+ PR_JoinThread(list[index].thread);
+ if (list[index].internal) {
+ PR_Lock(list[index].lock);
+ PR_DestroyCondVar(list[index].cvar);
+ PR_Unlock(list[index].lock);
+ PR_DestroyLock(list[index].lock);
+ }
+ }
+
+ PR_DestroyCondVar(sharedcvar);
+ PR_DestroyLock(sharedlock);
+ PR_DestroyCondVar(exitcvar);
+ PR_DestroyLock(exitlock);
+
+ PR_DELETE(list);
+}
+
+void
+CondVarMixedTest(void *_arg)
+{
+ PRInt32 arg = (PRInt32)_arg;
+ PRInt32 index, loops;
+ threadinfo *list;
+ PRLock *sharedlock;
+ PRCondVar *sharedcvar;
+ PRLock *exitlock;
+ PRCondVar *exitcvar;
+ PRInt32 *ptcount;
+
+ exitcount=0;
+ tcount=0;
+ list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4));
+ ptcount = (PRInt32 *)PR_CALLOC(sizeof(*ptcount) * (arg * 4));
+
+ sharedlock = PR_NewLock();
+ sharedcvar = PR_NewCondVar(sharedlock);
+ exitlock = PR_NewLock();
+ exitcvar = PR_NewCondVar(exitlock);
+
+ /* Create the threads */
+ for(index=0; index<arg*4; ) {
+ CreateTestThread(&list[index],
+ index,
+ sharedlock,
+ sharedcvar,
+ count,
+ PR_MillisecondsToInterval(50),
+ &tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_TRUE,
+ PR_LOCAL_THREAD);
+ index++;
+ CreateTestThread(&list[index],
+ index,
+ sharedlock,
+ sharedcvar,
+ count,
+ PR_MillisecondsToInterval(50),
+ &tcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_TRUE,
+ PR_GLOBAL_THREAD);
+ index++;
+ list[index].lock = PR_NewLock();
+ list[index].cvar = PR_NewCondVar(list[index].lock);
+ CreateTestThread(&list[index],
+ index,
+ list[index].lock,
+ list[index].cvar,
+ count,
+ PR_MillisecondsToInterval(50),
+ ptcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_FALSE,
+ PR_LOCAL_THREAD);
+ index++;
+ ptcount++;
+
+ list[index].lock = PR_NewLock();
+ list[index].cvar = PR_NewCondVar(list[index].lock);
+ CreateTestThread(&list[index],
+ index,
+ list[index].lock,
+ list[index].cvar,
+ count,
+ PR_MillisecondsToInterval(50),
+ ptcount,
+ exitlock,
+ exitcvar,
+ &exitcount,
+ PR_FALSE,
+ PR_GLOBAL_THREAD);
+ index++;
+ ptcount++;
+ }
+
+
+ /* Notify every 3rd thread */
+ for (loops = 0; loops < count; loops++) {
+
+ /* Notify the threads */
+ for(index=0; index<(arg*4); index+=3) {
+
+ PR_Lock(list[index].lock);
+ *list[index].tcount++;
+ PR_NotifyCondVar(list[index].cvar);
+ PR_Unlock(list[index].lock);
+
+ }
+ /* Wait for threads to finish */
+ PR_Lock(exitlock);
+ while(exitcount < arg*4)
+ PR_WaitCondVar(exitcvar, PR_SecondsToInterval(60));
+ PR_ASSERT(exitcount >= arg*4);
+ exitcount -= arg*4;
+ PR_Unlock(exitlock);
+ }
+
+ /* Join all the threads */
+ for(index=0; index<(arg*4); index++) {
+ PR_JoinThread(list[index].thread);
+ if (list[index].internal) {
+ PR_Lock(list[index].lock);
+ PR_DestroyCondVar(list[index].cvar);
+ PR_Unlock(list[index].lock);
+ PR_DestroyLock(list[index].lock);
+ }
+ }
+
+ PR_DestroyCondVar(sharedcvar);
+ PR_DestroyLock(sharedlock);
+
+ PR_DELETE(list);
+}
+
+void
+CondVarCombinedTest(void *arg)
+{
+ PRThread *threads[3];
+
+ threads[0] = PR_CreateThread(PR_USER_THREAD,
+ CondVarTest,
+ (void *)arg,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+ threads[1] = PR_CreateThread(PR_USER_THREAD,
+ CondVarTimeoutTest,
+ (void *)arg,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+ threads[2] = PR_CreateThread(PR_USER_THREAD,
+ CondVarMixedTest,
+ (void *)arg,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+
+ PR_JoinThread(threads[0]);
+ PR_JoinThread(threads[1]);
+ PR_JoinThread(threads[2]);
+}
+
+/************************************************************************/
+
+static void Measure(void (*func)(void *), PRInt32 arg, const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)((void *)arg);
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+
+ printf("%40s: %6.2f usec\n", msg, d / count);
+}
+
+static PRIntn PR_CALLBACK RealMain(int argc, char **argv)
+{
+ PRInt32 threads, default_threads = DEFAULT_THREADS;
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "vc:t:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'v': /* debug mode */
+ _debug_on = 1;
+ break;
+ case 'c': /* loop counter */
+ count = atoi(opt->value);
+ break;
+ case 't': /* number of threads involved */
+ default_threads = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (0 == count) count = DEFAULT_COUNT;
+ if (0 == default_threads) default_threads = DEFAULT_THREADS;
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("cvar2.log");
+#endif
+
+ printf("\n\
+CondVar Test: \n\
+ \n\
+Simple test creates several local and global threads; half use a single,\n\
+shared condvar, and the other half have their own condvar. The main \n\
+thread then loops notifying them to wakeup. \n\
+ \n\
+The timeout test is very similar except that the threads are not \n\
+notified. They will all wakeup on a 1 second timeout. \n\
+ \n\
+The mixed test combines the simple test and the timeout test; every \n\
+third thread is notified, the other threads are expected to timeout \n\
+correctly. \n\
+ \n\
+Lastly, the combined test creates a thread for each of the above three \n\
+cases and they all run simultaneously. \n\
+ \n\
+This test is run with %d, %d, %d, and %d threads of each type.\n\n",
+default_threads, default_threads*2, default_threads*3, default_threads*4);
+
+ PR_SetConcurrency(2);
+
+ for (threads = default_threads; threads < default_threads*5; threads+=default_threads) {
+ printf("\n%ld Thread tests\n", threads);
+ Measure(CondVarTestSUU, threads, "Condvar simple test shared UU");
+ Measure(CondVarTestSUK, threads, "Condvar simple test shared UK");
+ Measure(CondVarTestPUU, threads, "Condvar simple test priv UU");
+ Measure(CondVarTestPUK, threads, "Condvar simple test priv UK");
+#ifdef XP_MAC
+ /* Mac heaps can't handle thread*4 stack allocations at a time for (10, 15, 20)*4 */
+ Measure(CondVarTest, 5, "Condvar simple test All");
+ Measure(CondVarTimeoutTest, 5, "Condvar timeout test");
+#else
+ Measure(CondVarTest, threads, "Condvar simple test All");
+ Measure(CondVarTimeoutTest, threads, "Condvar timeout test");
+#endif
+#if 0
+ Measure(CondVarMixedTest, threads, "Condvar mixed timeout test");
+ Measure(CondVarCombinedTest, threads, "Combined condvar test");
+#endif
+ }
+
+ printf("PASS\n");
+
+ return 0;
+}
+
+PRIntn main(PRIntn argc, char *argv[])
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
diff --git a/pr/tests/dbmalloc.c b/pr/tests/dbmalloc.c
new file mode 100644
index 00000000..0241422a
--- /dev/null
+++ b/pr/tests/dbmalloc.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.
+ */
+
+/***********************************************************************
+**
+** Name: dbmalloc.c
+**
+** Description: Testing malloc (OBSOLETE)
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+***********************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include "nspr.h"
+
+void
+usage
+(
+ void
+)
+{
+ fprintf(stderr, "Usage: dbmalloc ('-m'|'-s') '-f' num_fails ('-d'|'-n') filename [...]\n");
+ exit(0);
+}
+
+typedef struct node_struct
+{
+ struct node_struct *next, *prev;
+ int line;
+ char value[4];
+}
+ node_t,
+ *node_pt;
+
+node_pt get_node(const char *line)
+{
+ node_pt rv;
+ int l = strlen(line);
+ rv = (node_pt)PR_MALLOC(sizeof(node_t) + l + 1 - 4);
+ if( (node_pt)0 == rv ) return (node_pt)0;
+ memcpy(&rv->value[0], line, l+1);
+ rv->next = rv->prev = (node_pt)0;
+ return rv;
+}
+
+void
+dump
+(
+ const char *name,
+ node_pt node,
+ int mf,
+ int debug
+)
+{
+ if( (node_pt)0 != node->prev ) dump(name, node->prev, mf, debug);
+ if( 0 != debug ) printf("[%s]: %6d: %s", name, node->line, node->value);
+ if( node->line == mf ) fprintf(stderr, "[%s]: Line %d was allocated!\n", name, node->line);
+ if( (node_pt)0 != node->next ) dump(name, node->next, mf, debug);
+ return;
+}
+
+void
+release
+(
+ node_pt node
+)
+{
+ if( (node_pt)0 != node->prev ) release(node->prev);
+ if( (node_pt)0 != node->next ) release(node->next);
+ PR_DELETE(node);
+}
+
+int
+t2
+(
+ const char *name,
+ int mf,
+ int debug
+)
+{
+ int rv;
+ FILE *fp;
+ int l = 0;
+ node_pt head = (node_pt)0;
+ char buffer[ BUFSIZ ];
+
+ fp = fopen(name, "r");
+ if( (FILE *)0 == fp )
+ {
+ fprintf(stderr, "[%s]: Cannot open \"%s.\"\n", name, name);
+ return -1;
+ }
+
+ /* fgets mallocs a buffer, first time through. */
+ if( (char *)0 == fgets(buffer, BUFSIZ, fp) )
+ {
+ fprintf(stderr, "[%s]: \"%s\" is empty.\n", name, name);
+ (void)fclose(fp);
+ return -1;
+ }
+
+ rewind(fp);
+
+ if( PR_SUCCESS != PR_ClearMallocCount() )
+ {
+ fprintf(stderr, "[%s]: Cannot clear malloc count.\n", name);
+ (void)fclose(fp);
+ return -1;
+ }
+
+ if( PR_SUCCESS != PR_SetMallocCountdown(mf) )
+ {
+ fprintf(stderr, "[%s]: Cannot set malloc countdown to %d\n", name, mf);
+ (void)fclose(fp);
+ return -1;
+ }
+
+ while( fgets(buffer, BUFSIZ, fp) )
+ {
+ node_pt n;
+ node_pt *w = &head;
+
+ if( (strlen(buffer) == (BUFSIZ-1)) && (buffer[BUFSIZ-2] != '\n') )
+ buffer[BUFSIZ-2] == '\n';
+
+ l++;
+
+ n = get_node(buffer);
+ if( (node_pt)0 == n )
+ {
+ printf("[%s]: Line %d: malloc failure!\n", name, l);
+ continue;
+ }
+
+ n->line = l;
+
+ while( 1 )
+ {
+ int comp;
+
+ if( (node_pt)0 == *w )
+ {
+ *w = n;
+ break;
+ }
+
+ comp = strcmp((*w)->value, n->value);
+ if( comp < 0 ) w = &(*w)->next;
+ else w = &(*w)->prev;
+ }
+ }
+
+ (void)fclose(fp);
+
+ dump(name, head, mf, debug);
+
+ rv = PR_GetMallocCount();
+ PR_ClearMallocCountdown();
+
+ release(head);
+
+ return rv;
+}
+
+int nf = 0;
+int debug = 0;
+
+void
+test
+(
+ const char *name
+)
+{
+ int n, i;
+
+ extern int nf, debug;
+
+ printf("[%s]: starting test 0\n", name);
+ n = t2(name, 0, debug);
+ if( -1 == n ) return;
+ printf("[%s]: test 0 had %ld allocations.\n", name, n);
+
+ if( 0 >= n ) return;
+
+ for( i = 0; i < nf; i++ )
+ {
+ int which = rand() % n;
+ if( 0 == which ) printf("[%s]: starting test %d -- no allocation should fail\n", name, i+1);
+ else printf("[%s]: starting test %d -- allocation %d should fail\n", name, i+1, which);
+ (void)t2(name, which, debug);
+ printf("[%s]: test %d done.\n", name, i+1);
+ }
+
+ return;
+}
+
+int
+main
+(
+ int argc,
+ char *argv[]
+)
+{
+ int okay = 0;
+ int multithread = 0;
+
+ struct threadlist
+ {
+ struct threadlist *next;
+ PRThread *thread;
+ }
+ *threadhead = (struct threadlist *)0;
+
+ extern int nf, debug;
+
+ srand(time(0));
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ printf("[main]: We %s using the debugging malloc.\n",
+ PR_IsDebuggingMalloc() ? "ARE" : "ARE NOT");
+
+ while( argv++, --argc )
+ {
+ if( '-' == argv[0][0] )
+ {
+ switch( argv[0][1] )
+ {
+ case 'f':
+ nf = atoi(argv[0][2] ? &argv[0][2] :
+ --argc ? *++argv : "0");
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'n':
+ debug = 0;
+ break;
+ case 'm':
+ multithread = 1;
+ break;
+ case 's':
+ multithread = 0;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+ else
+ {
+ FILE *fp = fopen(*argv, "r");
+ if( (FILE *)0 == fp )
+ {
+ fprintf(stderr, "Cannot open \"%s.\"\n", *argv);
+ continue;
+ }
+
+ okay++;
+ (void)fclose(fp);
+ if( multithread )
+ {
+ struct threadlist *n;
+
+ n = (struct threadlist *)malloc(sizeof(struct threadlist));
+ if( (struct threadlist *)0 == n )
+ {
+ fprintf(stderr, "This is getting tedious. \"%s\"\n", *argv);
+ continue;
+ }
+
+ n->next = threadhead;
+ n->thread = PR_CreateThread(PR_USER_THREAD, (void (*)(void *))test,
+ *argv, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD,
+ 0);
+ if( (PRThread *)0 == n->thread )
+ {
+ fprintf(stderr, "Can't create thread for \"%s.\"\n", *argv);
+ continue;
+ }
+ else
+ {
+ threadhead = n;
+ }
+ }
+ else
+ {
+ test(*argv);
+ }
+ }
+ }
+
+ if( okay == 0 ) usage();
+ else while( (struct threadlist *)0 != threadhead )
+ {
+ struct threadlist *x = threadhead->next;
+ (void)PR_JoinThread(threadhead->thread);
+ PR_DELETE(threadhead);
+ threadhead = x;
+ }
+
+ return 0;
+}
+
diff --git a/pr/tests/dbmalloc1.c b/pr/tests/dbmalloc1.c
new file mode 100644
index 00000000..6484ca06
--- /dev/null
+++ b/pr/tests/dbmalloc1.c
@@ -0,0 +1,122 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+**
+** Name: dbmalloc1.c (OBSOLETE)
+**
+** Description: Tests PR_SetMallocCountdown PR_ClearMallocCountdown functions.
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+**
+** 12-June-97 AGarcia Revert to return code 0 and 1, remove debug option (obsolete).
+***********************************************************************/
+
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include "nspr.h"
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+/* variable used for both r1 and r2 tests */
+int should_fail =0;
+int actually_failed=0;
+
+
+void
+r1
+(
+ void
+)
+{
+ int i;
+ actually_failed=0;
+ for( i = 0; i < 5; i++ )
+ {
+ void *x = PR_MALLOC(128);
+ if( (void *)0 == x ) {
+ if (debug_mode) printf("\tMalloc %d failed.\n", i+1);
+ actually_failed = 1;
+ }
+ PR_DELETE(x);
+ }
+
+ if (((should_fail != actually_failed) & (!debug_mode))) failed_already=1;
+
+
+ return;
+}
+
+void
+r2
+(
+ void
+)
+{
+ int i;
+
+ for( i = 0; i <= 5; i++ )
+ {
+ should_fail =0;
+ if( 0 == i ) {
+ if (debug_mode) printf("No malloc should fail:\n");
+ }
+ else {
+ if (debug_mode) printf("Malloc %d should fail:\n", i);
+ should_fail = 1;
+ }
+ PR_SetMallocCountdown(i);
+ r1();
+ PR_ClearMallocCountdown();
+ }
+}
+
+int
+main
+(
+ int argc,
+ char *argv[]
+)
+{
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+ r2();
+
+ if(failed_already)
+ return 1;
+ else
+ return 0;
+
+
+}
+
diff --git a/pr/tests/dceemu.c b/pr/tests/dceemu.c
new file mode 100644
index 00000000..cb04cbd6
--- /dev/null
+++ b/pr/tests/dceemu.c
@@ -0,0 +1,113 @@
+/* -*- 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: dceemu.c
+** Description: testing the DCE emulation api
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+** 12-June-97 Revert to return code 0 and 1, remove debug option (obsolete).
+**/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+
+
+#if defined(_PR_DCETHREADS)
+
+#include "prlog.h"
+#include "prinit.h"
+#include "prpdce.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+PRIntn failed_already=0;
+PRIntn debug_mode=0;
+
+static PRIntn prmain(PRIntn argc, char **argv)
+{
+ PRStatus rv;
+ PRLock *ml = PR_NewLock();
+ PRCondVar *cv = PRP_NewNakedCondVar();
+ PRIntervalTime tenmsecs = PR_MillisecondsToInterval(10);
+
+ rv = PRP_TryLock(ml);
+ PR_ASSERT(PR_SUCCESS == rv);
+ if ((rv != PR_SUCCESS) & (!debug_mode)) failed_already=1;
+
+ rv = PRP_TryLock(ml);
+ PR_ASSERT(PR_FAILURE == rv);
+ if ((rv != PR_FAILURE) & (!debug_mode)) failed_already=1;
+
+ rv = PRP_NakedNotify(cv);
+ PR_ASSERT(PR_SUCCESS == rv);
+ if ((rv != PR_SUCCESS) & (!debug_mode)) failed_already=1;
+
+ rv = PRP_NakedBroadcast(cv);
+ PR_ASSERT(PR_SUCCESS == rv);
+ if ((rv != PR_SUCCESS) & (!debug_mode)) failed_already=1;
+
+ rv = PRP_NakedWait(cv, ml, tenmsecs);
+ PR_ASSERT(PR_SUCCESS == rv);
+ if ((rv != PR_SUCCESS) & (!debug_mode)) failed_already=1;
+
+ PR_Unlock(ml);
+
+ rv = PRP_NakedNotify(cv);
+ PR_ASSERT(PR_SUCCESS == rv);
+ if ((rv != PR_SUCCESS) & (!debug_mode)) failed_already=1;
+
+ rv = PRP_NakedBroadcast(cv);
+ PR_ASSERT(PR_SUCCESS == rv);
+ if ((rv != PR_SUCCESS) & (!debug_mode)) failed_already=1;
+
+ PRP_DestroyNakedCondVar(cv);
+ PR_DestroyLock(ml);
+
+ if (debug_mode) printf("Test succeeded\n");
+
+ return 0;
+
+} /* prmain */
+
+#endif /* #if defined(_PR_DCETHREADS) */
+
+int main(int argc, char **argv)
+{
+
+#if defined(_PR_DCETHREADS)
+ PR_Initialize(prmain, argc, argv, 0);
+ if(failed_already)
+ return 1;
+ else
+ return 0;
+#else
+ return 0;
+#endif
+} /* main */
+
+
+/* decemu.c */
diff --git a/pr/tests/depend.c b/pr/tests/depend.c
new file mode 100644
index 00000000..6cfce442
--- /dev/null
+++ b/pr/tests/depend.c
@@ -0,0 +1,134 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+** 1996 - Netscape Communications Corporation
+**
+**
+** Name: depend.c
+** Description: Test to enumerate the dependencies
+*
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+***********************************************************************/
+#include "prinit.h"
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static void PrintVersion(
+ const char *msg, const PRVersion* info, PRIntn tab)
+{
+ static const len = 20;
+ static const char *tabs = {" "};
+
+ tab *= 2;
+ if (tab > len) tab = len;
+ printf("%s", &tabs[len - tab]);
+ printf("%s ", msg);
+ printf("%s ", info->id);
+ printf("%d.%d", info->major, info->minor);
+ if (0 != info->patch)
+ printf(".p%d", info->patch);
+ printf("\n");
+} /* PrintDependency */
+
+static void ChaseDependents(const PRVersionInfo *info, PRIntn tab)
+{
+ PrintVersion("exports", &info->selfExport, tab);
+ if (NULL != info->importEnumerator)
+ {
+ const PRDependencyInfo *dependent = NULL;
+ while (NULL != (dependent = info->importEnumerator(dependent)))
+ {
+ const PRVersionInfo *import = dependent->exportInfoFn();
+ PrintVersion("imports", &dependent->importNeeded, tab);
+ ChaseDependents(import, tab + 1);
+ }
+ }
+} /* ChaseDependents */
+
+static PRVersionInfo hack_export;
+static PRVersionInfo dummy_export;
+static PRDependencyInfo dummy_imports[2];
+
+static const PRVersionInfo *HackExportInfo(void)
+{
+ hack_export.selfExport.major = 11;
+ hack_export.selfExport.minor = 10;
+ hack_export.selfExport.patch = 200;
+ hack_export.selfExport.id = "Hack";
+ hack_export.importEnumerator = NULL;
+ return &hack_export;
+}
+
+static const PRDependencyInfo *DummyImports(
+ const PRDependencyInfo *previous)
+{
+ if (NULL == previous) return &dummy_imports[0];
+ else if (&dummy_imports[0] == previous) return &dummy_imports[1];
+ else if (&dummy_imports[1] == previous) return NULL;
+} /* DummyImports */
+
+static const PRVersionInfo *DummyLibVersion(void)
+{
+ dummy_export.selfExport.major = 1;
+ dummy_export.selfExport.minor = 0;
+ dummy_export.selfExport.patch = 0;
+ dummy_export.selfExport.id = "Dumbass application";
+ dummy_export.importEnumerator = DummyImports;
+
+ dummy_imports[0].importNeeded.major = 2;
+ dummy_imports[0].importNeeded.minor = 0;
+ dummy_imports[0].importNeeded.patch = 0;
+ dummy_imports[0].importNeeded.id = "Netscape Portable Runtime";
+ dummy_imports[0].exportInfoFn = PR_ExportInfo;
+
+ dummy_imports[1].importNeeded.major = 5;
+ dummy_imports[1].importNeeded.minor = 1;
+ dummy_imports[1].importNeeded.patch = 2;
+ dummy_imports[1].importNeeded.id = "Hack Library";
+ dummy_imports[1].exportInfoFn = HackExportInfo;
+
+ return &dummy_export;
+} /* DummyLibVersion */
+
+int main(int argc, char **argv)
+{
+ PRIntn tab = 0;
+ const PRVersionInfo *info = DummyLibVersion();
+ const char *buildDate = __DATE__, *buildTime = __TIME__;
+
+ printf("Depend.c build time is %s %s\n", buildDate, buildTime);
+
+ if (NULL != info) ChaseDependents(info, tab);
+
+ return 0;
+} /* main */
+
+/* depend.c */
diff --git a/pr/tests/dll/Makefile b/pr/tests/dll/Makefile
new file mode 100644
index 00000000..c2f72d71
--- /dev/null
+++ b/pr/tests/dll/Makefile
@@ -0,0 +1,94 @@
+#
+# 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
+
+INCLUDES = -I$(DIST)/include
+
+OBJS = $(OBJDIR)/mygetval.$(OBJ_SUFFIX) \
+ $(OBJDIR)/mysetval.$(OBJ_SUFFIX)
+
+ifeq ($(OS_TARGET), WIN16)
+W16OBJS = $(subst $(space),$(comma)$(space),$(OBJS))
+endif
+
+ifeq ($(OS_ARCH), WINNT)
+ifeq (,$(filter-out WIN16 OS2,$(OS_TARGET)))
+# do nothing
+else
+DLLBASE=/BASE:0x30000000
+RES=$(OBJDIR)/my.res
+RESNAME=../../../pr/src/nspr.rc
+endif
+endif
+
+ifeq ($(OS_ARCH), WINNT)
+IMPORT_LIBRARY = $(OBJDIR)/my.lib
+SHARED_LIBRARY = $(OBJDIR)/my.dll
+ifeq ($(OS_TARGET), OS2)
+LIBRARY = $(OBJDIR)/my_s.lib
+# XXX $(LIBRARY) probably doesn't need to be added to TARGETS.
+TARGETS = $(LIBRARY) $(SHARED_LIBRARY) $(IMPORT_LIBRARY)
+else
+TARGETS = $(SHARED_LIBRARY) $(IMPORT_LIBRARY)
+endif
+else
+ifdef MKSHLIB
+SHARED_LIBRARY = $(OBJDIR)/libmy.$(DLL_SUFFIX)
+endif
+TARGETS = $(SHARED_LIBRARY)
+endif
+
+#
+# To create a loadable module on Rhapsody, we must override
+# -dynamiclib with -bundle.
+#
+ifeq ($(OS_ARCH),Rhapsody)
+DSO_LDOPTS = -bundle
+endif
+
+include $(MOD_DEPTH)/config/rules.mk
+
+ifeq ($(OS_TARGET), WIN16)
+# Note: The Win16 target: my.dll requires these macros
+# to be overridden to build the test .dll
+# default values in win16...mk are for release targets.
+#
+OS_DLL_OPTION = NOCASEEXACT
+OS_LIB_FLAGS = -irn
+endif
+
+ifdef SHARED_LIBRARY
+export:: $(TARGETS)
+ $(NSINSTALL) $(TARGETS) ../$(OBJDIR)/dll
+
+install:: export
+
+clean::
+ rm -rf $(TARGETS)
+endif
diff --git a/pr/tests/dll/mygetval.c b/pr/tests/dll/mygetval.c
new file mode 100644
index 00000000..225b3893
--- /dev/null
+++ b/pr/tests/dll/mygetval.c
@@ -0,0 +1,37 @@
+/* -*- 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(WIN16)
+#include <windows.h>
+#endif
+#include "prtypes.h"
+
+extern PRIntn my_global;
+
+PR_IMPLEMENT(PRIntn) My_GetValue()
+{
+ return my_global;
+}
+
+#if defined(WIN16)
+int CALLBACK LibMain( HINSTANCE hInst, WORD wDataSeg,
+ WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ return TRUE;
+}
+#endif /* WIN16 */
+
diff --git a/pr/tests/dll/mysetval.c b/pr/tests/dll/mysetval.c
new file mode 100644
index 00000000..731eb2a4
--- /dev/null
+++ b/pr/tests/dll/mysetval.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.
+ */
+
+#include "prtypes.h"
+
+PRIntn my_global = 0;
+
+PR_IMPLEMENT(void) My_SetValue(PRIntn val)
+{
+ my_global = val;
+}
diff --git a/pr/tests/dlltest.c b/pr/tests/dlltest.c
new file mode 100644
index 00000000..bc817a05
--- /dev/null
+++ b/pr/tests/dlltest.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.
+ */
+
+/***********************************************************************
+**
+** Name: dlltest.c
+**
+** Description: test dll functionality.
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+** 12-June-97 Revert to return code 0 and 1.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+#include "prinit.h"
+#include "prlink.h"
+#include "prmem.h"
+#include "prerror.h"
+
+#include "plstr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+typedef PRIntn (PR_CALLBACK *GetFcnType)(void);
+typedef void (PR_CALLBACK *SetFcnType)(PRIntn);
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+int main(int argc, char** argv)
+{
+ PRLibrary *lib, *lib2; /* two handles to the same library */
+ GetFcnType getFcn;
+ SetFcnType setFcn;
+ PRIntn value;
+ PRStatus status;
+ char *libName;
+
+ if (argc >= 2 && PL_strcmp(argv[1], "-d") == 0) {
+ debug_mode = 1;
+ }
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ /*
+ * Test 1: load the library, look up the symbols, call the functions,
+ * and check the results.
+ */
+
+ libName = PR_GetLibraryName("dll", "my");
+ if (debug_mode) printf("Loading library %s\n", libName);
+ lib = PR_LoadLibrary(libName);
+ PR_FreeLibraryName(libName);
+ if (lib == NULL) {
+ PRInt32 textLength = PR_GetErrorTextLength();
+ char *text = (char*)PR_MALLOC(textLength);
+ (void)PR_GetErrorText(text);
+ fprintf(
+ stderr, "PR_LoadLibrary failed (%d, %d, %s)\n",
+ PR_GetError(), PR_GetOSError(), text);
+ if (!debug_mode) failed_already=1;
+ }
+ getFcn = (GetFcnType) PR_FindSymbol(lib, "My_GetValue");
+ setFcn = (SetFcnType) PR_FindSymbol(lib, "My_SetValue");
+ (*setFcn)(888);
+ value = (*getFcn)();
+ if (value != 888) {
+ fprintf(stderr, "Test 1 failed: set value to 888, but got %d\n", value);
+ if (!debug_mode) failed_already=1;
+ }
+ if (debug_mode) printf("Test 1 passed\n");
+
+ /*
+ * Test 2: get a second handle to the same library (this should increment
+ * the reference count), look up the symbols, call the functions, and
+ * check the results.
+ */
+
+ getFcn = (GetFcnType) PR_FindSymbolAndLibrary("My_GetValue", &lib2);
+ if (NULL == getFcn || lib != lib2) {
+ fprintf(stderr, "Test 2 failed: handles for the same library are not "
+ "equal: handle 1: %p, handle 2: %p\n", lib, lib2);
+ if (!debug_mode) failed_already=1;
+ }
+ setFcn = (SetFcnType) PR_FindSymbol(lib2, "My_SetValue");
+ value = (*getFcn)();
+ if (value != 888) {
+ fprintf(stderr, "Test 2 failed: value should be 888, but got %d\n",
+ value);
+ if (!debug_mode) failed_already=1;
+ }
+ (*setFcn)(777);
+ value = (*getFcn)();
+ if (value != 777) {
+ fprintf(stderr, "Test 2 failed: set value to 777, but got %d\n", value);
+ if (!debug_mode) failed_already=1;
+ goto exit_now;
+ }
+ if (debug_mode) printf("Test 2 passed\n");
+
+ /*
+ * Test 3: unload the library. The library should still be accessible
+ * via the second handle. do the same things as above.
+ */
+
+ status = PR_UnloadLibrary(lib);
+ if (PR_FAILURE == status) {
+ fprintf(stderr, "Test 3 failed: cannot unload library: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ if (!debug_mode) failed_already=1;
+ goto exit_now;
+ }
+ getFcn = (GetFcnType) PR_FindSymbol(lib2, "My_GetValue");
+ setFcn = (SetFcnType) PR_FindSymbol(lib2, "My_SetValue");
+ (*setFcn)(666);
+ value = (*getFcn)();
+ if (value != 666) {
+ fprintf(stderr, "Test 3 failed: set value to 666, but got %d\n", value);
+ if (!debug_mode) failed_already=1;
+ goto exit_now;
+ }
+ if (debug_mode) printf("Test 3 passed\n");
+
+ /*
+ * Test 4: unload the library, testing the reference count mechanism.
+ */
+
+ status = PR_UnloadLibrary(lib2);
+ if (PR_FAILURE == status) {
+ fprintf(stderr, "Test 4 failed: cannot unload library: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ if (!debug_mode) failed_already=1;
+ goto exit_now;
+ }
+ getFcn = (GetFcnType) PR_FindSymbolAndLibrary("My_GetValue", &lib2);
+ if (NULL != getFcn) {
+ fprintf(stderr, "Test 4 failed: how can we find a symbol "
+ "in an already unloaded library?\n");
+ if (!debug_mode) failed_already=1;
+ goto exit_now;
+ }
+ if (debug_mode) {
+ printf("Test 4 passed\n");
+ }
+
+ /*
+ ** Test 5: LoadStaticLibrary()
+ */
+ {
+ PRStaticLinkTable slt[10];
+ PRLibrary *lib;
+
+ lib = PR_LoadStaticLibrary( "my.dll", slt );
+ if ( lib == NULL )
+ {
+ fprintf(stderr, "Test 5: LoadStatiLibrary() failed\n" );
+ goto exit_now;
+ }
+ if (debug_mode)
+ {
+ printf("Test 5 passed\n");
+ }
+ }
+
+ goto exit_now;
+exit_now:
+ PR_Cleanup();
+
+ if (failed_already) {
+ printf("FAILED\n");
+ return 1;
+ } else {
+ printf("PASSED\n");
+ return 0;
+ }
+}
diff --git a/pr/tests/dtoa.c b/pr/tests/dtoa.c
new file mode 100644
index 00000000..5fa9a8cb
--- /dev/null
+++ b/pr/tests/dtoa.c
@@ -0,0 +1,198 @@
+/* -*- 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 contains a test program for the function conversion functions
+ * for double precision code:
+ * PR_strtod
+ * PR_dtoa
+ * PR_cnvtf
+ *
+ * This file was ns/nspr/tests/dtoa.c, created by rrj on 1996/06/22.
+ *
+ *****************************************************************************/
+#include <stdio.h>
+#include <sys/types.h>
+#include <string.h>
+#include <locale.h>
+#include "prprf.h"
+#include "prdtoa.h"
+
+static int failed_already = 0;
+
+int main( int argc, char* argv[] )
+{
+ double num;
+ double num1;
+ double zero = 0.0;
+ char cnvt[50];
+
+ num = 1e24;
+ num1 = PR_strtod("1e24",NULL);
+ if(num1 != num){
+ fprintf(stderr,"Failed to convert numeric value %s\n","1e24");
+ failed_already = 1;
+ }
+
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("1e+24",cnvt) != 0){
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+ num = 0.001e7;
+ num1 = PR_strtod("0.001e7",NULL);
+ if(num1 != num){
+ fprintf(stderr,"Failed to convert numeric value %s\n","0.001e7");
+ failed_already = 1;
+ }
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("10000",cnvt) != 0){
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+ num = 0.0000000000000753;
+ num1 = PR_strtod("0.0000000000000753",NULL);
+ if(num1 != num){
+ fprintf(stderr,"Failed to convert numeric value %s\n",
+ "0.0000000000000753");
+ failed_already = 1;
+ }
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("7.53e-14",cnvt) != 0){
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+ num = 1.867e73;
+ num1 = PR_strtod("1.867e73",NULL);
+ if(num1 != num){
+ fprintf(stderr,"Failed to convert numeric value %s\n","1.867e73");
+ failed_already = 1;
+ }
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("1.867e+73",cnvt) != 0){
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+
+ num = -1.867e73;
+ num1 = PR_strtod("-1.867e73",NULL);
+ if(num1 != num){
+ fprintf(stderr,"Failed to convert numeric value %s\n","-1.867e73");
+ failed_already = 1;
+ }
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("-1.867e+73",cnvt) != 0){
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+ num = -1.867e-73;
+ num1 = PR_strtod("-1.867e-73",NULL);
+ if(num1 != num){
+ fprintf(stderr,"Failed to convert numeric value %s\n","-1.867e-73");
+ failed_already = 1;
+ }
+
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("-1.867e-73",cnvt) != 0){
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+ /* Testing for infinity */
+ num = 1.0 / zero;
+ num1 = PR_strtod("1.867e765",NULL);
+ if(num1 != num){
+ fprintf(stderr,"Failed to convert numeric value %s\n","1.867e765");
+ failed_already = 1;
+ }
+
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("Infinity",cnvt) != 0){
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+ num = -1.0 / zero;
+ num1 = PR_strtod("-1.867e765",NULL);
+ if(num1 != num){
+ fprintf(stderr,"Failed to convert numeric value %s\n","-1.867e765");
+ failed_already = 1;
+ }
+
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("-Infinity",cnvt) != 0){
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+ /* Testing for NaN. PR_strtod can't parse "NaN" and "Infinity" */
+ num = zero / zero;
+
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("NaN",cnvt) != 0){
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+ num = - zero / zero;
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("NaN",cnvt) != 0){
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+ num = 1.0000000001e21;
+ num1 = PR_strtod("1.0000000001e21",NULL);
+ if(num1 != num){
+ fprintf(stderr,"Failed to convert numeric value %s\n",
+ "1.0000000001e21");
+ failed_already = 1;
+ }
+
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("1.0000000001e+21",cnvt) != 0){
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+
+
+ num = -1.0000000001e-21;
+ num1 = PR_strtod("-1.0000000001e-21",NULL);
+ if(num1 != num){
+ fprintf(stderr,"Failed to convert numeric value %s\n",
+ "-1.0000000001e-21");
+ failed_already = 1;
+ }
+ PR_cnvtf(cnvt,sizeof(cnvt),20,num);
+ if(strcmp("-1.0000000001e-21",cnvt) != 0){
+ fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt);
+ failed_already = 1;
+ }
+ if (failed_already) {
+ printf("FAILED\n");
+ } else {
+ printf("PASSED\n");
+ }
+ return failed_already;
+}
diff --git a/pr/tests/exit.c b/pr/tests/exit.c
new file mode 100644
index 00000000..343e8b02
--- /dev/null
+++ b/pr/tests/exit.c
@@ -0,0 +1,118 @@
+/* -*- 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 "prio.h"
+#include "prprf.h"
+#include "prinit.h"
+#include "prthread.h"
+#include "prproces.h"
+#include "prinrval.h"
+
+#include "plgetopt.h"
+
+#include <stdlib.h>
+
+static PRInt32 dally = 0;
+static PRFileDesc *err = NULL;
+static PRBool verbose = PR_FALSE, force = PR_FALSE;
+
+static void Help(void)
+{
+ PR_fprintf(err, "Usage: [-t s] [-h]\n");
+ PR_fprintf(err, "\t-d Verbose output (default: FALSE)\n");
+ PR_fprintf(err, "\t-x Forced termination (default: FALSE)\n");
+ PR_fprintf(err, "\t-t Time for thread to block (default: 10 seconds)\n");
+ PR_fprintf(err, "\t-h This message and nothing else\n");
+} /* Help */
+
+static void Dull(void *arg)
+{
+ PR_Sleep(PR_SecondsToInterval(dally));
+ if (verbose && force)
+ PR_fprintf(err, "If you see this, the test failed\n");
+} /* Dull */
+
+static PRIntn PR_CALLBACK RealMain(PRIntn argc, char **argv)
+{
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "ht:dx");
+
+ err = PR_GetSpecialFD(PR_StandardError);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* verbosity */
+ verbose = PR_TRUE;
+ break;
+ case 'x': /* force exit */
+ force = PR_TRUE;
+ break;
+ case 't': /* seconds to dally in child */
+ dally = atoi(opt->value);
+ break;
+ case 'h': /* user wants some guidance */
+ default:
+ Help(); /* so give him an earful */
+ return 2; /* but not a lot else */
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (0 == dally) dally = 10;
+
+ /*
+ * Create LOCAL and GLOBAL threads
+ */
+ (void)PR_CreateThread(
+ PR_USER_THREAD, Dull, NULL, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, 0);
+
+ (void)PR_CreateThread(
+ PR_USER_THREAD, Dull, NULL, PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0);
+
+ if (verbose)
+ PR_fprintf(
+ err, "Main is exiting now. Program should exit %s.\n",
+ (force) ? "immediately" : "after child dally time");
+
+ if (force)
+ {
+ PR_ProcessExit(0);
+ if (verbose)
+ {
+ PR_fprintf(err, "You should not have gotten here.\n");
+ return 1;
+ }
+ }
+ return 0;
+
+}
+
+
+PRIntn main(PRIntn argc, char *argv[])
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
diff --git a/pr/tests/fileio.c b/pr/tests/fileio.c
new file mode 100644
index 00000000..2fd84de5
--- /dev/null
+++ b/pr/tests/fileio.c
@@ -0,0 +1,231 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+**
+** Name: fileio.c
+**
+** Description: Program to copy one file to another.
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+** 12-June-97 Revert to return code 0 and 1, remove debug option (obsolete).
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+#include "prinit.h"
+#include "prthread.h"
+#include "prlock.h"
+#include "prcvar.h"
+#include "prmon.h"
+#include "prmem.h"
+#include "prio.h"
+#include "prlog.h"
+
+#include <stdio.h>
+
+#ifdef XP_MAC
+#include "prsem.h"
+#include "prlog.h"
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+#else
+#include "obsolete/prsem.h"
+#endif
+
+
+#define TBSIZE 1024
+
+static PRUint8 tbuf[TBSIZE];
+
+static PRFileDesc *t1, *t2;
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+static void InitialSetup(void)
+{
+ PRUintn i;
+ PRInt32 nWritten, rv;
+
+ t1 = PR_Open("t1.tmp", PR_CREATE_FILE | PR_RDWR, 0);
+ PR_ASSERT(t1 != NULL);
+
+ for (i=0; i<TBSIZE; i++)
+ tbuf[i] = i;
+
+ nWritten = PR_Write((PRFileDesc*)t1, tbuf, TBSIZE);
+ PR_ASSERT(nWritten == TBSIZE);
+
+ rv = PR_Seek(t1,0,PR_SEEK_SET);
+ PR_ASSERT(rv == 0);
+
+ t2 = PR_Open("t2.tmp", PR_CREATE_FILE | PR_RDWR, 0);
+ PR_ASSERT(t2 != NULL);
+}
+
+
+static void VerifyAndCleanup(void)
+{
+ PRUintn i;
+ PRInt32 nRead, rv;
+
+ for (i=0; i<TBSIZE; i++)
+ tbuf[i] = 0;
+
+ rv = PR_Seek(t2,0,PR_SEEK_SET);
+ PR_ASSERT(rv == 0);
+
+ nRead = PR_Read((PRFileDesc*)t2, tbuf, TBSIZE);
+ PR_ASSERT(nRead == TBSIZE);
+
+ for (i=0; i<TBSIZE; i++)
+ if (tbuf[i] != (PRUint8)i) {
+ if (debug_mode) printf("data mismatch for index= %d \n", i);
+ else failed_already=1;
+ }
+ PR_Close(t1);
+ PR_Close(t2);
+
+ PR_Delete("t1.tmp");
+ PR_Delete("t2.tmp");
+
+ if (debug_mode) printf("fileio test passed\n");
+}
+
+
+/*------------------ Following is the real test program ---------*/
+/*
+ Program to copy one file to another. Two temporary files get
+ created. First one gets written in one write call. Then,
+ a reader thread reads from this file into a double buffer.
+ The writer thread writes from double buffer into the other
+ temporary file. The second temporary file gets verified
+ for accurate data.
+*/
+
+PRSemaphore *emptyBufs; /* number of empty buffers */
+PRSemaphore *fullBufs; /* number of buffers that are full */
+
+#define BSIZE 100
+
+struct {
+ char data[BSIZE];
+ PRUintn nbytes; /* number of bytes in this buffer */
+} buf[2];
+
+static void PR_CALLBACK reader(void *arg)
+{
+ PRUintn i = 0;
+ PRInt32 nbytes;
+
+ do {
+ (void) PR_WaitSem(emptyBufs);
+ nbytes = PR_Read((PRFileDesc*)arg, buf[i].data, BSIZE);
+ if (nbytes >= 0) {
+ buf[i].nbytes = nbytes;
+ PR_PostSem(fullBufs);
+ i = (i + 1) % 2;
+ }
+ } while (nbytes > 0);
+}
+
+static void PR_CALLBACK writer(void *arg)
+{
+ PRUintn i = 0;
+ PRInt32 nbytes;
+
+ do {
+ (void) PR_WaitSem(fullBufs);
+ nbytes = buf[i].nbytes;
+ if (nbytes > 0) {
+ nbytes = PR_Write((PRFileDesc*)arg, buf[i].data, nbytes);
+ PR_PostSem(emptyBufs);
+ i = (i + 1) % 2;
+ }
+ } while (nbytes > 0);
+}
+
+int main(int argc, char **argv)
+{
+ PRThread *r, *w;
+
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("fileio.log");
+ debug_mode = 1;
+#endif
+
+ emptyBufs = PR_NewSem(2); /* two empty buffers */
+
+ fullBufs = PR_NewSem(0); /* zero full buffers */
+
+ /* Create initial temp file setup */
+ InitialSetup();
+
+ /* create the reader thread */
+
+ r = PR_CreateThread(PR_USER_THREAD,
+ reader, t1,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+
+ w = PR_CreateThread(PR_USER_THREAD,
+ writer, t2,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+
+ /* Do the joining for both threads */
+ (void) PR_JoinThread(r);
+ (void) PR_JoinThread(w);
+
+ /* Do the verification and clean up */
+ VerifyAndCleanup();
+
+ PR_DestroySem(emptyBufs);
+ PR_DestroySem(fullBufs);
+
+ PR_Cleanup();
+
+ if(failed_already)
+ {
+ printf("Fail\n");
+ return 1;
+ }
+ else
+ {
+ printf("PASS\n");
+ return 0;
+ }
+
+
+}
diff --git a/pr/tests/foreign.c b/pr/tests/foreign.c
new file mode 100644
index 00000000..b26a5fea
--- /dev/null
+++ b/pr/tests/foreign.c
@@ -0,0 +1,355 @@
+/* -*- 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: foreign.c
+** Description: Testing various functions w/ foreign threads
+**
+** We create a thread and get it to call exactly one runtime function.
+** The thread is allowed to be created by some other environment that
+** NSPR, but it does not announce itself to the runtime prior to calling
+** in.
+**
+** The goal: try to survive.
+**
+*/
+
+#include "prcvar.h"
+#include "prenv.h"
+#include "prerror.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prio.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prmem.h"
+#include "prthread.h"
+#include "prtypes.h"
+#include "prprf.h"
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static enum {
+ thread_nspr, thread_pthread, thread_sproc, thread_win32
+} thread_provider;
+
+typedef void (*StartFn)(void*);
+typedef struct StartObject
+{
+ StartFn start;
+ void *arg;
+} StartObject;
+
+static PRFileDesc *output;
+
+static int _debug_on = 0;
+
+#define DEFAULT_THREAD_COUNT 10
+
+#define DPRINTF(arg) if (_debug_on) PR_fprintf arg
+
+#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
+#include "md/_pth.h"
+#include <pthread.h>
+static void *pthread_start(void *arg)
+{
+ StartFn start = ((StartObject*)arg)->start;
+ void *data = ((StartObject*)arg)->arg;
+ PR_Free(arg);
+ start(data);
+ return NULL;
+} /* pthread_start */
+#endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
+
+#if defined(IRIX) && !defined(_PR_PTHREADS)
+#include <sys/types.h>
+#include <sys/prctl.h>
+static void sproc_start(void *arg, PRSize size)
+{
+ StartObject *so = (StartObject*)arg;
+ StartFn start = so->start;
+ void *data = so->arg;
+ PR_Free(so);
+ start(data);
+} /* sproc_start */
+#endif /* defined(IRIX) && !defined(_PR_PTHREADS) */
+
+#if defined(WIN32)
+#include <process.h> /* for _beginthreadex() */
+
+static PRUintn __stdcall windows_start(void *arg)
+{
+ StartObject *so = (StartObject*)arg;
+ StartFn start = so->start;
+ void *data = so->arg;
+ PR_Free(so);
+ start(data);
+ return 0;
+} /* windows_start */
+#endif /* defined(WIN32) */
+
+static PRStatus CreateThread(StartFn start, void *arg)
+{
+ PRStatus rv;
+
+ switch (thread_provider)
+ {
+ case thread_nspr:
+ {
+ PRThread *thread = PR_CreateThread(
+ PR_USER_THREAD, start, arg,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+ rv = (NULL == thread) ? PR_FAILURE : PR_SUCCESS;
+ }
+ break;
+ case thread_pthread:
+#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
+ {
+ int rv;
+ pthread_t id;
+ pthread_attr_t tattr;
+ StartObject *start_object;
+ start_object = PR_NEW(StartObject);
+ PR_ASSERT(NULL != start_object);
+ start_object->start = start;
+ start_object->arg = arg;
+
+ rv = PTHREAD_ATTR_INIT(&tattr);
+ PR_ASSERT(0 == rv);
+
+ rv = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
+ PR_ASSERT(0 == rv);
+
+#if !defined(LINUX)
+ rv = pthread_attr_setstacksize(&tattr, 64 * 1024);
+ PR_ASSERT(0 == rv);
+#endif
+
+ rv = PTHREAD_CREATE(&id, tattr, pthread_start, start_object);
+ (void)PTHREAD_ATTR_DESTROY(&tattr);
+ return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
+ }
+#else
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ rv = PR_FAILURE;
+ break;
+#endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
+
+ case thread_sproc:
+#if defined(IRIX) && !defined(_PR_PTHREADS)
+ {
+ PRInt32 pid;
+ StartObject *start_object;
+ start_object = PR_NEW(StartObject);
+ PR_ASSERT(NULL != start_object);
+ start_object->start = start;
+ start_object->arg = arg;
+ pid = sprocsp(
+ sproc_start, PR_SALL, start_object, NULL, 64 * 1024);
+ rv = (0 < pid) ? PR_SUCCESS : PR_FAILURE;
+ }
+#else
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ rv = PR_FAILURE;
+#endif /* defined(IRIX) && !defined(_PR_PTHREADS) */
+ break;
+ case thread_win32:
+#if defined(WIN32)
+ {
+ void *th;
+ PRUintn id;
+ StartObject *start_object;
+ start_object = PR_NEW(StartObject);
+ PR_ASSERT(NULL != start_object);
+ start_object->start = start;
+ start_object->arg = arg;
+ th = (void*)_beginthreadex(
+ NULL, /* LPSECURITY_ATTRIBUTES - pointer to thread security attributes */
+ 0U, /* DWORD - initial thread stack size, in bytes */
+ windows_start, /* LPTHREAD_START_ROUTINE - pointer to thread function */
+ start_object, /* LPVOID - argument for new thread */
+ 0U, /*DWORD dwCreationFlags - creation flags */
+ &id /* LPDWORD - pointer to returned thread identifier */ );
+
+ rv = (NULL == th) ? PR_FAILURE : PR_SUCCESS;
+ }
+#else
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ rv = PR_FAILURE;
+#endif
+ break;
+ default:
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ rv = PR_FAILURE;
+ }
+ return rv;
+} /* CreateThread */
+
+static void PR_CALLBACK lazyEntry(void *arg)
+{
+ PR_ASSERT(NULL == arg);
+} /* lazyEntry */
+
+
+static void OneShot(void *arg)
+{
+ PRUintn pdkey;
+ PRLock *lock;
+ PRFileDesc *fd;
+ PRDir *dir;
+ PRFileDesc *pair[2];
+ PRIntn test = (PRIntn)arg;
+
+ for (test = 0; test < 11; ++test) {
+
+ switch (test)
+ {
+ case 0:
+ lock = PR_NewLock();
+ DPRINTF((output,"Thread[0x%x] called PR_NewLock\n",
+ PR_GetCurrentThread()));
+ PR_DestroyLock(lock);
+ break;
+
+ case 1:
+ (void)PR_SecondsToInterval(1);
+ DPRINTF((output,"Thread[0x%x] called PR_SecondsToInterval\n",
+ PR_GetCurrentThread()));
+ break;
+
+ case 2: (void)PR_CreateThread(
+ PR_USER_THREAD, lazyEntry, NULL, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, 0);
+ DPRINTF((output,"Thread[0x%x] called PR_CreateThread\n",
+ PR_GetCurrentThread()));
+ break;
+
+ case 3:
+ fd = PR_Open("foreign.tmp", PR_CREATE_FILE | PR_RDWR, 0666);
+ DPRINTF((output,"Thread[0x%x] called PR_Open\n",
+ PR_GetCurrentThread()));
+ PR_Close(fd);
+ break;
+
+ case 4:
+ fd = PR_NewUDPSocket();
+ DPRINTF((output,"Thread[0x%x] called PR_NewUDPSocket\n",
+ PR_GetCurrentThread()));
+ PR_Close(fd);
+ break;
+
+ case 5:
+ fd = PR_NewTCPSocket();
+ DPRINTF((output,"Thread[0x%x] called PR_NewTCPSocket\n",
+ PR_GetCurrentThread()));
+ PR_Close(fd);
+ break;
+
+ case 6:
+ dir = PR_OpenDir("/usr/tmp/");
+ DPRINTF((output,"Thread[0x%x] called PR_OpenDir\n",
+ PR_GetCurrentThread()));
+ PR_CloseDir(dir);
+ break;
+
+ case 7:
+ (void)PR_NewThreadPrivateIndex(&pdkey, NULL);
+ DPRINTF((output,"Thread[0x%x] called PR_NewThreadPrivateIndex\n",
+ PR_GetCurrentThread()));
+ break;
+
+ case 8:
+ (void)PR_GetEnv("PATH");
+ DPRINTF((output,"Thread[0x%x] called PR_GetEnv\n",
+ PR_GetCurrentThread()));
+ break;
+
+ case 9:
+ (void)PR_NewTCPSocketPair(pair);
+ DPRINTF((output,"Thread[0x%x] called PR_NewTCPSocketPair\n",
+ PR_GetCurrentThread()));
+ PR_Close(pair[0]);
+ PR_Close(pair[1]);
+ break;
+
+ case 10:
+ PR_SetConcurrency(2);
+ DPRINTF((output,"Thread[0x%x] called PR_SetConcurrency\n",
+ PR_GetCurrentThread()));
+ break;
+
+ default:
+ break;
+ } /* switch() */
+ }
+} /* OneShot */
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PRStatus rv;
+ PRIntn test_number;
+ PRInt32 thread_cnt = DEFAULT_THREAD_COUNT;
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dt:");
+
+#if defined(WIN32)
+ thread_provider = thread_win32;
+#elif defined(_PR_PTHREADS)
+ thread_provider = thread_pthread;
+#elif defined(IRIX)
+ thread_provider = thread_sproc;
+#else
+ thread_provider = thread_nspr;
+#endif
+
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ _debug_on = 1;
+ break;
+ case 't': /* thread count */
+ thread_cnt = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_SetConcurrency(2);
+
+ output = PR_GetSpecialFD(PR_StandardOutput);
+
+ while (thread_cnt-- > 0)
+ {
+ rv = CreateThread(OneShot, (void*)thread_cnt);
+ PR_ASSERT(PR_SUCCESS == rv);
+ PR_Sleep(PR_MillisecondsToInterval(5));
+ }
+ PR_Sleep(PR_SecondsToInterval(3));
+ return (PR_SUCCESS == PR_Cleanup()) ? 0 : 1;
+} /* main */
+
+/* foreign.c */
diff --git a/pr/tests/forktest.c b/pr/tests/forktest.c
new file mode 100644
index 00000000..ecb09c5b
--- /dev/null
+++ b/pr/tests/forktest.c
@@ -0,0 +1,327 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+**
+** Name: forktest.c
+**
+** Description: UNIX test for fork functions.
+**
+** Modification History:
+** 15-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+** 12-June-97 AGarcic - Revert to return code 0 and 1, remove debug option (obsolete).
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+PRIntn failed_already=0;
+
+#ifdef XP_UNIX
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+
+static char *message = "Hello world!";
+
+static void
+ClientThreadFunc(void *arg)
+{
+ PRNetAddr addr;
+ PRFileDesc *sock = NULL;
+ PRInt32 tmp = (PRInt32)arg;
+
+ /*
+ * Make sure the PR_Accept call will block
+ */
+
+ printf("Wait one second before connect\n");
+ fflush(stdout);
+ PR_Sleep(PR_SecondsToInterval(1));
+
+ addr.inet.family = AF_INET;
+ addr.inet.ip = PR_htonl(INADDR_ANY);
+ addr.inet.port = 0;
+ if ((sock = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "failed to create TCP socket: error code %d\n",
+ PR_GetError());
+ failed_already = 1;
+ goto finish;
+ }
+ if (PR_Bind(sock, &addr) != PR_SUCCESS) {
+ fprintf(stderr, "PR_Bind failed: error code %d\n",
+ PR_GetError());
+ failed_already = 1;
+ goto finish;
+ }
+ addr.inet.ip = PR_htonl(INADDR_LOOPBACK);
+ addr.inet.port = PR_htons((PRInt16)tmp);
+ printf("Connecting to port %hu\n", PR_ntohs(addr.inet.port));
+ fflush(stdout);
+ if (PR_Connect(sock, &addr, PR_SecondsToInterval(5)) !=
+ PR_SUCCESS) {
+ fprintf(stderr, "PR_Connect failed: error code %d\n",
+ PR_GetError());
+ failed_already = 1;
+ goto finish;
+ }
+ printf("Writing message \"%s\"\n", message);
+ fflush(stdout);
+ if (PR_Send(sock, message, strlen(message) + 1, 0, PR_INTERVAL_NO_TIMEOUT) ==
+ -1) {
+ fprintf(stderr, "PR_Send failed: error code %d\n",
+ PR_GetError());
+ failed_already = 1;
+ goto finish;
+ }
+finish:
+ if (sock) {
+ PR_Close(sock);
+ }
+ return;
+}
+
+/*
+ * DoIO --
+ * This function creates a thread that acts as a client and itself.
+ * acts as a server. Then it joins the client thread.
+ */
+static void
+DoIO(void)
+{
+ PRThread *clientThread;
+ PRFileDesc *listenSock = NULL;
+ PRFileDesc *sock = NULL;
+ PRNetAddr addr;
+ PRInt32 nBytes;
+ char buf[128];
+
+ listenSock = PR_NewTCPSocket();
+ if (!listenSock) {
+ fprintf(stderr, "failed to create a TCP socket: error code %d\n",
+ PR_GetError());
+ failed_already = 1;
+ goto finish;
+ }
+ addr.inet.family = AF_INET;
+ addr.inet.ip = PR_htonl(INADDR_ANY);
+ addr.inet.port = 0;
+ if (PR_Bind(listenSock, &addr) == PR_FAILURE) {
+ fprintf(stderr, "failed to bind socket: error code %d\n",
+ PR_GetError());
+ failed_already = 1;
+ goto finish;
+ }
+ if (PR_GetSockName(listenSock, &addr) == PR_FAILURE) {
+ fprintf(stderr, "failed to get socket port number: error code %d\n",
+ PR_GetError());
+ failed_already = 1;
+ goto finish;
+ }
+ if (PR_Listen(listenSock, 5) == PR_FAILURE) {
+ fprintf(stderr, "PR_Listen failed: error code %d\n",
+ PR_GetError());
+ failed_already = 1;
+ goto finish;
+ }
+ clientThread = PR_CreateThread( PR_USER_THREAD, ClientThreadFunc,
+ (void *) PR_ntohs(addr.inet.port), PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+ PR_JOINABLE_THREAD, 0);
+ if (clientThread == NULL) {
+ fprintf(stderr, "Cannot create client thread: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already = 1;
+ goto finish;
+ }
+ printf("Accepting connection at port %hu\n", PR_ntohs(addr.inet.port));
+ fflush(stdout);
+ sock = PR_Accept(listenSock, &addr, PR_SecondsToInterval(5));
+ if (!sock) {
+ fprintf(stderr, "PR_Accept failed: error code %d\n",
+ PR_GetError());
+ failed_already = 1;
+ goto finish;
+ }
+ nBytes = PR_Recv(sock, buf, sizeof(buf), 0, PR_INTERVAL_NO_TIMEOUT);
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Recv failed: error code %d\n",
+ PR_GetError());
+ failed_already = 1;
+ goto finish;
+ }
+
+ /*
+ * Make sure it has proper null byte to mark end of string
+ */
+
+ buf[sizeof(buf) - 1] = '\0';
+ printf("Received \"%s\" from the client\n", buf);
+ fflush(stdout);
+ if (!strcmp(buf, message)) {
+ PR_JoinThread(clientThread);
+
+ printf("The message is received correctly\n");
+ fflush(stdout);
+ } else {
+ fprintf(stderr, "The message should be \"%s\"\n",
+ message);
+ failed_already = 1;
+ }
+
+finish:
+ if (listenSock) {
+ PR_Close(listenSock);
+ }
+ if (sock) {
+ PR_Close(sock);
+ }
+ return;
+}
+
+#ifdef _PR_DCETHREADS
+
+#include <syscall.h>
+
+pid_t PR_UnixFork1(void)
+{
+ pid_t parent = getpid();
+ int rv = syscall(SYS_fork);
+
+ if (rv == -1) {
+ return (pid_t) -1;
+ } else {
+ /* For each process, rv is the pid of the other process */
+ if (rv == parent) {
+ /* the child */
+ return 0;
+ } else {
+ /* the parent */
+ return rv;
+ }
+ }
+}
+
+#elif defined(SOLARIS)
+
+/*
+ * It seems like that in Solaris 2.4 one must call fork1() if the
+ * the child process is going to use thread functions. Solaris 2.5
+ * doesn't have this problem. Calling fork() also works.
+ */
+
+pid_t PR_UnixFork1(void)
+{
+ return fork1();
+}
+
+#else
+
+pid_t PR_UnixFork1(void)
+{
+ return fork();
+}
+
+#endif /* PR_DCETHREADS */
+
+int main(
+int argc,
+char *argv[]
+)
+{
+ pid_t pid;
+ int rv;
+
+ /* main test program */
+
+ DoIO();
+
+ pid = PR_UnixFork1();
+
+ if (pid == (pid_t) -1) {
+ fprintf(stderr, "Fork failed: errno %d\n", errno);
+ failed_already=1;
+ return 1;
+ } else if (pid > 0) {
+ int childStatus;
+
+ printf("Fork succeeded. Parent process continues.\n");
+ DoIO();
+ if ((rv = waitpid(pid, &childStatus, 0)) != pid) {
+#if defined(IRIX) && !defined(_PR_PTHREADS)
+ /*
+ * nspr may handle SIGCLD signal
+ */
+ if ((rv < 0) && (errno == ECHILD)) {
+ } else
+#endif
+ {
+ fprintf(stderr, "waitpid failed: %d\n", errno);
+ failed_already = 1;
+ }
+ } else if (!WIFEXITED(childStatus)
+ || WEXITSTATUS(childStatus) != 0) {
+ failed_already = 1;
+ }
+ printf("Parent process exits.\n");
+ if (!failed_already) {
+ printf("PASSED\n");
+ } else {
+ printf("FAILED\n");
+ }
+ return failed_already;
+ } else {
+#if defined(IRIX) && !defined(_PR_PTHREADS)
+ extern void _PR_IRIX_CHILD_PROCESS(void);
+ _PR_IRIX_CHILD_PROCESS();
+#endif
+ printf("Fork succeeded. Child process continues.\n");
+ DoIO();
+ printf("Child process exits.\n");
+ return failed_already;
+ }
+}
+
+#else /* XP_UNIX */
+
+int main( int argc,
+char *argv[]
+)
+{
+
+ printf("The fork test is applicable to Unix only.\n");
+ return 0;
+
+}
+
+#endif /* XP_UNIX */
diff --git a/pr/tests/fsync.c b/pr/tests/fsync.c
new file mode 100644
index 00000000..5a51a330
--- /dev/null
+++ b/pr/tests/fsync.c
@@ -0,0 +1,136 @@
+/* -*- 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 "prio.h"
+#include "prmem.h"
+#include "prprf.h"
+#include "prinrval.h"
+
+#include "plerror.h"
+#include "plgetopt.h"
+
+static PRFileDesc *err = NULL;
+
+static void Help(void)
+{
+ PR_fprintf(err, "Usage: [-S] [-K <n>] [-h] <filename>\n");
+ PR_fprintf(err, "\t-c Nuber of iterations (default: 10)\n");
+ PR_fprintf(err, "\t-S Sync the file (default: FALSE)\n");
+ PR_fprintf(err, "\t-K Size of file (K bytes) (default: 10)\n");
+ PR_fprintf(err, "\t Name of file to write (default: /usr/tmp/sync.dat)\n");
+ PR_fprintf(err, "\t-h This message and nothing else\n");
+} /* Help */
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PRStatus rv;
+ PLOptStatus os;
+ PRUint8 *buffer;
+ PRFileDesc *file = NULL;
+ const char *filename = "sync.dat";
+ PRUint32 index, loops, iterations = 10, filesize = 10;
+ PRIntn flags = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "hSK:c:");
+ PRIntervalTime time, total = 0, shortest = 0x7fffffff, longest = 0;
+
+ err = PR_GetSpecialFD(PR_StandardError);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 0: /* Name of file to create */
+ filename = opt->value;
+ break;
+ case 'S': /* Use sych option on file */
+ flags |= PR_SYNC;
+ break;
+ case 'K': /* Size of file to write */
+ filesize = atoi(opt->value);
+ break;
+ case 'c': /* Number of iterations */
+ iterations = atoi(opt->value);
+ break;
+ case 'h': /* user wants some guidance */
+ default: /* user needs some guidance */
+ Help(); /* so give him an earful */
+ return 2; /* but not a lot else */
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ file = PR_Open(filename, flags, 0666);
+ if (NULL == file)
+ {
+ PL_FPrintError(err, "Failed to open file");
+ return 1;
+ }
+
+ buffer = (PRUint8*)PR_CALLOC(1024);
+ if (NULL == buffer)
+ {
+ PL_FPrintError(err, "Cannot allocate buffer");
+ return 1;
+ }
+
+ for (index = 0; index < sizeof(buffer); ++index)
+ buffer[index] = (PRUint8)index;
+
+ for (loops = 0; loops < iterations; ++loops)
+ {
+ time = PR_IntervalNow();
+ for (index = 0; index < filesize; ++index)
+ {
+ PR_Write(file, buffer, 1024);
+ }
+ time = (PR_IntervalNow() - time);
+
+ total += time;
+ if (time < shortest) shortest = time;
+ else if (time > longest) longest = time;
+ if (0 != PR_Seek(file, 0, PR_SEEK_SET))
+ {
+ PL_FPrintError(err, "Rewinding file");
+ return 1;
+ }
+ }
+
+ total = total / iterations;
+ PR_fprintf(
+ err, "%u iterations over a %u kbyte %sfile: %u [%u] %u\n",
+ iterations, filesize, ((flags & PR_SYNC) ? "SYNCH'd " : ""),
+ PR_IntervalToMicroseconds(shortest),
+ PR_IntervalToMicroseconds(total),
+ PR_IntervalToMicroseconds(longest));
+
+ PR_DELETE(buffer);
+ rv = PR_Close(file);
+ if (PR_SUCCESS != rv)
+ {
+ PL_FPrintError(err, "Closing file failed");
+ return 1;
+ }
+ rv = PR_Delete(filename);
+ if (PR_SUCCESS != rv)
+ {
+ PL_FPrintError(err, "Deleting file failed");
+ return 1;
+ }
+ return 0;
+}
diff --git a/pr/tests/getproto.c b/pr/tests/getproto.c
new file mode 100644
index 00000000..47b83552
--- /dev/null
+++ b/pr/tests/getproto.c
@@ -0,0 +1,95 @@
+/* -*- 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: getproto.c
+ *
+ * A test program for PR_GetProtoByName and PR_GetProtoByNumber
+ *
+ *************************************************************************
+ */
+
+#include "plstr.h"
+#include "plerror.h"
+#include "prinit.h"
+#include "prprf.h"
+#include "prnetdb.h"
+#include "prerror.h"
+
+int main()
+{
+ PRFileDesc *prstderr = PR_GetSpecialFD(PR_StandardError);
+ PRBool failed = PR_FALSE;
+ PRProtoEnt proto;
+ char buf[2048];
+ PRStatus rv;
+
+ PR_STDIO_INIT();
+ rv = PR_GetProtoByName("tcp", buf, sizeof(buf), &proto);
+ if (PR_FAILURE == rv) {
+ failed = PR_TRUE;
+ PL_FPrintError(prstderr, "PR_GetProtoByName failed");
+ }
+ else if (6 != proto.p_num) {
+ PR_fprintf(
+ prstderr,"tcp is usually 6, but is %d on this machine\n",
+ proto.p_num);
+ }
+ else PR_fprintf(prstderr, "tcp is protocol number %d\n", proto.p_num);
+
+ rv = PR_GetProtoByName("udp", buf, sizeof(buf), &proto);
+ if (PR_FAILURE == rv) {
+ failed = PR_TRUE;
+ PL_FPrintError(prstderr, "PR_GetProtoByName failed");
+ }
+ else if (17 != proto.p_num) {
+ PR_fprintf(
+ prstderr, "udp is usually 17, but is %d on this machine\n",
+ proto.p_num);
+ }
+ else PR_fprintf(prstderr, "udp is protocol number %d\n", proto.p_num);
+
+ rv = PR_GetProtoByNumber(6, buf, sizeof(buf), &proto);
+ if (PR_FAILURE == rv) {
+ failed = PR_TRUE;
+ PL_FPrintError(prstderr, "PR_GetProtoByNumber failed");
+ }
+ else if (PL_strcmp("tcp", proto.p_name)) {
+ PR_fprintf(
+ prstderr, "Protocol number 6 is usually tcp, but is %s"
+ " on this platform\n", proto.p_name);
+ }
+ else PR_fprintf(prstderr, "Protocol number 6 is %s\n", proto.p_name);
+
+ rv = PR_GetProtoByNumber(17, buf, sizeof(buf), &proto);
+ if (PR_FAILURE == rv) {
+ failed = PR_TRUE;
+ PL_FPrintError(prstderr, "PR_GetProtoByNumber failed");
+ }
+ else if (PL_strcmp("udp", proto.p_name)) {
+ PR_fprintf(
+ prstderr, "Protocol number 17 is usually udp, but is %s"
+ " on this platform\n", proto.p_name);
+ }
+ else PR_fprintf(prstderr, "Protocol number 17 is %s\n", proto.p_name);
+
+ PR_fprintf(prstderr, (failed) ? "FAILED\n" : "PASSED\n");
+ return (failed) ? 1 : 0;
+}
diff --git a/pr/tests/i2l.c b/pr/tests/i2l.c
new file mode 100644
index 00000000..63031fb7
--- /dev/null
+++ b/pr/tests/i2l.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.
+ */
+
+#include <stdlib.h>
+
+#include "prio.h"
+#include "prinit.h"
+#include "prprf.h"
+#include "prlong.h"
+
+#include "plerror.h"
+#include "plgetopt.h"
+
+typedef union Overlay_i
+{
+ PRInt32 i;
+ PRInt64 l;
+} Overlay_i;
+
+typedef union Overlay_u
+{
+ PRUint32 i;
+ PRUint64 l;
+} Overlay_u;
+
+static PRFileDesc *err = NULL;
+
+static void Help(void)
+{
+ PR_fprintf(err, "Usage: -i n | -u n | -h\n");
+ PR_fprintf(err, "\t-i n treat following number as signed integer\n");
+ PR_fprintf(err, "\t-u n treat following number as unsigned integer\n");
+ PR_fprintf(err, "\t-h This message and nothing else\n");
+} /* Help */
+
+static PRIntn PR_CALLBACK RealMain(PRIntn argc, char **argv)
+{
+ Overlay_i si;
+ Overlay_u ui;
+ PLOptStatus os;
+ PRBool bsi = PR_FALSE, bui = PR_FALSE;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "hi:u:");
+ err = PR_GetSpecialFD(PR_StandardError);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'i': /* signed integer */
+ si.i = (PRInt32)atoi(opt->value);
+ bsi = PR_TRUE;
+ break;
+ case 'u': /* unsigned */
+ ui.i = (PRUint32)atoi(opt->value);
+ bui = PR_TRUE;
+ break;
+ case 'h': /* user wants some guidance */
+ default:
+ Help(); /* so give him an earful */
+ return 2; /* but not a lot else */
+ }
+ }
+ PL_DestroyOptState(opt);
+
+#if defined(HAVE_LONG_LONG)
+ PR_fprintf(err, "We have long long\n");
+#else
+ PR_fprintf(err, "We don't have long long\n");
+#endif
+
+ if (bsi)
+ {
+ PR_fprintf(err, "Converting %ld: ", si.i);
+ LL_I2L(si.l, si.i);
+ PR_fprintf(err, "%lld\n", si.l);
+ }
+
+ if (bui)
+ {
+ PR_fprintf(err, "Converting %lu: ", ui.i);
+ LL_I2L(ui.l, ui.i);
+ PR_fprintf(err, "%llu\n", ui.l);
+ }
+ return 0;
+
+} /* main */
+
+
+PRIntn main(PRIntn argc, char *argv[])
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
+
+/* i2l.c */
diff --git a/pr/tests/initclk.c b/pr/tests/initclk.c
new file mode 100644
index 00000000..a815997f
--- /dev/null
+++ b/pr/tests/initclk.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.
+ */
+
+/*
+ * This is a regression test for the bug that the interval timer
+ * is not initialized when _PR_CreateCPU calls PR_IntervalNow.
+ * The bug would make this test program finish prematurely,
+ * when the SHORT_TIMEOUT period expires. The correct behavior
+ * is for the test to finish when the LONG_TIMEOUT period expires.
+ */
+
+#include "prlock.h"
+#include "prcvar.h"
+#include "prthread.h"
+#include "prinrval.h"
+#include "prlog.h"
+#include <stdio.h>
+
+/* The timeouts, in milliseconds */
+#define SHORT_TIMEOUT 1000
+#define LONG_TIMEOUT 3000
+
+PRLock *lock1, *lock2;
+PRCondVar *cv1, *cv2;
+
+void ThreadFunc(void *arg)
+{
+ PR_Lock(lock1);
+ PR_WaitCondVar(cv1, PR_MillisecondsToInterval(SHORT_TIMEOUT));
+ PR_Unlock(lock1);
+}
+
+int main()
+{
+ PRThread *thread;
+ PRIntervalTime start, end;
+ PRUint32 elapsed_ms;
+
+ lock1 = PR_NewLock();
+ PR_ASSERT(NULL != lock1);
+ cv1 = PR_NewCondVar(lock1);
+ PR_ASSERT(NULL != cv1);
+ lock2 = PR_NewLock();
+ PR_ASSERT(NULL != lock2);
+ cv2 = PR_NewCondVar(lock2);
+ PR_ASSERT(NULL != cv2);
+ start = PR_IntervalNow();
+ thread = PR_CreateThread(
+ PR_USER_THREAD,
+ ThreadFunc,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+ PR_ASSERT(NULL != thread);
+ PR_Lock(lock2);
+ PR_WaitCondVar(cv2, PR_MillisecondsToInterval(LONG_TIMEOUT));
+ PR_Unlock(lock2);
+ PR_JoinThread(thread);
+ end = PR_IntervalNow();
+ elapsed_ms = PR_IntervalToMilliseconds((PRIntervalTime)(end - start));
+ /* Allow 100ms imprecision */
+ if (elapsed_ms < LONG_TIMEOUT - 100 || elapsed_ms > LONG_TIMEOUT + 100) {
+ printf("Elapsed time should be %u ms but is %u ms\n",
+ LONG_TIMEOUT, elapsed_ms);
+ printf("FAIL\n");
+ exit(1);
+ }
+ printf("PASS\n");
+ return 0;
+}
diff --git a/pr/tests/inrval.c b/pr/tests/inrval.c
new file mode 100644
index 00000000..ce1df9cc
--- /dev/null
+++ b/pr/tests/inrval.c
@@ -0,0 +1,223 @@
+/* -*- 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: inrval.c
+** description: Interval conversion test.
+** Modification History:
+** 15-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+**/
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "prinit.h"
+#ifdef XP_MAC
+#include "pralarm.h"
+#else
+#include "obsolete/pralarm.h"
+#endif
+
+#include "prio.h"
+#include "prprf.h"
+#include "prlock.h"
+#include "prlong.h"
+#include "prcvar.h"
+#include "prinrval.h"
+#include "prtime.h"
+
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static PRIntn debug_mode;
+static PRFileDesc *output;
+
+
+static void TestConversions(void)
+{
+ PRIntervalTime ticks = PR_TicksPerSecond();
+
+ if (debug_mode) {
+ PR_fprintf(output, "PR_TicksPerSecond: %ld\n\n", ticks);
+ PR_fprintf(output, "PR_SecondsToInterval(1): %ld\n", PR_SecondsToInterval(1));
+ PR_fprintf(output, "PR_MillisecondsToInterval(1000): %ld\n", PR_MillisecondsToInterval(1000));
+ PR_fprintf(output, "PR_MicrosecondsToInterval(1000000): %ld\n\n", PR_MicrosecondsToInterval(1000000));
+
+ PR_fprintf(output, "PR_SecondsToInterval(3): %ld\n", PR_SecondsToInterval(3));
+ PR_fprintf(output, "PR_MillisecondsToInterval(3000): %ld\n", PR_MillisecondsToInterval(3000));
+ PR_fprintf(output, "PR_MicrosecondsToInterval(3000000): %ld\n\n", PR_MicrosecondsToInterval(3000000));
+
+ PR_fprintf(output, "PR_IntervalToSeconds(%ld): %ld\n", ticks, PR_IntervalToSeconds(ticks));
+ PR_fprintf(output, "PR_IntervalToMilliseconds(%ld): %ld\n", ticks, PR_IntervalToMilliseconds(ticks));
+ PR_fprintf(output, "PR_IntervalToMicroseconds(%ld): %ld\n\n", ticks, PR_IntervalToMicroseconds(ticks));
+
+ ticks *= 3;
+ PR_fprintf(output, "PR_IntervalToSeconds(%ld): %ld\n", ticks, PR_IntervalToSeconds(ticks));
+ PR_fprintf(output, "PR_IntervalToMilliseconds(%ld): %ld\n", ticks, PR_IntervalToMilliseconds(ticks));
+ PR_fprintf(output, "PR_IntervalToMicroseconds(%ld): %ld\n\n", ticks, PR_IntervalToMicroseconds(ticks));
+ } /*end debug mode */
+} /* TestConversions */
+
+static void TestIntervalOverhead(void)
+{
+ /* Hopefully the optimizer won't delete this function */
+ PRUint32 elapsed, per_call, loops = 1000000;
+
+ PRIntervalTime timeout, timein = PR_IntervalNow();
+ while (--loops > 0)
+ timeout = PR_IntervalNow();
+
+ elapsed = 1000U * PR_IntervalToMicroseconds(timeout - timein);
+ per_call = elapsed / 1000000U;
+ PR_fprintf(
+ output, "Overhead of 'PR_IntervalNow()' is %u nsecs\n\n", per_call);
+} /* TestIntervalOverhead */
+
+static void TestNowOverhead(void)
+{
+ PRTime timeout, timein;
+ PRInt32 overhead, loops = 1000000;
+ PRInt64 elapsed, per_call, ten23rd, ten26th;
+
+ LL_I2L(ten23rd, 1000);
+ LL_I2L(ten26th, 1000000);
+
+ timein = PR_Now();
+ while (--loops > 0)
+ timeout = PR_Now();
+
+ LL_SUB(elapsed, timeout, timein);
+ LL_MUL(elapsed, elapsed, ten23rd);
+ LL_DIV(per_call, elapsed, ten26th);
+ LL_L2I(overhead, per_call);
+ PR_fprintf(
+ output, "Overhead of 'PR_Now()' is %u nsecs\n\n", overhead);
+} /* TestNowOverhead */
+
+static void TestIntervals(void)
+{
+ PRStatus rv;
+ PRUint32 delta;
+ PRInt32 seconds;
+ PRUint64 elapsed, thousand;
+ PRTime timein, timeout;
+ PRLock *ml = PR_NewLock();
+ PRCondVar *cv = PR_NewCondVar(ml);
+ for (seconds = 0; seconds < 10; ++seconds)
+ {
+ PRIntervalTime ticks = PR_SecondsToInterval(seconds);
+ PR_Lock(ml);
+ timein = PR_Now();
+ rv = PR_WaitCondVar(cv, ticks);
+ timeout = PR_Now();
+ PR_Unlock(ml);
+ LL_SUB(elapsed, timeout, timein);
+ LL_I2L(thousand, 1000);
+ LL_DIV(elapsed, elapsed, thousand);
+ LL_L2UI(delta, elapsed);
+ if (debug_mode) PR_fprintf(output,
+ "TestIntervals: %swaiting %ld seconds took %ld msecs\n",
+ ((rv == PR_SUCCESS) ? "" : "FAILED "), seconds, delta);
+ }
+ PR_DestroyCondVar(cv);
+ PR_DestroyLock(ml);
+ if (debug_mode) PR_fprintf(output, "\n");
+} /* TestIntervals */
+
+static PRIntn PR_CALLBACK RealMain(int argc, char** argv)
+{
+ PRUint32 vcpu, cpus = 0, loops = 1000;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+
+ /* main test */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dl:c:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ case 'c': /* concurrency counter */
+ cpus = atoi(opt->value);
+ break;
+ case 'l': /* loop counter */
+ loops = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ output = PR_GetSpecialFD(PR_StandardOutput);
+ PR_fprintf(output, "inrval: Examine stdout to determine results.\n");
+
+ if (cpus == 0) cpus = 8;
+ if (loops == 0) loops = 1000;
+
+ if (debug_mode > 0)
+ {
+ PR_fprintf(output, "Inrval: Using %d loops\n", loops);
+ PR_fprintf(output, "Inrval: Using 1 and %d cpu(s)\n", cpus);
+ }
+
+ for (vcpu = 1; vcpu <= cpus; vcpu += cpus - 1)
+ {
+ if (debug_mode)
+ PR_fprintf(output, "\nInrval: Using %d CPU(s)\n\n", vcpu);
+ PR_SetConcurrency(vcpu);
+
+ TestNowOverhead();
+ TestIntervalOverhead();
+ TestConversions();
+ TestIntervals();
+ }
+
+ return 0;
+}
+
+
+PRIntn main(PRIntn argc, char *argv[])
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
+
diff --git a/pr/tests/instrumt.c b/pr/tests/instrumt.c
new file mode 100644
index 00000000..ee2b33b5
--- /dev/null
+++ b/pr/tests/instrumt.c
@@ -0,0 +1,469 @@
+/* -*- 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: instrumt.c
+** Description: This test is for the NSPR debug aids defined in
+** prcountr.h, prtrace.h, prolock.h
+**
+** The test case tests the three debug aids in NSPR:
+**
+** Diagnostic messages can be enabled using "instrumt -v 6"
+** This sets the msgLevel to something that PR_LOG() likes.
+** Also define in the environment "NSPR_LOG_MODULES=Test:6"
+**
+** CounterTest() tests the counter facility. This test
+** creates 4 threads. Each thread either increments, decrements,
+** adds to or subtracts from a counter, depending on an argument
+** passed to the thread at thread-create time. Each of these threads
+** does COUNT_LIMIT iterations doing its thing. When all 4 threads
+** are done, the result of the counter is evaluated. If all was atomic,
+** the the value of the counter should be zero.
+**
+** TraceTest():
+** This test mingles with the counter test. Counters trace.
+** A thread to extract trace entries on the fly is started.
+** A thread to dump trace entries to a file is started.
+**
+** OrderedLockTest():
+**
+**
+**
+**
+**
+*/
+
+#include <stdio.h>
+#include <plstr.h>
+#include <prclist.h>
+#include <prmem.h>
+#include <plgetopt.h>
+#include <prlog.h>
+#include <prmon.h>
+#include <pratom.h>
+#include <prtrace.h>
+#include <prcountr.h>
+#include <prolock.h>
+
+#define COUNT_LIMIT (10 * ( 1024))
+
+#define SMALL_TRACE_BUFSIZE ( 60 * 1024 )
+
+typedef enum
+{
+ CountLoop = 1,
+ TraceLoop = 2,
+ TraceFlow = 3
+} TraceTypes;
+
+
+PRLogModuleLevel msgLevel = PR_LOG_ALWAYS;
+
+PRBool help = PR_FALSE;
+PRBool failed = PR_FALSE;
+
+
+PRLogModuleInfo *lm;
+PRMonitor *mon;
+PRInt32 activeThreads = 0;
+PR_DEFINE_COUNTER( hCounter );
+PR_DEFINE_TRACE( hTrace );
+
+static void Help(void)
+{
+ printf("Help? ... Ha!\n");
+}
+
+static void ListCounters(void)
+{
+ PR_DEFINE_COUNTER( qh );
+ PR_DEFINE_COUNTER( rh );
+ char *qn, *rn, *dn;
+ char **qname = &qn, **rname = &rn, **desc = &dn;
+ PRUint32 tCtr;
+
+ PR_INIT_COUNTER_HANDLE( qh, NULL );
+ PR_FIND_NEXT_COUNTER_QNAME(qh, qh );
+ while ( qh != NULL )
+ {
+ PR_INIT_COUNTER_HANDLE( rh, NULL );
+ PR_FIND_NEXT_COUNTER_RNAME(rh, rh, qh );
+ while ( rh != NULL )
+ {
+ PR_GET_COUNTER_NAME_FROM_HANDLE( rh, qname, rname, desc );
+ tCtr = PR_GET_COUNTER(tCtr, rh);
+ PR_LOG( lm, msgLevel,
+ ( "QName: %s RName: %s Desc: %s Value: %ld\n",
+ qn, rn, dn, tCtr ));
+ PR_FIND_NEXT_COUNTER_RNAME(rh, rh, qh );
+ }
+ PR_FIND_NEXT_COUNTER_QNAME(qh, qh);
+ }
+ return;
+} /* end ListCounters() */
+
+static void ListTraces(void)
+{
+ PR_DEFINE_TRACE( qh );
+ PR_DEFINE_TRACE( rh );
+ char *qn, *rn, *dn;
+ char **qname = &qn, **rname = &rn, **desc = &dn;
+
+ PR_INIT_TRACE_HANDLE( qh, NULL );
+ PR_FIND_NEXT_TRACE_QNAME(qh, qh );
+ while ( qh != NULL )
+ {
+ PR_INIT_TRACE_HANDLE( rh, NULL );
+ PR_FIND_NEXT_TRACE_RNAME(rh, rh, qh );
+ while ( rh != NULL )
+ {
+ PR_GET_TRACE_NAME_FROM_HANDLE( rh, qname, rname, desc );
+ PR_LOG( lm, msgLevel,
+ ( "QName: %s RName: %s Desc: %s",
+ qn, rn, dn ));
+ PR_FIND_NEXT_TRACE_RNAME(rh, rh, qh );
+ }
+ PR_FIND_NEXT_TRACE_QNAME(qh, qh);
+ }
+ return;
+} /* end ListCounters() */
+
+
+static PRInt32 one = 1;
+static PRInt32 two = 2;
+static PRInt32 three = 3;
+static PRInt32 four = 4;
+
+/*
+** Thread to iteratively count something.
+*/
+static void PR_CALLBACK CountSomething( void *arg )
+{
+ PRInt32 switchVar = *((PRInt32 *)arg);
+ PRInt32 i;
+
+ PR_LOG( lm, msgLevel,
+ ("CountSomething: begin thread %ld", switchVar ));
+
+ for ( i = 0; i < COUNT_LIMIT ; i++)
+ {
+ switch ( switchVar )
+ {
+ case 1 :
+ PR_INCREMENT_COUNTER( hCounter );
+ break;
+ case 2 :
+ PR_DECREMENT_COUNTER( hCounter );
+ break;
+ case 3 :
+ PR_ADD_TO_COUNTER( hCounter, 1 );
+ break;
+ case 4 :
+ PR_SUBTRACT_FROM_COUNTER( hCounter, 1 );
+ break;
+ default :
+ PR_ASSERT( 0 );
+ break;
+ }
+ PR_TRACE( hTrace, CountLoop, switchVar, i, 0, 0, 0, 0, 0 );
+ } /* end for() */
+
+ PR_LOG( lm, msgLevel,
+ ("CounterSomething: end thread %ld", switchVar ));
+
+ PR_EnterMonitor(mon);
+ --activeThreads;
+ PR_Notify( mon );
+ PR_ExitMonitor(mon);
+
+ return;
+} /* end CountSomething() */
+
+/*
+** Create the counter threads.
+*/
+static void CounterTest( void )
+{
+ PRThread *t1, *t2, *t3, *t4;
+ PR_DEFINE_COUNTER( tc );
+
+ PR_LOG( lm, msgLevel,
+ ("Begin CounterTest"));
+
+ activeThreads += 4;
+ PR_CREATE_COUNTER( hCounter, "Atomic", "SMP Tests", "test atomic nature of counter" );
+
+ PR_GET_COUNTER_HANDLE_FROM_NAME( tc, "Atomic", "SMP Tests" );
+ PR_ASSERT( tc == hCounter );
+
+ t1 = PR_CreateThread(PR_USER_THREAD,
+ CountSomething, &one,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ PR_ASSERT(t1);
+
+ t2 = PR_CreateThread(PR_USER_THREAD,
+ CountSomething, &two,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ PR_ASSERT(t2);
+
+ t3 = PR_CreateThread(PR_USER_THREAD,
+ CountSomething, &three,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ PR_ASSERT(t3);
+
+ t4 = PR_CreateThread(PR_USER_THREAD,
+ CountSomething, &four,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ PR_ASSERT(t4);
+
+ PR_LOG( lm, msgLevel,
+ ("Counter Threads started"));
+
+ ListCounters();
+ return;
+} /* end CounterTest() */
+
+/*
+** Thread to dump trace buffer to a file.
+*/
+static void PR_CALLBACK RecordTrace(void *arg )
+{
+ PR_RECORD_TRACE_ENTRIES();
+
+ PR_EnterMonitor(mon);
+ --activeThreads;
+ PR_Notify( mon );
+ PR_ExitMonitor(mon);
+
+ return;
+} /* end RecordTrace() */
+
+
+
+#define NUM_TRACE_RECORDS ( 10000 )
+/*
+** Thread to extract and print trace entries from the buffer.
+*/
+static void PR_CALLBACK SampleTrace( void *arg )
+{
+#if defined(DEBUG) || defined(FORCE_NSPR_TRACE)
+ PRInt32 found, rc;
+ PRTraceEntry *foundEntries;
+ PRInt32 i;
+
+ foundEntries = (PRTraceEntry *)PR_Malloc( NUM_TRACE_RECORDS * sizeof(PRTraceEntry));
+ PR_ASSERT(foundEntries != NULL );
+
+ do
+ {
+ rc = PR_GetTraceEntries( foundEntries, NUM_TRACE_RECORDS, &found);
+ PR_LOG( lm, msgLevel,
+ ("SampleTrace: Lost Data: %ld found: %ld", rc, found ));
+
+ if ( found != 0)
+ {
+ for ( i = 0 ; i < found; i++ )
+ {
+ PR_LOG( lm, msgLevel,
+ ("SampleTrace, detail: Thread: %p, Time: %llX, UD0: %ld, UD1: %ld, UD2: %8.8ld",
+ (foundEntries +i)->thread,
+ (foundEntries +i)->time,
+ (foundEntries +i)->userData[0],
+ (foundEntries +i)->userData[1],
+ (foundEntries +i)->userData[2] ));
+ }
+ }
+ PR_Sleep(PR_MillisecondsToInterval(50));
+ }
+ while( found != 0 && activeThreads >= 1 );
+
+ PR_Free( foundEntries );
+
+ PR_EnterMonitor(mon);
+ --activeThreads;
+ PR_Notify( mon );
+ PR_ExitMonitor(mon);
+
+ PR_LOG( lm, msgLevel,
+ ("SampleTrace(): exiting"));
+
+#endif
+ return;
+} /* end RecordTrace() */
+
+/*
+** Basic trace test.
+*/
+static void TraceTest( void )
+{
+ PRInt32 i;
+ PRInt32 size;
+ PR_DEFINE_TRACE( th );
+ PRThread *t1, *t2;
+
+ PR_LOG( lm, msgLevel,
+ ("Begin TraceTest"));
+
+ size = SMALL_TRACE_BUFSIZE;
+ PR_SET_TRACE_OPTION( PRTraceBufSize, &size );
+ PR_GET_TRACE_OPTION( PRTraceBufSize, &i );
+
+ PR_CREATE_TRACE( th, "TraceTest", "tt2", "A description for the trace test" );
+ PR_CREATE_TRACE( th, "TraceTest", "tt3", "A description for the trace test" );
+ PR_CREATE_TRACE( th, "TraceTest", "tt4", "A description for the trace test" );
+ PR_CREATE_TRACE( th, "TraceTest", "tt5", "A description for the trace test" );
+ PR_CREATE_TRACE( th, "TraceTest", "tt6", "A description for the trace test" );
+ PR_CREATE_TRACE( th, "TraceTest", "tt7", "A description for the trace test" );
+ PR_CREATE_TRACE( th, "TraceTest", "tt8", "A description for the trace test" );
+
+ PR_CREATE_TRACE( th, "Trace Test", "tt0", "QName is Trace Test, not TraceTest" );
+ PR_CREATE_TRACE( th, "Trace Test", "tt1", "QName is Trace Test, not TraceTest" );
+ PR_CREATE_TRACE( th, "Trace Test", "tt2", "QName is Trace Test, not TraceTest" );
+ PR_CREATE_TRACE( th, "Trace Test", "tt3", "QName is Trace Test, not TraceTest" );
+ PR_CREATE_TRACE( th, "Trace Test", "tt4", "QName is Trace Test, not TraceTest" );
+ PR_CREATE_TRACE( th, "Trace Test", "tt5", "QName is Trace Test, not TraceTest" );
+ PR_CREATE_TRACE( th, "Trace Test", "tt6", "QName is Trace Test, not TraceTest" );
+ PR_CREATE_TRACE( th, "Trace Test", "tt7", "QName is Trace Test, not TraceTest" );
+ PR_CREATE_TRACE( th, "Trace Test", "tt8", "QName is Trace Test, not TraceTest" );
+ PR_CREATE_TRACE( th, "Trace Test", "tt9", "QName is Trace Test, not TraceTest" );
+ PR_CREATE_TRACE( th, "Trace Test", "tt10", "QName is Trace Test, not TraceTest" );
+
+
+
+ activeThreads += 2;
+ t1 = PR_CreateThread(PR_USER_THREAD,
+ RecordTrace, NULL,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ PR_ASSERT(t1);
+
+ t2 = PR_CreateThread(PR_USER_THREAD,
+ SampleTrace, 0,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ PR_ASSERT(t2);
+
+ ListTraces();
+
+ PR_GET_TRACE_HANDLE_FROM_NAME( th, "TraceTest","tt1" );
+ PR_ASSERT( th == hTrace );
+
+ PR_LOG( lm, msgLevel,
+ ("End TraceTest"));
+ return;
+} /* end TraceTest() */
+
+
+/*
+** Ordered lock test.
+*/
+static void OrderedLockTest( void )
+{
+ PR_LOG( lm, msgLevel,
+ ("Begin OrderedLockTest"));
+
+
+} /* end OrderedLockTest() */
+
+
+PRIntn main(PRIntn argc, char *argv[])
+{
+#if defined(DEBUG) || defined(FORCE_NSPR_TRACE)
+ PRUint32 counter;
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "hdv:");
+ lm = PR_NewLogModule("Test");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'v': /* verbose mode */
+ msgLevel = (PRLogModuleLevel)atol( opt->value);
+ break;
+ case 'h': /* help message */
+ Help();
+ help = PR_TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_CREATE_TRACE( hTrace, "TraceTest", "tt1", "A description for the trace test" );
+ mon = PR_NewMonitor();
+ PR_EnterMonitor( mon );
+
+ TraceTest();
+ CounterTest();
+ OrderedLockTest();
+
+ /* Wait for all threads to exit */
+ while ( activeThreads > 0 ) {
+ if ( activeThreads == 1 )
+ PR_SET_TRACE_OPTION( PRTraceStopRecording, NULL );
+ PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
+ PR_GET_COUNTER( counter, hCounter );
+ }
+ PR_ExitMonitor( mon );
+
+ /*
+ ** Evaluate results
+ */
+ PR_GET_COUNTER( counter, hCounter );
+ if ( counter != 0 )
+ {
+ failed = PR_TRUE;
+ PR_LOG( lm, msgLevel,
+ ("Expected counter == 0, found: %ld", counter));
+ printf("FAIL\n");
+ }
+ else
+ {
+ printf("PASS\n");
+ }
+
+ PR_DestroyMonitor( mon );
+
+ PR_TRACE( hTrace, TraceFlow, 0xfff,0,0,0,0,0,0);
+ PR_DESTROY_TRACE( hTrace );
+#else
+ printf("Test not defined\n");
+#endif
+ return 0;
+} /* main() */
+/* end instrumt.c */
+
diff --git a/pr/tests/intrupt.c b/pr/tests/intrupt.c
new file mode 100644
index 00000000..d8d1de53
--- /dev/null
+++ b/pr/tests/intrupt.c
@@ -0,0 +1,283 @@
+/* -*- 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: intrupt.c
+ * Purpose: testing thread interrupts
+ */
+
+#include "plgetopt.h"
+#include "prcvar.h"
+#include "prerror.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prio.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prthread.h"
+#include "prtypes.h"
+#include "prnetdb.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+#endif
+
+static PRLock *ml = NULL;
+static PRCondVar *cv = NULL;
+
+static PRBool passed = PR_TRUE;
+static PRBool debug_mode = PR_FALSE;
+static PRThreadScope thread_scope = PR_LOCAL_THREAD;
+
+static void PR_CALLBACK AbortCV(void *arg)
+{
+ PRStatus rv;
+ PRThread *me = PR_CurrentThread();
+
+ /* some other thread (main) is doing the interrupt */
+ PR_Lock(ml);
+ rv = PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT);
+ if (debug_mode) printf( "Expected interrupt on wait CV and ");
+ if (PR_FAILURE == rv)
+ {
+ if (PR_PENDING_INTERRUPT_ERROR == PR_GetError())
+ {
+ if (debug_mode) printf("got it\n");
+ }
+ else
+ {
+ if (debug_mode) printf("got random error\n");
+ passed = PR_FALSE;
+ }
+ }
+ else
+ {
+ if (debug_mode) printf("got a successful completion\n");
+ passed = PR_FALSE;
+ }
+
+ rv = PR_WaitCondVar(cv, 10);
+ if (debug_mode)
+ {
+ printf(
+ "Expected success on wait CV and %s\n",
+ (PR_SUCCESS == rv) ? "got it" : "failed");
+ }
+ passed = ((PR_TRUE == passed) && (PR_SUCCESS == rv)) ? PR_TRUE : PR_FALSE;
+
+ /* interrupt myself, then clear */
+ PR_Interrupt(me);
+ PR_ClearInterrupt();
+ rv = PR_WaitCondVar(cv, 10);
+ if (debug_mode)
+ {
+ printf("Expected success on wait CV and ");
+ if (PR_FAILURE == rv)
+ {
+ printf(
+ "%s\n", (PR_PENDING_INTERRUPT_ERROR == PR_GetError()) ?
+ "got interrupted" : "a random failure");
+ }
+ printf("got it\n");
+ }
+ passed = ((PR_TRUE == passed) && (PR_SUCCESS == rv)) ? PR_TRUE : PR_FALSE;
+
+ /* set, then wait - interrupt - then wait again */
+ PR_Interrupt(me);
+ rv = PR_WaitCondVar(cv, 10);
+ if (debug_mode) printf( "Expected interrupt on wait CV and ");
+ if (PR_FAILURE == rv)
+ {
+ if (PR_PENDING_INTERRUPT_ERROR == PR_GetError())
+ {
+ if (debug_mode) printf("got it\n");
+ }
+ else
+ {
+ if (debug_mode) printf("failed\n");
+ passed = PR_FALSE;
+ }
+ }
+ else
+ {
+ if (debug_mode) printf("got a successful completion\n");
+ passed = PR_FALSE;
+ }
+
+ rv = PR_WaitCondVar(cv, 10);
+ if (debug_mode)
+ {
+ printf(
+ "Expected success on wait CV and %s\n",
+ (PR_SUCCESS == rv) ? "got it" : "failed");
+ }
+ passed = ((PR_TRUE == passed) && (PR_SUCCESS == rv)) ? PR_TRUE : PR_FALSE;
+
+ PR_Unlock(ml);
+
+} /* AbortCV */
+
+static void PR_CALLBACK AbortIO(void *arg)
+{
+ PRStatus rv;
+ PR_Sleep(PR_SecondsToInterval(2));
+ rv = PR_Interrupt((PRThread*)arg);
+ PR_ASSERT(PR_SUCCESS == rv);
+} /* AbortIO */
+
+static void PR_CALLBACK AbortJoin(void *arg)
+{
+} /* AbortJoin */
+
+void PR_CALLBACK Intrupt(void *arg)
+{
+ PRStatus rv;
+ PRNetAddr netaddr;
+ PRFileDesc *listner;
+ PRInt16 port = 12848;
+ PRThread *abortCV, *abortIO, *abortJoin;
+
+ ml = PR_NewLock();
+ cv = PR_NewCondVar(ml);
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("intrupt.log");
+ debug_mode = PR_TRUE;
+#endif
+
+ /* Part I */
+ if (debug_mode) printf("Part I\n");
+ abortCV = PR_CreateThread(
+ PR_USER_THREAD, AbortCV, 0, PR_PRIORITY_NORMAL,
+ thread_scope, PR_JOINABLE_THREAD, 0);
+
+ PR_Sleep(PR_SecondsToInterval(2));
+ rv = PR_Interrupt(abortCV);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(abortCV);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ /* Part II */
+ if (debug_mode) printf("Part II\n");
+ abortJoin = PR_CreateThread(
+ PR_USER_THREAD, AbortJoin, 0, PR_PRIORITY_NORMAL,
+ thread_scope, PR_JOINABLE_THREAD, 0);
+ PR_Sleep(PR_SecondsToInterval(2));
+ if (debug_mode) printf("Expecting to interrupt an exited thread ");
+ rv = PR_Interrupt(abortJoin);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(abortJoin);
+ PR_ASSERT(PR_SUCCESS == rv);
+ if (debug_mode) printf("and succeeded\n");
+
+ /* Part III */
+ if (debug_mode) printf("Part III\n");
+ listner = PR_NewTCPSocket();
+ memset(&netaddr, 0, sizeof(netaddr));
+ netaddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ netaddr.inet.family = PR_AF_INET;
+ do
+ {
+ netaddr.inet.port = PR_htons(port);
+ rv = PR_Bind(listner, &netaddr);
+ port += 1;
+ PR_ASSERT(port < (12848 + 10));
+ } while (PR_FAILURE == rv);
+
+ rv = PR_Listen(listner, 5);
+
+ if (PR_GetSockName(listner, &netaddr) < 0) {
+ if (debug_mode) printf("intrupt: ERROR - PR_GetSockName failed\n");
+ passed = PR_FALSE;
+ return;
+ }
+
+ abortIO = PR_CreateThread(
+ PR_USER_THREAD, AbortIO, PR_CurrentThread(), PR_PRIORITY_NORMAL,
+ thread_scope, PR_JOINABLE_THREAD, 0);
+
+ if (PR_Accept(listner, &netaddr, PR_INTERVAL_NO_TIMEOUT) == NULL)
+ {
+ PRInt32 error = PR_GetError();
+ if (debug_mode) printf("Expected interrupt on PR_Accept() and ");
+ if (PR_PENDING_INTERRUPT_ERROR == error)
+ {
+ if (debug_mode) printf("got it\n");
+ }
+ else
+ {
+ if (debug_mode) printf("failed\n");
+ passed = PR_FALSE;
+ }
+ }
+ else
+ {
+ if (debug_mode) printf("Failed to interrupt PR_Accept()\n");
+ passed = PR_FALSE;
+ }
+
+ (void)PR_Close(listner); listner = NULL;
+
+ rv = PR_JoinThread(abortIO);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ PR_DestroyCondVar(cv);
+ PR_DestroyLock(ml);
+} /* Intrupt */
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PRThread *intrupt;
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dG");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'G': /* use global threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ PR_STDIO_INIT();
+ intrupt = PR_CreateThread(
+ PR_USER_THREAD, Intrupt, NULL, PR_PRIORITY_NORMAL,
+ thread_scope, PR_JOINABLE_THREAD, 0);
+ if (intrupt == NULL) {
+ fprintf(stderr, "cannot create thread\n");
+ passed = PR_FALSE;
+ } else {
+ PRStatus rv;
+ rv = PR_JoinThread(intrupt);
+ PR_ASSERT(rv == PR_SUCCESS);
+ }
+ printf("%s\n", ((passed) ? "PASSED" : "FAILED"));
+ return ((passed) ? 0 : 1);
+} /* main */
+
+/* intrupt.c */
diff --git a/pr/tests/io_timeout.c b/pr/tests/io_timeout.c
new file mode 100644
index 00000000..2e1e46f2
--- /dev/null
+++ b/pr/tests/io_timeout.c
@@ -0,0 +1,217 @@
+/* -*- 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.
+ */
+
+/*
+** Test socket IO timeouts
+**
+**
+**
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+***********************************************************************/
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+#include "prttools.h"
+
+#include <stdio.h>
+#include "nspr.h"
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+#endif
+
+#define NUM_THREADS 1
+#define BASE_PORT 8000
+#define DEFAULT_ACCEPT_TIMEOUT 2
+
+typedef struct threadInfo {
+ PRInt16 id;
+ PRInt16 accept_timeout;
+ PRLock *dead_lock;
+ PRCondVar *dead_cv;
+ PRInt32 *alive;
+} threadInfo;
+
+void
+thread_main(void *_info)
+{
+ threadInfo *info = (threadInfo *)_info;
+ PRNetAddr listenAddr;
+ PRNetAddr clientAddr;
+ PRFileDesc *listenSock = NULL;
+ PRFileDesc *clientSock;
+ PRStatus rv;
+
+ printf("thread %d is alive\n", info->id);
+
+ listenSock = PR_NewTCPSocket();
+ if (!listenSock) {
+ printf("unable to create listen socket\n");
+ goto dead;
+ }
+
+ listenAddr.inet.family = PR_AF_INET;
+ listenAddr.inet.port = PR_htons(BASE_PORT + info->id);
+ listenAddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ rv = PR_Bind(listenSock, &listenAddr);
+ if (rv == PR_FAILURE) {
+ printf("unable to bind\n");
+ goto dead;
+ }
+
+ rv = PR_Listen(listenSock, 4);
+ if (rv == PR_FAILURE) {
+ printf("unable to listen\n");
+ goto dead;
+ }
+
+ printf("thread %d going into accept for %d seconds\n",
+ info->id, info->accept_timeout + info->id);
+
+ clientSock = PR_Accept(listenSock, &clientAddr, PR_SecondsToInterval(info->accept_timeout +info->id));
+
+ if (clientSock == NULL) {
+ if (PR_GetError() == PR_IO_TIMEOUT_ERROR)
+ printf("PR_Accept() timeout worked!\n");
+ else
+ printf("TEST FAILED! PR_Accept() returned error %d\n", PR_GetError());
+ } else {
+ printf ("TEST FAILED! PR_Accept() succeeded?\n");
+ PR_Close(clientSock);
+ }
+
+dead:
+ if (listenSock) {
+ PR_Close(listenSock);
+ }
+ PR_Lock(info->dead_lock);
+ (*info->alive)--;
+ PR_NotifyCondVar(info->dead_cv);
+ PR_Unlock(info->dead_lock);
+
+ printf("thread %d is dead\n", info->id);
+}
+
+void
+thread_test(PRThreadScope scope, PRInt32 num_threads)
+{
+ PRInt32 index;
+ PRThread *thr;
+ PRLock *dead_lock;
+ PRCondVar *dead_cv;
+ PRInt32 alive;
+
+ printf("IO Timeout test started with %d threads\n", num_threads);
+
+ dead_lock = PR_NewLock();
+ dead_cv = PR_NewCondVar(dead_lock);
+ alive = num_threads;
+
+ for (index = 0; index < num_threads; index++) {
+ threadInfo *info = (threadInfo *)PR_Malloc(sizeof(threadInfo));
+
+ info->id = index;
+ info->dead_lock = dead_lock;
+ info->dead_cv = dead_cv;
+ info->alive = &alive;
+ info->accept_timeout = DEFAULT_ACCEPT_TIMEOUT;
+
+ thr = PR_CreateThread( PR_USER_THREAD,
+ thread_main,
+ (void *)info,
+ PR_PRIORITY_NORMAL,
+ scope,
+ PR_UNJOINABLE_THREAD,
+ 0);
+
+ if (!thr) {
+ PR_Lock(dead_lock);
+ alive--;
+ PR_Unlock(dead_lock);
+ }
+ }
+
+ PR_Lock(dead_lock);
+ while(alive) {
+ printf("main loop awake; alive = %d\n", alive);
+ PR_WaitCondVar(dead_cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(dead_lock);
+}
+
+int main(int argc, char **argv)
+{
+ PRInt32 num_threads = 0;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name [-d] [-t <threads>]
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dt:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ case 't': /* threads to involve */
+ num_threads = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ if (0 == num_threads)
+ num_threads = NUM_THREADS;
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_LOW, 0);
+ PR_STDIO_INIT();
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("io_timeout.log");
+ debug_mode = 1;
+#endif
+
+ printf("user level test\n");
+ thread_test(PR_LOCAL_THREAD, num_threads);
+
+ printf("kernel level test\n");
+ thread_test(PR_GLOBAL_THREAD, num_threads);
+
+ PR_Cleanup();
+ return 0;
+}
diff --git a/pr/tests/io_timeoutk.c b/pr/tests/io_timeoutk.c
new file mode 100644
index 00000000..ec485f95
--- /dev/null
+++ b/pr/tests/io_timeoutk.c
@@ -0,0 +1,214 @@
+/* -*- 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.
+ */
+
+/*
+** name io_timeoutk.c
+** Description:Test socket IO timeouts (kernel level)
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include "nspr.h"
+
+#define NUM_THREADS 1
+#define BASE_PORT 8000
+#define DEFAULT_ACCEPT_TIMEOUT 2
+
+typedef struct threadInfo {
+ PRInt16 id;
+ PRInt16 accept_timeout;
+ PRLock *dead_lock;
+ PRCondVar *dead_cv;
+ PRInt32 *alive;
+} threadInfo;
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+
+void
+thread_main(void *_info)
+{
+ threadInfo *info = (threadInfo *)_info;
+ PRNetAddr listenAddr;
+ PRNetAddr clientAddr;
+ PRFileDesc *listenSock = NULL;
+ PRFileDesc *clientSock;
+ PRStatus rv;
+
+ if (debug_mode) printf("thread %d is alive\n", info->id);
+
+ listenSock = PR_NewTCPSocket();
+ if (!listenSock) {
+ if (debug_mode) printf("unable to create listen socket\n");
+ goto dead;
+ }
+
+ listenAddr.inet.family = AF_INET;
+ listenAddr.inet.port = PR_htons(BASE_PORT + info->id);
+ listenAddr.inet.ip = PR_htonl(INADDR_ANY);
+ rv = PR_Bind(listenSock, &listenAddr);
+ if (rv == PR_FAILURE) {
+ if (debug_mode) printf("unable to bind\n");
+ goto dead;
+ }
+
+ rv = PR_Listen(listenSock, 4);
+ if (rv == PR_FAILURE) {
+ if (debug_mode) printf("unable to listen\n");
+ goto dead;
+ }
+
+ if (debug_mode) printf("thread %d going into accept for %d seconds\n",
+ info->id, info->accept_timeout + info->id);
+
+ clientSock = PR_Accept(listenSock, &clientAddr, PR_SecondsToInterval(info->accept_timeout +info->id));
+
+ if (clientSock == NULL) {
+ if (PR_GetError() == PR_IO_TIMEOUT_ERROR)
+ if (debug_mode) {
+ printf("PR_Accept() timeout worked!\n");
+ printf("TEST FAILED! PR_Accept() returned error %d\n",
+ PR_GetError());
+ }
+ else failed_already=1;
+ } else {
+ if (debug_mode) printf ("TEST FAILED! PR_Accept() succeeded?\n");
+ else failed_already=1;
+ PR_Close(clientSock);
+ }
+
+dead:
+ if (listenSock) {
+ PR_Close(listenSock);
+ }
+ PR_Lock(info->dead_lock);
+ (*info->alive)--;
+ PR_NotifyCondVar(info->dead_cv);
+ PR_Unlock(info->dead_lock);
+
+ if (debug_mode) printf("thread %d is dead\n", info->id);
+}
+
+void
+thread_test(PRInt32 scope, PRInt32 num_threads)
+{
+ PRInt32 index;
+ PRThread *thr;
+ PRLock *dead_lock;
+ PRCondVar *dead_cv;
+ PRInt32 alive;
+
+ if (debug_mode) printf("IO Timeout test started with %d threads\n", num_threads);
+
+ dead_lock = PR_NewLock();
+ dead_cv = PR_NewCondVar(dead_lock);
+ alive = num_threads;
+
+ for (index = 0; index < num_threads; index++) {
+ threadInfo *info = (threadInfo *)malloc(sizeof(threadInfo));
+
+ info->id = index;
+ info->dead_lock = dead_lock;
+ info->dead_cv = dead_cv;
+ info->alive = &alive;
+ info->accept_timeout = DEFAULT_ACCEPT_TIMEOUT;
+
+ thr = PR_CreateThread( PR_USER_THREAD,
+ thread_main,
+ (void *)info,
+ PR_PRIORITY_NORMAL,
+ scope,
+ PR_UNJOINABLE_THREAD,
+ 0);
+
+ if (!thr) {
+ PR_Lock(dead_lock);
+ alive--;
+ PR_Unlock(dead_lock);
+ }
+ }
+
+ PR_Lock(dead_lock);
+ while(alive) {
+ if (debug_mode) printf("main loop awake; alive = %d\n", alive);
+ PR_WaitCondVar(dead_cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(dead_lock);
+}
+
+int main(int argc, char **argv)
+{
+ PRInt32 num_threads;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ if (argc > 2)
+ num_threads = atoi(argv[2]);
+ else
+ num_threads = NUM_THREADS;
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_LOW, 0);
+ PR_STDIO_INIT();
+
+ if (debug_mode) printf("kernel level test\n");
+ thread_test(PR_GLOBAL_THREAD, num_threads);
+
+ PR_Cleanup();
+
+ if(failed_already)
+ return 1;
+ else
+ return 0;
+
+}
diff --git a/pr/tests/io_timeoutu.c b/pr/tests/io_timeoutu.c
new file mode 100644
index 00000000..4a9cb8ba
--- /dev/null
+++ b/pr/tests/io_timeoutu.c
@@ -0,0 +1,215 @@
+/* -*- 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.
+ */
+
+
+/*
+** name io_timeoutu.c
+** Description: Test socket IO timeouts (user level)
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include "nspr.h"
+
+#define NUM_THREADS 1
+#define BASE_PORT 8000
+#define DEFAULT_ACCEPT_TIMEOUT 2
+
+typedef struct threadInfo {
+ PRInt16 id;
+ PRInt16 accept_timeout;
+ PRLock *dead_lock;
+ PRCondVar *dead_cv;
+ PRInt32 *alive;
+} threadInfo;
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+void
+thread_main(void *_info)
+{
+ threadInfo *info = (threadInfo *)_info;
+ PRNetAddr listenAddr;
+ PRNetAddr clientAddr;
+ PRFileDesc *listenSock = NULL;
+ PRFileDesc *clientSock;
+ PRStatus rv;
+
+ if (debug_mode) printf("thread %d is alive\n", info->id);
+
+ listenSock = PR_NewTCPSocket();
+ if (!listenSock) {
+ if (debug_mode) printf("unable to create listen socket\n");
+ goto dead;
+ }
+
+ listenAddr.inet.family = AF_INET;
+ listenAddr.inet.port = PR_htons(BASE_PORT + info->id);
+ listenAddr.inet.ip = PR_htonl(INADDR_ANY);
+ rv = PR_Bind(listenSock, &listenAddr);
+ if (rv == PR_FAILURE) {
+ if (debug_mode) printf("unable to bind\n");
+ goto dead;
+ }
+
+ rv = PR_Listen(listenSock, 4);
+ if (rv == PR_FAILURE) {
+ if (debug_mode) printf("unable to listen\n");
+ goto dead;
+ }
+
+ if (debug_mode) printf("thread %d going into accept for %d seconds\n",
+ info->id, info->accept_timeout + info->id);
+
+ clientSock = PR_Accept(
+ listenSock, &clientAddr, PR_SecondsToInterval(
+ info->accept_timeout + info->id));
+
+ if (clientSock == NULL) {
+ if (PR_GetError() == PR_IO_TIMEOUT_ERROR)
+ if (debug_mode) {
+ printf("PR_Accept() timeout worked!\n");
+ printf("TEST FAILED! PR_Accept() returned error %d\n",
+ }
+ PR_GetError());
+ else failed_already=1;
+ } else {
+ if (debug_mode) printf ("TEST FAILED! PR_Accept() succeeded?\n");
+ else failed_already=1;
+ PR_Close(clientSock);
+ }
+
+dead:
+ if (listenSock) {
+ PR_Close(listenSock);
+ }
+ PR_Lock(info->dead_lock);
+ (*info->alive)--;
+ PR_NotifyCondVar(info->dead_cv);
+ PR_Unlock(info->dead_lock);
+
+ if (debug_mode) printf("thread %d is dead\n", info->id);
+}
+
+void
+thread_test(PRInt32 scope, PRInt32 num_threads)
+{
+ PRInt32 index;
+ PRThread *thr;
+ PRLock *dead_lock;
+ PRCondVar *dead_cv;
+ PRInt32 alive;
+
+ if (debug_mode) printf("IO Timeout test started with %d threads\n", num_threads);
+
+ dead_lock = PR_NewLock();
+ dead_cv = PR_NewCondVar(dead_lock);
+ alive = num_threads;
+
+ for (index = 0; index < num_threads; index++) {
+ threadInfo *info = (threadInfo *)malloc(sizeof(threadInfo));
+
+ info->id = index;
+ info->dead_lock = dead_lock;
+ info->dead_cv = dead_cv;
+ info->alive = &alive;
+ info->accept_timeout = DEFAULT_ACCEPT_TIMEOUT;
+
+ thr = PR_CreateThread( PR_USER_THREAD,
+ thread_main,
+ (void *)info,
+ PR_PRIORITY_NORMAL,
+ scope,
+ PR_UNJOINABLE_THREAD,
+ 0);
+
+ if (!thr) {
+ PR_Lock(dead_lock);
+ alive--;
+ PR_Unlock(dead_lock);
+ }
+ }
+
+ PR_Lock(dead_lock);
+ while(alive) {
+ if (debug_mode) printf("main loop awake; alive = %d\n", alive);
+ PR_WaitCondVar(dead_cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(dead_lock);
+}
+
+int main(int argc, char **argv)
+{
+ PRInt32 num_threads;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ if (argc > 2)
+ num_threads = atoi(argv[2]);
+ else
+ num_threads = NUM_THREADS;
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_LOW, 0);
+ PR_STDIO_INIT();
+
+ if (debug_mode) printf("user level test\n");
+ thread_test(PR_LOCAL_THREAD, num_threads);
+
+ PR_Cleanup();
+ if(failed_already)
+ return 1;
+ else
+ return 0;
+
+
+}
diff --git a/pr/tests/ioconthr.c b/pr/tests/ioconthr.c
new file mode 100644
index 00000000..60d6949b
--- /dev/null
+++ b/pr/tests/ioconthr.c
@@ -0,0 +1,123 @@
+/* -*- 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 is a test for the io continuation thread machinery
+ * in pthreads.
+ */
+
+#include "nspr.h"
+#include <stdio.h>
+
+int num_threads = 10; /* must be an even number */
+PRThreadScope thread_scope = PR_GLOBAL_THREAD;
+
+void ThreadFunc(void *arg)
+{
+ PRFileDesc *fd = (PRFileDesc *) arg;
+ char buf[1024];
+ PRInt32 nbytes;
+ PRErrorCode err;
+
+ nbytes = PR_Recv(fd, buf, sizeof(buf), 0, PR_SecondsToInterval(20));
+ if (nbytes == -1) {
+ err = PR_GetError();
+ if (err != PR_PENDING_INTERRUPT_ERROR) {
+ fprintf(stderr, "PR_Recv failed: (%d, %d)\n",
+ err, PR_GetOSError());
+ PR_ProcessExit(1);
+ }
+ if (PR_Close(fd) == PR_FAILURE) {
+ fprintf(stderr, "PR_Close failed\n");
+ PR_ProcessExit(1);
+ }
+ } else {
+ fprintf(stderr, "PR_Recv received %d bytes!?\n", nbytes);
+ PR_ProcessExit(1);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ PRFileDesc **fds;
+ PRThread **threads;
+ PRIntervalTime start, elapsed;
+ int index;
+
+ fds = (PRFileDesc **) PR_MALLOC(num_threads * sizeof(PRFileDesc *));
+ PR_ASSERT(fds != NULL);
+ threads = (PRThread **) PR_MALLOC(num_threads * sizeof(PRThread *));
+ PR_ASSERT(threads != NULL);
+
+ for (index = 0; index < (num_threads / 2); index++) {
+ if (PR_NewTCPSocketPair(&fds[2 * index]) == PR_FAILURE) {
+ fprintf(stderr, "PR_NewTCPSocket failed\n");
+ PR_ProcessExit(1);
+ }
+
+ threads[2 * index] = PR_CreateThread(
+ PR_USER_THREAD, ThreadFunc, fds[2 * index],
+ PR_PRIORITY_NORMAL, thread_scope, PR_JOINABLE_THREAD, 0);
+ if (NULL == threads[2 * index]) {
+ fprintf(stderr, "PR_CreateThread failed\n");
+ PR_ProcessExit(1);
+ }
+ threads[2 * index + 1] = PR_CreateThread(
+ PR_USER_THREAD, ThreadFunc, fds[2 * index + 1],
+ PR_PRIORITY_NORMAL, thread_scope, PR_JOINABLE_THREAD, 0);
+ if (NULL == threads[2 * index + 1]) {
+ fprintf(stderr, "PR_CreateThread failed\n");
+ PR_ProcessExit(1);
+ }
+ }
+
+ /* Let the threads block in PR_Recv */
+ PR_Sleep(PR_SecondsToInterval(2));
+
+ printf("Interrupting the threads\n");
+ fflush(stdout);
+ start = PR_IntervalNow();
+ for (index = 0; index < num_threads; index++) {
+ if (PR_Interrupt(threads[index]) == PR_FAILURE) {
+ fprintf(stderr, "PR_Interrupt failed\n");
+ PR_ProcessExit(1);
+ }
+ }
+ for (index = 0; index < num_threads; index++) {
+ if (PR_JoinThread(threads[index]) == PR_FAILURE) {
+ fprintf(stderr, "PR_JoinThread failed\n");
+ PR_ProcessExit(1);
+ }
+ }
+ elapsed = (PRIntervalTime)(PR_IntervalNow() - start);
+ printf("Threads terminated in %d milliseconds\n",
+ PR_IntervalToMilliseconds(elapsed));
+ fflush(stdout);
+
+ /* We are being very generous and allow 10 seconds. */
+ if (elapsed >= PR_SecondsToInterval(10)) {
+ fprintf(stderr, "Interrupting threads took longer than 10 seconds!!\n");
+ PR_ProcessExit(1);
+ }
+
+ PR_DELETE(threads);
+ PR_DELETE(fds);
+ printf("PASS\n");
+ PR_Cleanup();
+ return 0;
+}
diff --git a/pr/tests/ipv6.c b/pr/tests/ipv6.c
new file mode 100644
index 00000000..15303430
--- /dev/null
+++ b/pr/tests/ipv6.c
@@ -0,0 +1,241 @@
+/* -*- 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 "prio.h"
+#include "prenv.h"
+#include "prmem.h"
+#include "prlink.h"
+#include "prsystem.h"
+#include "prnetdb.h"
+#include "prprf.h"
+#include "prvrsion.h"
+
+#include "plerror.h"
+#include "plgetopt.h"
+#include "obsolete/probslet.h"
+
+#include <string.h>
+
+#define DNS_BUFFER 100
+#define ADDR_BUFFER 100
+#define HOST_BUFFER 1024
+#define PROTO_BUFFER 1500
+
+static PRFileDesc *err = NULL;
+
+static void Help(void)
+{
+ PR_fprintf(err, "Usage: [-V] [-6] [-h]\n");
+ PR_fprintf(err, "\t<nul> Name of host to lookup (default: self)\n");
+ PR_fprintf(err, "\t-V Display runtime version info (default: FALSE)\n");
+ PR_fprintf(err, "\t-6 First turn on IPv6 capability (default: FALSE)\n");
+ PR_fprintf(err, "\t-h This message and nothing else\n");
+} /* Help */
+
+static void DumpAddr(const PRNetAddr* address, const char *msg)
+{
+ PRUint32 *word = (PRUint32*)address;
+ PRUint32 addr_len = sizeof(PRNetAddr);
+ PR_fprintf(err, "%s[%d]\t", msg, PR_NETADDR_SIZE(address));
+ while (addr_len > 0)
+ {
+ PR_fprintf(err, " %08x", *word++);
+ addr_len -= sizeof(PRUint32);
+ }
+ PR_fprintf(err, "\n");
+} /* DumpAddr */
+
+static PRStatus PrintAddress(const PRNetAddr* address)
+{
+ PRNetAddr translation;
+ char buffer[ADDR_BUFFER];
+ PRStatus rv = PR_NetAddrToString(address, buffer, sizeof(buffer));
+ if (PR_FAILURE == rv) PL_FPrintError(err, "PR_NetAddrToString");
+ else
+ {
+ PR_fprintf(err, "\t%s\n", buffer);
+ memset(&translation, 0, sizeof(translation));
+ rv = PR_StringToNetAddr(buffer, &translation);
+ if (PR_FAILURE == rv) PL_FPrintError(err, "PR_StringToNetAddr");
+ else
+ {
+ PRSize addr_len = PR_NETADDR_SIZE(address);
+ if (0 != memcmp(address, &translation, addr_len))
+ {
+ PR_fprintf(err, "Address translations do not match\n");
+ DumpAddr(address, "original");
+ DumpAddr(&translation, "translate");
+ rv = PR_FAILURE;
+ }
+ }
+ }
+ return rv;
+} /* PrintAddress */
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PRStatus rv;
+ PLOptStatus os;
+ PRHostEnt host;
+ PRProtoEnt proto;
+ PRBool ipv6 = PR_FALSE;
+ const char *name = NULL;
+ PRBool failed = PR_FALSE, version = PR_FALSE;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "Vh6");
+
+ err = PR_GetSpecialFD(PR_StandardError);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 0: /* Name of host to lookup */
+ name = opt->value;
+ break;
+ case '6': /* Turn on IPv6 mode */
+ ipv6 = PR_TRUE;
+ break;
+ case 'V': /* Do version discovery */
+ version = PR_TRUE;
+ break;
+ case 'h': /* user wants some guidance */
+ default:
+ Help(); /* so give him an earful */
+ return 2; /* but not a lot else */
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (version)
+ {
+#if defined(XP_UNIX)
+#define NSPR_LIB "nspr21"
+#elif defined(WIN32)
+#define NSPR_LIB "libnspr21"
+#else
+#error "Architecture not supported"
+#endif
+ const PRVersionDescription *version_info;
+ char *nspr_path = PR_GetEnv("LD_LIBRARY_PATH");
+ char *nspr_name = PR_GetLibraryName(nspr_path, NSPR_LIB);
+ PRLibrary *runtime = PR_LoadLibrary(nspr_name);
+ if (NULL == runtime)
+ PL_FPrintError(err, "PR_LoadLibrary");
+ else
+ {
+ versionEntryPointType versionPoint = (versionEntryPointType)
+ PR_FindSymbol(runtime, "libVersionPoint");
+ if (NULL == versionPoint)
+ PL_FPrintError(err, "PR_FindSymbol");
+ else
+ {
+ char buffer[100];
+ PRExplodedTime exploded;
+ version_info = versionPoint();
+ (void)PR_fprintf(err, "Runtime library version information\n");
+ PR_ExplodeTime(
+ version_info->buildTime, PR_GMTParameters, &exploded);
+ (void)PR_FormatTime(
+ buffer, sizeof(buffer), "%d %b %Y %H:%M:%S", &exploded);
+ (void)PR_fprintf(err, " Build time: %s GMT\n", buffer);
+ (void)PR_fprintf(
+ err, " Build time: %s\n", version_info->buildTimeString);
+ (void)PR_fprintf(
+ err, " %s V%u.%u.%u (%s%s%s)\n",
+ version_info->description,
+ version_info->vMajor,
+ version_info->vMinor,
+ version_info->vPatch,
+ (version_info->beta ? " beta " : ""),
+ (version_info->debug ? " debug " : ""),
+ (version_info->special ? " special" : ""));
+ (void)PR_fprintf(err, " filename: %s\n", version_info->filename);
+ (void)PR_fprintf(err, " security: %s\n", version_info->security);
+ (void)PR_fprintf(err, " copyright: %s\n", version_info->copyright);
+ (void)PR_fprintf(err, " comment: %s\n", version_info->comment);
+ }
+ }
+ if (NULL != nspr_name) PR_FreeLibraryName(nspr_name);
+ }
+
+ if (ipv6)
+ {
+ rv = PR_SetIPv6Enable(ipv6);
+ if (PR_FAILURE == rv)
+ {
+ failed = PR_TRUE;
+ PL_FPrintError(err, "PR_SetIPv6Enable");
+ }
+ }
+
+ {
+ if (NULL == name)
+ {
+ char *me = (char*)PR_MALLOC(DNS_BUFFER);
+ rv = PR_GetSystemInfo(PR_SI_HOSTNAME, me, DNS_BUFFER);
+ if (PR_FAILURE == rv)
+ {
+ failed = PR_TRUE;
+ PL_FPrintError(err, "PR_GetHostName");
+ return 2;
+ }
+ name = me; /* just leak the storage */
+ }
+ }
+
+ {
+ char buffer[HOST_BUFFER];
+ PR_fprintf(err, "Translating the name %s ...", name);
+
+ rv = PR_GetHostByName(name, buffer, sizeof(buffer), &host);
+ if (PR_FAILURE == rv)
+ {
+ failed = PR_TRUE;
+ PL_FPrintError(err, "PR_GetHostByName");
+ }
+ else
+ {
+ PRIntn index = 0;
+ PRNetAddr address;
+ PR_fprintf(err, "success .. enumerating results\n");
+ do
+ {
+ index = PR_EnumerateHostEnt(index, &host, 0, &address);
+ if (index > 0) PrintAddress(&address);
+ else if (-1 == index)
+ {
+ failed = PR_TRUE;
+ PL_FPrintError(err, "PR_EnumerateHostEnt");
+ }
+ } while (index > 0);
+ }
+ }
+
+
+ {
+ char buffer[PROTO_BUFFER];
+ /*
+ ** Get Proto by name/number
+ */
+ rv = PR_GetProtoByName("tcp", &buffer[1], sizeof(buffer) - 1, &proto);
+ rv = PR_GetProtoByNumber(6, &buffer[3], sizeof(buffer) - 3, &proto);
+ }
+
+ return (failed) ? 1 : 0;
+}
diff --git a/pr/tests/join.c b/pr/tests/join.c
new file mode 100644
index 00000000..108b11f0
--- /dev/null
+++ b/pr/tests/join.c
@@ -0,0 +1,199 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+**
+** Name: dbmalloc1.c
+**
+** Description: Tests PR_SetMallocCountdown PR_ClearMallocCountdown functions.
+**
+** Modification History:
+**
+** 19-May-97 AGarcia - separate the four join tests into different unit test modules.
+** AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+#include "prttools.h"
+
+#include "nspr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+#endif
+/***********************************************************************
+** PRIVATE FUNCTION: Test_Result
+** DESCRIPTION: Used in conjunction with the regress tool, prints out the
+** status of the test case.
+** INPUTS: PASS/FAIL
+** OUTPUTS: None
+** RETURN: None
+** SIDE EFFECTS:
+**
+** RESTRICTIONS:
+** None
+** MEMORY: NA
+** ALGORITHM: Determine what the status is and print accordingly.
+**
+***********************************************************************/
+
+
+static void Test_Result (int result)
+{
+ if (result == PASS)
+ printf ("PASS\n");
+ else
+ printf ("FAIL\n");
+ exit (1);
+}
+
+
+/*
+ Program to test joining of threads. Two threads are created. One
+ to be waited upon until it has started. The other to join after it has
+ completed.
+*/
+
+
+static void PR_CALLBACK lowPriority(void *arg)
+{
+}
+
+static void PR_CALLBACK highPriority(void *arg)
+{
+}
+
+void runTest(PRThreadScope scope1, PRThreadScope scope2)
+{
+ PRThread *low,*high;
+
+ /* create the low and high priority threads */
+
+ low = PR_CreateThread(PR_USER_THREAD,
+ lowPriority, 0,
+ PR_PRIORITY_LOW,
+ scope1,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!low) {
+ if (debug_mode) printf("\tcannot create low priority thread\n");
+ else Test_Result(FAIL);
+ return;
+ }
+
+ high = PR_CreateThread(PR_USER_THREAD,
+ highPriority, 0,
+ PR_PRIORITY_HIGH,
+ scope2,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!high) {
+ if (debug_mode) printf("\tcannot create high priority thread\n");
+ else Test_Result(FAIL);
+ return;
+ }
+
+ /* Do the joining for both threads */
+ if (PR_JoinThread(low) == PR_FAILURE) {
+ if (debug_mode) printf("\tcannot join low priority thread\n");
+ else Test_Result (FAIL);
+ return;
+ } else {
+ if (debug_mode) printf("\tjoined low priority thread\n");
+ }
+ if (PR_JoinThread(high) == PR_FAILURE) {
+ if (debug_mode) printf("\tcannot join high priority thread\n");
+ else Test_Result(FAIL);
+ return;
+ } else {
+ if (debug_mode) printf("\tjoined high priority thread\n");
+ }
+}
+
+static PRIntn PR_CALLBACK RealMain(int argc, char **argv)
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("join.log");
+ debug_mode = 1;
+#endif
+
+
+
+ /* main test */
+ printf("User-User test\n");
+ runTest(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
+ printf("User-Kernel test\n");
+ runTest(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
+ printf("Kernel-User test\n");
+ runTest(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
+ printf("Kernel-Kernel test\n");
+ runTest(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
+
+ printf("PASSED\n");
+
+ return 0;
+}
+
+
+
+
+PRIntn main(PRIntn argc, char *argv[])
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
diff --git a/pr/tests/joinkk.c b/pr/tests/joinkk.c
new file mode 100644
index 00000000..5c2e4e9c
--- /dev/null
+++ b/pr/tests/joinkk.c
@@ -0,0 +1,174 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+**
+** Name: dbmalloc1.c
+**
+** Description: Tests PR_SetMallocCountdown PR_ClearMallocCountdown functions.
+**
+** Modification History:
+**
+** 19-May-97 AGarcia - separate the four join tests into different unit test modules.
+** AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+#endif
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+/*
+ Program to test joining of threads. Two threads are created. One
+ to be waited upon until it has started. The other to join after it has
+ completed.
+*/
+
+
+static void lowPriority(void *arg)
+{
+}
+
+static void highPriority(void *arg)
+{
+}
+
+void runTest(PRThreadScope scope1, PRThreadScope scope2)
+{
+ PRThread *low,*high;
+
+ /* create the low and high priority threads */
+
+ low = PR_CreateThread(PR_USER_THREAD,
+ lowPriority, 0,
+ PR_PRIORITY_LOW,
+ scope1,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!low) {
+ if (debug_mode) printf("\tcannot create low priority thread\n");
+ else failed_already=1;
+ return;
+ }
+
+ high = PR_CreateThread(PR_USER_THREAD,
+ highPriority, 0,
+ PR_PRIORITY_HIGH,
+ scope2,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!high) {
+ if (debug_mode) printf("\tcannot create high priority thread\n");
+ else failed_already=1;
+ return;
+ }
+
+ /* Do the joining for both threads */
+ if (PR_JoinThread(low) == PR_FAILURE) {
+ if (debug_mode) printf("\tcannot join low priority thread\n");
+ else failed_already=1;
+ return;
+ } else {
+ if (debug_mode) printf("\tjoined low priority thread\n");
+ }
+ if (PR_JoinThread(high) == PR_FAILURE) {
+ if (debug_mode) printf("\tcannot join high priority thread\n");
+ else failed_already=1;
+ return;
+ } else {
+ if (debug_mode) printf("\tjoined high priority thread\n");
+ }
+}
+
+static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv )
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("join.log");
+#endif
+
+
+
+ /* main test */
+
+ if (debug_mode) printf("Kernel-Kernel test\n");
+ runTest(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
+
+ if(failed_already)
+ {
+ printf("FAIL\n");
+ return 1;
+ }
+ else
+ {
+ printf("PASS\n");
+ return 0;
+ }
+
+}
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
diff --git a/pr/tests/joinku.c b/pr/tests/joinku.c
new file mode 100644
index 00000000..417f7e12
--- /dev/null
+++ b/pr/tests/joinku.c
@@ -0,0 +1,180 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+**
+** Name: dbmalloc1.c
+**
+** Description: Tests PR_SetMallocCountdown PR_ClearMallocCountdown functions.
+**
+** Modification History:
+**
+** 19-May-97 AGarcia - separate the four join tests into different unit test modules.
+** AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+#endif
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+
+/*
+ Program to test joining of threads. Two threads are created. One
+ to be waited upon until it has started. The other to join after it has
+ completed.
+*/
+
+
+static void lowPriority(void *arg)
+{
+}
+
+static void highPriority(void *arg)
+{
+}
+
+void runTest(PRThreadScope scope1, PRThreadScope scope2)
+{
+ PRThread *low,*high;
+
+ /* create the low and high priority threads */
+
+ low = PR_CreateThread(PR_USER_THREAD,
+ lowPriority, 0,
+ PR_PRIORITY_LOW,
+ scope1,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!low) {
+ if (debug_mode) printf("\tcannot create low priority thread\n");
+ else failed_already=1;
+ return;
+ }
+
+ high = PR_CreateThread(PR_USER_THREAD,
+ highPriority, 0,
+ PR_PRIORITY_HIGH,
+ scope2,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!high) {
+ if (debug_mode) printf("\tcannot create high priority thread\n");
+ else failed_already=1;
+ return;
+ }
+
+ /* Do the joining for both threads */
+ if (PR_JoinThread(low) == PR_FAILURE) {
+ if (debug_mode) printf("\tcannot join low priority thread\n");
+ else failed_already=1;
+ return;
+ } else {
+ if (debug_mode) printf("\tjoined low priority thread\n");
+ }
+ if (PR_JoinThread(high) == PR_FAILURE) {
+ if (debug_mode) printf("\tcannot join high priority thread\n");
+ else failed_already=1;
+ return;
+ } else {
+ if (debug_mode) printf("\tjoined high priority thread\n");
+ }
+}
+
+static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv )
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("joinku.log");
+#endif
+
+
+
+ /* main test */
+
+ if (debug_mode) printf("Kernel-User test\n");
+ runTest(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
+
+
+ if(failed_already)
+ {
+ printf("FAIL\n");
+ return 1;
+ }
+ else
+ {
+ printf("PASS\n");
+ return 0;
+ }
+
+}
+
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
diff --git a/pr/tests/joinuk.c b/pr/tests/joinuk.c
new file mode 100644
index 00000000..a2a552af
--- /dev/null
+++ b/pr/tests/joinuk.c
@@ -0,0 +1,176 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+**
+** Name: joinuk.c
+**
+** Description: Join kernel - user
+**
+** Modification History:
+**
+** 19-May-97 AGarcia - separate the four join tests into different unit test modules.
+** AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+#endif
+PRIntn failed_already=0;
+PRIntn debug_mode;
+/*
+ Program to test joining of threads. Two threads are created. One
+ to be waited upon until it has started. The other to join after it has
+ completed.
+*/
+
+
+static void lowPriority(void *arg)
+{
+}
+
+static void highPriority(void *arg)
+{
+}
+
+void runTest(PRThreadScope scope1, PRThreadScope scope2)
+{
+ PRThread *low,*high;
+
+ /* create the low and high priority threads */
+
+ low = PR_CreateThread(PR_USER_THREAD,
+ lowPriority, 0,
+ PR_PRIORITY_LOW,
+ scope1,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!low) {
+ if (debug_mode) printf("\tcannot create low priority thread\n");
+ else failed_already=1;
+ return;
+ }
+
+ high = PR_CreateThread(PR_USER_THREAD,
+ highPriority, 0,
+ PR_PRIORITY_HIGH,
+ scope2,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!high) {
+ if (debug_mode) printf("\tcannot create high priority thread\n");
+ else failed_already=1;
+ return;
+ }
+
+ /* Do the joining for both threads */
+ if (PR_JoinThread(low) == PR_FAILURE) {
+ if (debug_mode) printf("\tcannot join low priority thread\n");
+ else failed_already=1;
+ return;
+ } else {
+ if (debug_mode) printf("\tjoined low priority thread\n");
+ }
+ if (PR_JoinThread(high) == PR_FAILURE) {
+ if (debug_mode) printf("\tcannot join high priority thread\n");
+ else failed_already=1;
+ return;
+ } else {
+ if (debug_mode) printf("\tjoined high priority thread\n");
+ }
+}
+
+static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv )
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("joinuk.log");
+#endif
+
+
+
+ /* main test */
+
+ if (debug_mode) printf("User-Kernel test\n");
+ runTest(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
+
+
+ if(failed_already)
+ {
+ printf("FAIL\n");
+ return 1;
+ } else
+ {
+ printf("PASS\n");
+ return 0;
+ }
+}
+
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
diff --git a/pr/tests/joinuu.c b/pr/tests/joinuu.c
new file mode 100644
index 00000000..206b96ac
--- /dev/null
+++ b/pr/tests/joinuu.c
@@ -0,0 +1,178 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+**
+** Name: dbmalloc1.c
+**
+** Description: Join tests user - user
+**
+** Modification History:
+**
+** 19-May-97 AGarcia - separate the four join tests into different unit test modules.
+** AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+#endif
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+
+/*
+ Program to test joining of threads. Two threads are created. One
+ to be waited upon until it has started. The other to join after it has
+ completed.
+*/
+
+
+static void lowPriority(void *arg)
+{
+}
+
+static void highPriority(void *arg)
+{
+}
+
+void runTest(PRThreadScope scope1, PRThreadScope scope2)
+{
+ PRThread *low,*high;
+
+ /* create the low and high priority threads */
+
+ low = PR_CreateThread(PR_USER_THREAD,
+ lowPriority, 0,
+ PR_PRIORITY_LOW,
+ scope1,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!low) {
+ if (debug_mode) printf("\tcannot create low priority thread\n");
+ else failed_already=1;
+ return;
+ }
+
+ high = PR_CreateThread(PR_USER_THREAD,
+ highPriority, 0,
+ PR_PRIORITY_HIGH,
+ scope2,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (!high) {
+ if (debug_mode) printf("\tcannot create high priority thread\n");
+ else failed_already=1;
+ return;
+ }
+
+ /* Do the joining for both threads */
+ if (PR_JoinThread(low) == PR_FAILURE) {
+ if (debug_mode) printf("\tcannot join low priority thread\n");
+ else failed_already=1;
+ return;
+ } else {
+ if (debug_mode) printf("\tjoined low priority thread\n");
+ }
+ if (PR_JoinThread(high) == PR_FAILURE) {
+ if (debug_mode) printf("\tcannot join high priority thread\n");
+ else failed_already=1;
+ return;
+ } else {
+ if (debug_mode) printf("\tjoined high priority thread\n");
+ }
+}
+
+static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv )
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("joinuu.log");
+#endif
+
+
+
+ /* main test */
+ if (debug_mode) printf("User-User test\n");
+ runTest(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
+
+ if(failed_already)
+ {
+ printf("FAIL\n");
+ return 1;
+ } else
+ {
+ printf("PASS\n");
+ return 0;
+ }
+
+
+}
+
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
diff --git a/pr/tests/layer.c b/pr/tests/layer.c
new file mode 100644
index 00000000..4e61523a
--- /dev/null
+++ b/pr/tests/layer.c
@@ -0,0 +1,371 @@
+/* -*- 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 "prio.h"
+#include "prprf.h"
+#include "prlog.h"
+#include "prnetdb.h"
+#include "prthread.h"
+
+#include "plerror.h"
+#include "plgetopt.h"
+#include "prwin16.h"
+
+#include <stdlib.h>
+
+/*
+** Testing layering of I/O
+**
+** The layered server
+** A thread that acts as a server. It creates a TCP listener with a dummy
+** layer pushed on top. Then listens for incoming connections. Each connection
+** request for connection will be layered as well, accept one request, echo
+** it back and close.
+**
+** The layered client
+** Pretty much what you'd expect.
+*/
+
+static PRFileDesc *logFile;
+static PRDescIdentity identity;
+static PRNetAddr server_address;
+
+static PRIOMethods myMethods;
+
+typedef enum Verbosity {silent, quiet, chatty, noisy} Verbosity;
+
+static PRIntn minor_iterations = 5;
+static PRIntn major_iterations = 1;
+static Verbosity verbosity = quiet;
+static PRUint16 default_port = 12273;
+
+static PRFileDesc *PushLayer(PRFileDesc *stack)
+{
+ PRFileDesc *layer = PR_CreateIOLayerStub(identity, &myMethods);
+ PRStatus rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer);
+ if (verbosity > quiet)
+ PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer, stack);
+ PR_ASSERT(PR_SUCCESS == rv);
+ return stack;
+} /* PushLayer */
+
+#if 0
+static PRFileDesc *PopLayer(PRFileDesc *stack)
+{
+ PRFileDesc *popped = PR_PopIOLayer(stack, identity);
+ if (verbosity > quiet)
+ PR_fprintf(logFile, "Popped layer(0x%x) from stack(0x%x)\n", popped, stack);
+ popped->dtor(popped);
+
+ return stack;
+} /* PopLayer */
+#endif
+
+static void PR_CALLBACK Client(void *arg)
+{
+ PRStatus rv;
+ PRUint8 buffer[100];
+ PRIntn empty_flags = 0;
+ PRIntn bytes_read, bytes_sent;
+ PRFileDesc *stack = (PRFileDesc*)arg;
+
+ /* Initialize the buffer so that Purify won't complain */
+ memset(buffer, 0, sizeof(buffer));
+
+ rv = PR_Connect(stack, &server_address, PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(PR_SUCCESS == rv);
+ while (minor_iterations-- > 0)
+ {
+ bytes_sent = PR_Send(
+ stack, buffer, sizeof(buffer), empty_flags, PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(sizeof(buffer) == bytes_sent);
+ if (verbosity > chatty)
+ PR_fprintf(logFile, "Client sending %d bytes\n", bytes_sent);
+ bytes_read = PR_Recv(
+ stack, buffer, bytes_sent, empty_flags, PR_INTERVAL_NO_TIMEOUT);
+ if (verbosity > chatty)
+ PR_fprintf(logFile, "Client receiving %d bytes\n", bytes_read);
+ PR_ASSERT(bytes_read == bytes_sent);
+ }
+
+ if (verbosity > quiet)
+ PR_fprintf(logFile, "Client shutting down stack\n");
+
+ rv = PR_Shutdown(stack, PR_SHUTDOWN_BOTH); PR_ASSERT(PR_SUCCESS == rv);
+} /* Client */
+
+static void PR_CALLBACK Server(void *arg)
+{
+ PRStatus rv;
+ PRUint8 buffer[100];
+ PRFileDesc *service;
+ PRUintn empty_flags = 0;
+ PRIntn bytes_read, bytes_sent;
+ PRFileDesc *stack = (PRFileDesc*)arg;
+ PRNetAddr any_address, client_address;
+
+ rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ rv = PR_Bind(stack, &any_address); PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Listen(stack, 10); PR_ASSERT(PR_SUCCESS == rv);
+
+ service = PR_Accept(stack, &client_address, PR_INTERVAL_NO_TIMEOUT);
+ if (verbosity > quiet)
+ PR_fprintf(logFile, "Server accepting connection\n");
+
+ do
+ {
+ bytes_read = PR_Recv(
+ service, buffer, sizeof(buffer), empty_flags, PR_INTERVAL_NO_TIMEOUT);
+ if (0 != bytes_read)
+ {
+ if (verbosity > chatty)
+ PR_fprintf(logFile, "Server receiving %d bytes\n", bytes_read);
+ PR_ASSERT(bytes_read > 0);
+ bytes_sent = PR_Send(
+ service, buffer, bytes_read, empty_flags, PR_INTERVAL_NO_TIMEOUT);
+ if (verbosity > chatty)
+ PR_fprintf(logFile, "Server sending %d bytes\n", bytes_sent);
+ PR_ASSERT(bytes_read == bytes_sent);
+ }
+
+ } while (0 != bytes_read);
+
+ if (verbosity > quiet)
+ PR_fprintf(logFile, "Server shutting down and closing stack\n");
+ rv = PR_Shutdown(service, PR_SHUTDOWN_BOTH); PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv);
+
+} /* Server */
+
+static PRInt32 PR_CALLBACK MyRecv(
+ PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ char *b = (char*)buf;
+ PRFileDesc *lo = fd->lower;
+ PRInt32 rv, readin = 0, request = 0;
+ rv = lo->methods->recv(lo, &request, sizeof(request), flags, timeout);
+ if (verbosity > chatty) PR_fprintf(
+ logFile, "MyRecv sending permission for %d bytes\n", request);
+ if (0 < rv)
+ {
+ if (verbosity > chatty) PR_fprintf(
+ logFile, "MyRecv received permission request for %d bytes\n", request);
+ rv = lo->methods->send(
+ lo, &request, sizeof(request), flags, timeout);
+ if (0 < rv)
+ {
+ if (verbosity > chatty) PR_fprintf(
+ logFile, "MyRecv sending permission for %d bytes\n", request);
+ while (readin < request)
+ {
+ rv = lo->methods->recv(
+ lo, b + readin, amount - readin, flags, timeout);
+ if (rv <= 0) break;
+ if (verbosity > chatty) PR_fprintf(
+ logFile, "MyRecv received %d bytes\n", rv);
+ readin += rv;
+ }
+ rv = readin;
+ }
+ }
+ return rv;
+} /* MyRecv */
+
+static PRInt32 PR_CALLBACK MySend(
+ PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ PRFileDesc *lo = fd->lower;
+ const char *b = (const char*)buf;
+ PRInt32 rv, wroteout = 0, request;
+ if (verbosity > chatty) PR_fprintf(
+ logFile, "MySend asking permission to send %d bytes\n", amount);
+ rv = lo->methods->send(lo, &amount, sizeof(amount), flags, timeout);
+ if (0 < rv)
+ {
+ rv = lo->methods->recv(
+ lo, &request, sizeof(request), flags, timeout);
+ if (0 < rv)
+ {
+ PR_ASSERT(request == amount);
+ if (verbosity > chatty) PR_fprintf(
+ logFile, "MySend got permission to send %d bytes\n", request);
+ while (wroteout < request)
+ {
+ rv = lo->methods->send(
+ lo, b + wroteout, request - wroteout, flags, timeout);
+ if (rv <= 0) break;
+ if (verbosity > chatty) PR_fprintf(
+ logFile, "MySend wrote %d bytes\n", rv);
+ wroteout += rv;
+ }
+ rv = amount;
+ }
+ }
+ return rv;
+} /* MySend */
+
+static Verbosity ChangeVerbosity(Verbosity verbosity, PRIntn delta)
+{
+ PRIntn verbage = (PRIntn)verbosity + delta;
+ if (verbage < (PRIntn)silent) verbage = (PRIntn)silent;
+ else if (verbage > (PRIntn)noisy) verbage = (PRIntn)noisy;
+ return (Verbosity)verbage;
+} /* ChangeVerbosity */
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PRStatus rv;
+ PRIntn mits;
+ PLOptStatus os;
+ PRFileDesc *client, *service;
+ const char *server_name = NULL;
+ const PRIOMethods *stubMethods;
+ PRThread *client_thread, *server_thread;
+ PRThreadScope thread_scope = PR_LOCAL_THREAD;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dqGC:c:p:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 0:
+ server_name = opt->value;
+ break;
+ case 'd': /* debug mode */
+ if (verbosity < noisy)
+ verbosity = ChangeVerbosity(verbosity, 1);
+ break;
+ case 'q': /* debug mode */
+ if (verbosity > silent)
+ verbosity = ChangeVerbosity(verbosity, -1);
+ break;
+ case 'G': /* use global threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'C': /* number of threads waiting */
+ major_iterations = atoi(opt->value);
+ break;
+ case 'c': /* number of client threads */
+ minor_iterations = atoi(opt->value);
+ break;
+ case 'p': /* default port */
+ default_port = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ PR_STDIO_INIT();
+
+ logFile = PR_GetSpecialFD(PR_StandardError);
+
+ identity = PR_GetUniqueIdentity("Dummy");
+ stubMethods = PR_GetDefaultIOMethods();
+
+ /*
+ ** The protocol we're going to implement is one where in order to initiate
+ ** a send, the sender must first solicit permission. Therefore, every
+ ** send is really a send - receive - send sequence.
+ */
+ myMethods = *stubMethods; /* first get the entire batch */
+ myMethods.recv = MyRecv; /* then override the ones we care about */
+ myMethods.send = MySend; /* then override the ones we care about */
+
+ if (NULL == server_name)
+ rv = PR_InitializeNetAddr(
+ PR_IpAddrLoopback, default_port, &server_address);
+ else
+ {
+ rv = PR_StringToNetAddr(server_name, &server_address);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_InitializeNetAddr(
+ PR_IpAddrNull, default_port, &server_address);
+ }
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ /* one type w/o layering */
+
+ mits = minor_iterations;
+ while (major_iterations-- > 0)
+ {
+ if (verbosity > silent)
+ PR_fprintf(logFile, "Beginning non-layered test\n");
+ client = PR_NewTCPSocket(); PR_ASSERT(NULL != client);
+ service = PR_NewTCPSocket(); PR_ASSERT(NULL != service);
+
+ minor_iterations = mits;
+ server_thread = PR_CreateThread(
+ PR_USER_THREAD, Server, service,
+ PR_PRIORITY_HIGH, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ PR_ASSERT(NULL != server_thread);
+
+ client_thread = PR_CreateThread(
+ PR_USER_THREAD, Client, client,
+ PR_PRIORITY_NORMAL, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ PR_ASSERT(NULL != client_thread);
+
+ rv = PR_JoinThread(client_thread);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(server_thread);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ rv = PR_Close(client); PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv);
+ if (verbosity > silent)
+ PR_fprintf(logFile, "Ending non-layered test\n");
+
+ /* with layering */
+ if (verbosity > silent)
+ PR_fprintf(logFile, "Beginning layered test\n");
+ client = PR_NewTCPSocket(); PR_ASSERT(NULL != client);
+ service = PR_NewTCPSocket(); PR_ASSERT(NULL != service);
+
+ minor_iterations = mits;
+ server_thread = PR_CreateThread(
+ PR_USER_THREAD, Server, PushLayer(service),
+ PR_PRIORITY_HIGH, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ PR_ASSERT(NULL != server_thread);
+
+ client_thread = PR_CreateThread(
+ PR_USER_THREAD, Client, PushLayer(client),
+ PR_PRIORITY_NORMAL, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ PR_ASSERT(NULL != client_thread);
+
+ rv = PR_JoinThread(client_thread);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(server_thread);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ rv = PR_Close(client); PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv);
+ if (verbosity > silent)
+ PR_fprintf(logFile, "Ending layered test\n");
+ }
+ return 0;
+} /* main */
+
+/* layer.c */
diff --git a/pr/tests/lazyinit.c b/pr/tests/lazyinit.c
new file mode 100644
index 00000000..c5475379
--- /dev/null
+++ b/pr/tests/lazyinit.c
@@ -0,0 +1,120 @@
+/* -*- 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: lazyinit.c
+** Description: Testing lazy initialization
+**
+** Since you only get to initialize once, you have to rerun the test
+** for each test case. The test cases are numbered. If you want to
+** add more tests, take the next number and add it to the switch
+** statement.
+**
+** This test is problematic on systems that don't support the notion
+** of console output. The workarounds to emulate that feature include
+** initializations themselves, which defeats the purpose here.
+*/
+
+#include "prcvar.h"
+#include "prenv.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prio.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prthread.h"
+#include "prtypes.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static void PR_CALLBACK lazyEntry(void *arg)
+{
+ PR_ASSERT(NULL == arg);
+} /* lazyEntry */
+
+
+PRIntn main(PRIntn argc, char *argv[])
+{
+ PRUintn pdkey;
+ PRStatus status;
+ char *path = NULL;
+ PRDir *dir = NULL;
+ PRLock *ml = NULL;
+ PRCondVar *cv = NULL;
+ PRThread *thread = NULL;
+ PRIntervalTime interval = 0;
+ PRFileDesc *file, *udp, *tcp, *pair[2];
+ PRIntn test;
+
+ if ( argc < 2)
+ {
+ test = 0;
+ }
+ else
+ test = atoi(argv[1]);
+
+ switch (test)
+ {
+ case 0: ml = PR_NewLock();
+ break;
+
+ case 1: interval = PR_SecondsToInterval(1);
+ break;
+
+ case 2: thread = PR_CreateThread(
+ PR_USER_THREAD, lazyEntry, NULL, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ break;
+
+ case 3: file = PR_Open("/usr/tmp/", PR_RDONLY, 0);
+ break;
+
+ case 4: udp = PR_NewUDPSocket();
+ break;
+
+ case 5: tcp = PR_NewTCPSocket();
+ break;
+
+ case 6: dir = PR_OpenDir("/usr/tmp/");
+ break;
+
+ case 7: (void)PR_NewThreadPrivateIndex(&pdkey, NULL);
+ break;
+
+ case 8: path = PR_GetEnv("PATH");
+ break;
+
+ case 9: status = PR_NewTCPSocketPair(pair);
+ break;
+
+ case 10: PR_SetConcurrency(2);
+ break;
+
+ default:
+ printf(
+ "lazyinit: unrecognized command line argument: %s\n",
+ argv[1] );
+ printf( "FAIL\n" );
+ exit( 1 );
+ break;
+ } /* switch() */
+ return 0;
+} /* Lazy */
+
+/* lazyinit.c */
diff --git a/pr/tests/lltest.c b/pr/tests/lltest.c
new file mode 100644
index 00000000..7f3dbb4d
--- /dev/null
+++ b/pr/tests/lltest.c
@@ -0,0 +1,732 @@
+/* -*- 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.
+ */
+
+/*
+** testll.c -- test suite for 64bit integer (longlong) operations
+**
+** Summary: testll [-d] | [-h]
+**
+** Where:
+** -d set debug mode on; displays individual test failures
+** -v verbose mode; displays progress in test, plus -d
+** -h gives usage message.
+**
+** Description:
+** lltest.c tests the functions defined in NSPR 2.0's prlong.h.
+**
+** Successive tests begin to depend on other LL functions working
+** correctly. So, ... Do not change the order of the tests as run
+** from main().
+**
+** Caveats:
+** Do not even begin to think that this is an exhaustive test!
+**
+** These tests try a little of everything, but not all boundary
+** conditions and limits are tested.
+** You want better coverage? ... Add it.
+**
+** ---
+** Author: Lawrence Hardiman <larryh@netscape.com>.
+** ---
+** Revision History:
+** 01-Oct-1997. Original implementation.
+**
+*/
+
+#include "nspr.h"
+#include "plgetopt.h"
+
+/* --- Local Definitions --- */
+#define ReportProgress(m) if (verboseMode) PR_fprintf(output, (m));
+
+
+/* --- Global variables --- */
+static PRIntn failedAlready = 0;
+static PRFileDesc* output = NULL;
+static PRBool debugMode = PR_FALSE;
+static PRBool verboseMode = PR_FALSE;
+
+/*
+** Constants used in tests.
+*/
+const PRInt64 bigZero = LL_INIT( 0, 0 );
+const PRInt64 bigOne = LL_INIT( 0, 1 );
+const PRInt64 bigTwo = LL_INIT( 0, 2 );
+const PRInt64 bigSixTeen = LL_INIT( 0, 16 );
+const PRInt64 bigThirtyTwo = LL_INIT( 0, 32 );
+const PRInt64 bigMinusOne = LL_INIT( 0xffffffff, 0xffffffff );
+const PRInt64 bigMinusTwo = LL_INIT( 0xffffffff, 0xfffffffe );
+const PRInt64 bigNumber = LL_INIT( 0x7fffffff, 0xffffffff );
+const PRInt64 bigMinusNumber = LL_INIT( 0x80000000, 0x00000001 );
+const PRInt64 bigMaxInt32 = LL_INIT( 0x00000000, 0x7fffffff );
+const PRInt64 big2To31 = LL_INIT( 0x00000000, 0x80000000 );
+const PRUint64 bigZeroFox = LL_INIT( 0x00000000, 0xffffffff );
+const PRUint64 bigFoxFox = LL_INIT( 0xffffffff, 0xffffffff );
+const PRUint64 bigFoxZero = LL_INIT( 0xffffffff, 0x00000000 );
+const PRUint64 bigEightZero = LL_INIT( 0x80000000, 0x00000000 );
+const PRUint64 big64K = LL_INIT( 0x00000000, 0x00010000 );
+const PRInt32 one = 1l;
+const PRInt32 minusOne = -1l;
+const PRInt32 sixteen = 16l;
+const PRInt32 thirtyTwo = 32l;
+const PRInt32 sixtyThree = 63l;
+
+/*
+** SetFailed() -- Report individual test failure
+**
+*/
+static void
+SetFailed( char *what, char *how )
+{
+ failedAlready = 1;
+ if ( debugMode )
+ PR_fprintf(output, "%s: failed: %s\n", what, how );
+ return;
+}
+
+static void
+ResultFailed( char *what, char *how, PRInt64 expected, PRInt64 got)
+{
+ if ( debugMode)
+ {
+ SetFailed( what, how );
+ PR_fprintf(output, "Expected: 0x%llx Got: 0x%llx\n", expected, got );
+ }
+ return;
+}
+
+
+/*
+** TestAssignment() -- Test the assignment
+*/
+static void TestAssignment( void )
+{
+ PRInt64 zero = LL_Zero();
+ PRInt64 min = LL_MinInt();
+ PRInt64 max = LL_MaxInt();
+ if (!LL_EQ(zero, bigZero))
+ SetFailed("LL_EQ(zero, bigZero)", "!=");
+ if (!LL_CMP(max, >, min))
+ SetFailed("LL_CMP(max, >, min)", "!>");
+}
+
+/*
+** TestComparisons() -- Test the longlong comparison operations
+*/
+static void
+TestComparisons( void )
+{
+ ReportProgress("Testing Comparisons Operations\n");
+
+ /* test for zero */
+ if ( !LL_IS_ZERO( bigZero ))
+ SetFailed( "LL_IS_ZERO", "Zero is not zero" );
+
+ if ( LL_IS_ZERO( bigOne ))
+ SetFailed( "LL_IS_ZERO", "One tests as zero" );
+
+ if ( LL_IS_ZERO( bigMinusOne ))
+ SetFailed( "LL_IS_ZERO", "Minus One tests as zero" );
+
+ /* test equal */
+ if ( !LL_EQ( bigZero, bigZero ))
+ SetFailed( "LL_EQ", "zero EQ zero");
+
+ if ( !LL_EQ( bigOne, bigOne ))
+ SetFailed( "LL_EQ", "one EQ one" );
+
+ if ( !LL_EQ( bigNumber, bigNumber ))
+ SetFailed( "LL_EQ", "bigNumber EQ bigNumber" );
+
+ if ( !LL_EQ( bigMinusOne, bigMinusOne ))
+ SetFailed( "LL_EQ", "minus one EQ minus one");
+
+ if ( LL_EQ( bigZero, bigOne ))
+ SetFailed( "LL_EQ", "zero EQ one");
+
+ if ( LL_EQ( bigOne, bigZero ))
+ SetFailed( "LL_EQ", "one EQ zero" );
+
+ if ( LL_EQ( bigMinusOne, bigOne ))
+ SetFailed( "LL_EQ", "minus one EQ one");
+
+ if ( LL_EQ( bigNumber, bigOne ))
+ SetFailed( "LL_EQ", "bigNumber EQ one");
+
+ /* test not equal */
+ if ( LL_NE( bigZero, bigZero ))
+ SetFailed( "LL_NE", "0 NE 0");
+
+ if ( LL_NE( bigOne, bigOne ))
+ SetFailed( "LL_NE", "1 NE 1");
+
+ if ( LL_NE( bigMinusOne, bigMinusOne ))
+ SetFailed( "LL_NE", "-1 NE -1");
+
+ if ( LL_NE( bigNumber, bigNumber ))
+ SetFailed( "LL_NE", "n NE n");
+
+ if ( LL_NE( bigMinusNumber, bigMinusNumber ))
+ SetFailed( "LL_NE", "-n NE -n");
+
+ if ( !LL_NE( bigZero, bigOne))
+ SetFailed( "LL_NE", "0 NE 1");
+
+ if ( !LL_NE( bigOne, bigMinusNumber))
+ SetFailed( "LL_NE", "1 NE -n");
+
+ /* Greater than or equal to zero */
+ if ( !LL_GE_ZERO( bigZero ))
+ SetFailed( "LL_GE_ZERO", "0");
+
+ if ( !LL_GE_ZERO( bigOne ))
+ SetFailed( "LL_GE_ZERO", "1");
+
+ if ( !LL_GE_ZERO( bigNumber ))
+ SetFailed( "LL_GE_ZERO", "n");
+
+ if ( LL_GE_ZERO( bigMinusOne ))
+ SetFailed( "LL_GE_ZERO", "-1");
+
+ if ( LL_GE_ZERO( bigMinusNumber ))
+ SetFailed( "LL_GE_ZERO", "-n");
+
+ /* Algebraic Compare two values */
+ if ( !LL_CMP( bigZero, ==, bigZero ))
+ SetFailed( "LL_CMP", "0 == 0");
+
+ if ( LL_CMP( bigZero, >, bigZero ))
+ SetFailed( "LL_CMP", "0 > 0");
+
+ if ( LL_CMP( bigZero, <, bigZero ))
+ SetFailed( "LL_CMP", "0 < 0");
+
+ if ( LL_CMP( bigNumber, <, bigOne ))
+ SetFailed( "LL_CMP", "n < 1");
+
+ if ( !LL_CMP( bigNumber, >, bigOne ))
+ SetFailed( "LL_CMP", "n <= 1");
+
+ if ( LL_CMP( bigOne, >, bigNumber ))
+ SetFailed( "LL_CMP", "1 > n");
+
+ if ( LL_CMP( bigMinusNumber, >, bigNumber ))
+ SetFailed( "LL_CMP", "-n > n");
+
+ if ( LL_CMP( bigNumber, !=, bigNumber))
+ SetFailed( "LL_CMP", "n != n");
+
+ if ( !LL_CMP( bigMinusOne, >, bigMinusTwo ))
+ SetFailed( "LL_CMP", "-1 <= -2");
+
+ if ( !LL_CMP( bigMaxInt32, <, big2To31 ))
+ SetFailed( "LL_CMP", "Max 32-bit signed int >= 2^31");
+
+ /* Bitwise Compare two numbers */
+ if ( !LL_UCMP( bigZero, ==, bigZero ))
+ SetFailed( "LL_UCMP", "0 == 0");
+
+ if ( LL_UCMP( bigZero, >, bigZero ))
+ SetFailed( "LL_UCMP", "0 > 0");
+
+ if ( LL_UCMP( bigZero, <, bigZero ))
+ SetFailed( "LL_UCMP", "0 < 0");
+
+ if ( LL_UCMP( bigNumber, <, bigOne ))
+ SetFailed( "LL_UCMP", "n < 1");
+
+ if ( !LL_UCMP( bigNumber, >, bigOne ))
+ SetFailed( "LL_UCMP", "n < 1");
+
+ if ( LL_UCMP( bigOne, >, bigNumber ))
+ SetFailed( "LL_UCMP", "1 > n");
+
+ if ( LL_UCMP( bigMinusNumber, <, bigNumber ))
+ SetFailed( "LL_UCMP", "-n < n");
+
+ return;
+}
+
+/*
+** TestLogicalOperations() -- Tests for AND, OR, ...
+**
+*/
+static void
+TestLogicalOperations( void )
+{
+ PRUint64 result, result2;
+
+ ReportProgress("Testing Logical Operations\n");
+
+ /* Test AND */
+ LL_AND( result, bigZero, bigZero );
+ if ( !LL_IS_ZERO( result ))
+ ResultFailed( "LL_AND", "0 & 0", bigZero, result );
+
+ LL_AND( result, bigOne, bigOne );
+ if ( LL_IS_ZERO( result ))
+ ResultFailed( "LL_AND", "1 & 1", bigOne, result );
+
+ LL_AND( result, bigZero, bigOne );
+ if ( !LL_IS_ZERO( result ))
+ ResultFailed( "LL_AND", "1 & 1", bigZero, result );
+
+ LL_AND( result, bigMinusOne, bigMinusOne );
+ if ( !LL_UCMP( result, ==, bigMinusOne ))
+ ResultFailed( "LL_AND", "-1 & -1", bigMinusOne, result );
+
+ /* test OR */
+ LL_OR( result, bigZero, bigZero );
+ if ( !LL_IS_ZERO( result ))
+ ResultFailed( "LL_OR", "0 | 1", bigZero, result);
+
+ LL_OR( result, bigZero, bigOne );
+ if ( LL_IS_ZERO( result ))
+ ResultFailed( "LL_OR", "0 | 1", bigOne, result );
+
+ LL_OR( result, bigZero, bigMinusNumber );
+ if ( !LL_UCMP( result, ==, bigMinusNumber ))
+ ResultFailed( "LL_OR", "0 | -n", bigMinusNumber, result);
+
+ LL_OR( result, bigMinusNumber, bigZero );
+ if ( !LL_UCMP( result, ==, bigMinusNumber ))
+ ResultFailed( "LL_OR", "-n | 0", bigMinusNumber, result );
+
+ /* test XOR */
+ LL_XOR( result, bigZero, bigZero );
+ if ( LL_UCMP( result, !=, bigZero ))
+ ResultFailed( "LL_XOR", "0 ^ 0", bigZero, result);
+
+ LL_XOR( result, bigOne, bigZero );
+ if ( LL_UCMP( result, !=, bigOne ))
+ ResultFailed( "LL_XOR", "1 ^ 0", bigZero, result );
+
+ LL_XOR( result, bigMinusNumber, bigZero );
+ if ( LL_UCMP( result, !=, bigMinusNumber ))
+ ResultFailed( "LL_XOR", "-n ^ 0", bigMinusNumber, result );
+
+ LL_XOR( result, bigMinusNumber, bigMinusNumber );
+ if ( LL_UCMP( result, !=, bigZero ))
+ ResultFailed( "LL_XOR", "-n ^ -n", bigMinusNumber, result);
+
+ /* test OR2. */
+ result = bigZero;
+ LL_OR2( result, bigOne );
+ if ( LL_UCMP( result, !=, bigOne ))
+ ResultFailed( "LL_OR2", "(r=0) |= 1", bigOne, result);
+
+ result = bigOne;
+ LL_OR2( result, bigNumber );
+ if ( LL_UCMP( result, !=, bigNumber ))
+ ResultFailed( "LL_OR2", "(r=1) |= n", bigNumber, result);
+
+ result = bigMinusNumber;
+ LL_OR2( result, bigMinusNumber );
+ if ( LL_UCMP( result, !=, bigMinusNumber ))
+ ResultFailed( "LL_OR2", "(r=-n) |= -n", bigMinusNumber, result);
+
+ /* test NOT */
+ LL_NOT( result, bigMinusNumber);
+ LL_NOT( result2, result);
+ if ( LL_UCMP( result2, !=, bigMinusNumber ))
+ ResultFailed( "LL_NOT", "r != ~(~-n)", bigMinusNumber, result);
+
+ /* test Negation */
+ LL_NEG( result, bigMinusNumber );
+ LL_NEG( result2, result );
+ if ( LL_CMP( result2, !=, bigMinusNumber ))
+ ResultFailed( "LL_NEG", "r != -(-(-n))", bigMinusNumber, result);
+
+ return;
+}
+
+
+
+/*
+** TestConversion() -- Test Conversion Operations
+**
+*/
+static void
+TestConversion( void )
+{
+ PRInt64 result;
+ PRInt64 resultU;
+ PRInt32 result32;
+ PRUint32 resultU32;
+ float resultF;
+ PRFloat64 resultD;
+
+ ReportProgress("Testing Conversion Operations\n");
+
+ /* LL_L2I -- Convert to signed 32bit */
+ LL_L2I(result32, bigOne );
+ if ( result32 != one )
+ SetFailed( "LL_L2I", "r != 1");
+
+ LL_L2I(result32, bigMinusOne );
+ if ( result32 != minusOne )
+ SetFailed( "LL_L2I", "r != -1");
+
+ /* LL_L2UI -- Convert 64bit to unsigned 32bit */
+ LL_L2UI( resultU32, bigMinusOne );
+ if ( resultU32 != (PRUint32) minusOne )
+ SetFailed( "LL_L2UI", "r != -1");
+
+ LL_L2UI( resultU32, bigOne );
+ if ( resultU32 != (PRUint32) one )
+ SetFailed( "LL_L2UI", "r != 1");
+
+ /* LL_L2F -- Convert to 32bit floating point */
+ LL_L2F( resultF, bigOne );
+ if ( resultF != 1.0 )
+ SetFailed( "LL_L2F", "r != 1.0");
+
+ LL_L2F( resultF, bigMinusOne );
+ if ( resultF != -1.0 )
+ SetFailed( "LL_L2F", "r != 1.0");
+
+ /* LL_L2D -- Convert to 64bit floating point */
+ LL_L2D( resultD, bigOne );
+ if ( resultD != 1.0L )
+ SetFailed( "LL_L2D", "r != 1.0");
+
+ LL_L2D( resultD, bigMinusOne );
+ if ( resultD != -1.0L )
+ SetFailed( "LL_L2D", "r != -1.0");
+
+ /* LL_I2L -- Convert 32bit signed to 64bit signed */
+ LL_I2L( result, one );
+ if ( LL_CMP(result, !=, bigOne ))
+ SetFailed( "LL_I2L", "r != 1");
+
+ LL_I2L( result, minusOne );
+ if ( LL_CMP(result, !=, bigMinusOne ))
+ SetFailed( "LL_I2L", "r != -1");
+
+ /* LL_UI2L -- Convert 32bit unsigned to 64bit unsigned */
+ LL_UI2L( resultU, (PRUint32) one );
+ if ( LL_CMP(resultU, !=, bigOne ))
+ SetFailed( "LL_UI2L", "r != 1");
+
+ /* [lth.] This did not behave as expected, but it is correct
+ */
+ LL_UI2L( resultU, (PRUint32) minusOne );
+ if ( LL_CMP(resultU, !=, bigZeroFox ))
+ ResultFailed( "LL_UI2L", "r != -1", bigZeroFox, resultU);
+
+ /* LL_F2L -- Convert 32bit float to 64bit signed */
+ LL_F2L( result, 1.0 );
+ if ( LL_CMP(result, !=, bigOne ))
+ SetFailed( "LL_F2L", "r != 1");
+
+ LL_F2L( result, -1.0 );
+ if ( LL_CMP(result, !=, bigMinusOne ))
+ SetFailed( "LL_F2L", "r != -1");
+
+ /* LL_D2L -- Convert 64bit Float to 64bit signed */
+ LL_D2L( result, 1.0L );
+ if ( LL_CMP(result, !=, bigOne ))
+ SetFailed( "LL_D2L", "r != 1");
+
+ LL_D2L( result, -1.0L );
+ if ( LL_CMP(result, !=, bigMinusOne ))
+ SetFailed( "LL_D2L", "r != -1");
+
+ return;
+}
+
+static void ShiftCompileOnly()
+{
+ /*
+ ** This function is only compiled, never called.
+ ** The real test is to see if it compiles w/o
+ ** warnings. This is no small feat, by the way.
+ */
+ PRInt64 ia, ib;
+ PRUint64 ua, ub;
+ LL_SHR(ia, ib, 32);
+ LL_SHL(ia, ib, 32);
+
+ LL_USHR(ua, ub, 32);
+ LL_ISHL(ia, 49, 32);
+
+} /* ShiftCompileOnly */
+
+
+/*
+** TestShift() -- Test Shifting Operations
+**
+*/
+static void
+TestShift( void )
+{
+ static const PRInt64 largeTwoZero = LL_INIT( 0x00000002, 0x00000000 );
+ PRInt64 result;
+ PRUint64 resultU;
+
+ ReportProgress("Testing Shifting Operations\n");
+
+ /* LL_SHL -- Shift left algebraic */
+ LL_SHL( result, bigOne, one );
+ if ( LL_CMP( result, !=, bigTwo ))
+ ResultFailed( "LL_SHL", "r != 2", bigOne, result );
+
+ LL_SHL( result, bigTwo, thirtyTwo );
+ if ( LL_CMP( result, !=, largeTwoZero ))
+ ResultFailed( "LL_SHL", "r != twoZero", largeTwoZero, result);
+
+ /* LL_SHR -- Shift right algebraic */
+ LL_SHR( result, bigFoxZero, thirtyTwo );
+ if ( LL_CMP( result, !=, bigMinusOne ))
+ ResultFailed( "LL_SHR", "r != -1", bigMinusOne, result);
+
+ LL_SHR( result, bigTwo, one );
+ if ( LL_CMP( result, !=, bigOne ))
+ ResultFailed( "LL_SHR", "r != 1", bigOne, result);
+
+ LL_SHR( result, bigFoxFox, thirtyTwo );
+ if ( LL_CMP( result, !=, bigMinusOne ))
+ ResultFailed( "LL_SHR", "r != -1 (was ff,ff)", bigMinusOne, result);
+
+ /* LL_USHR -- Logical shift right */
+ LL_USHR( resultU, bigZeroFox, thirtyTwo );
+ if ( LL_UCMP( resultU, !=, bigZero ))
+ ResultFailed( "LL_USHR", "r != 0 ", bigZero, result);
+
+ LL_USHR( resultU, bigFoxFox, thirtyTwo );
+ if ( LL_UCMP( resultU, !=, bigZeroFox ))
+ ResultFailed( "LL_USHR", "r != 0 ", bigZeroFox, result);
+
+ /* LL_ISHL -- Shift a 32bit integer into a 64bit result */
+ LL_ISHL( resultU, minusOne, thirtyTwo );
+ if ( LL_UCMP( resultU, !=, bigFoxZero ))
+ ResultFailed( "LL_ISHL", "r != ff,00 ", bigFoxZero, result);
+
+ LL_ISHL( resultU, one, sixtyThree );
+ if ( LL_UCMP( resultU, !=, bigEightZero ))
+ ResultFailed( "LL_ISHL", "r != 80,00 ", bigEightZero, result);
+
+ LL_ISHL( resultU, one, sixteen );
+ if ( LL_UCMP( resultU, !=, big64K ))
+ ResultFailed( "LL_ISHL", "r != 64K ", big64K, resultU);
+
+ return;
+}
+
+
+/*
+** TestArithmetic() -- Test arithmetic operations.
+**
+*/
+static void
+TestArithmetic( void )
+{
+ PRInt64 largeVal = LL_INIT( 0x00000001, 0xffffffff );
+ PRInt64 largeValPlusOne = LL_INIT( 0x00000002, 0x00000000 );
+ PRInt64 largeValTimesTwo = LL_INIT( 0x00000003, 0xfffffffe );
+ PRInt64 largeMultCand = LL_INIT( 0x00000000, 0x7fffffff );
+ PRInt64 largeMinusMultCand = LL_INIT( 0xffffffff, 0x10000001 );
+ PRInt64 largeMultCandx64K = LL_INIT( 0x00007fff, 0xffff0000 );
+ PRInt64 largeNumSHL5 = LL_INIT( 0x0000001f, 0xffffffe0 );
+ PRInt64 result, result2;
+
+ /* Addition */
+ LL_ADD( result, bigOne, bigOne );
+ if ( LL_CMP( result, !=, bigTwo ))
+ ResultFailed( "LL_ADD", "r != 1 + 1", bigTwo, result);
+
+ LL_ADD( result, bigMinusOne, bigOne );
+ if ( LL_CMP( result, !=, bigZero ))
+ ResultFailed( "LL_ADD", "r != -1 + 1", bigOne, result);
+
+ LL_ADD( result, largeVal, bigOne );
+ if ( LL_CMP( result, !=, largeValPlusOne ))
+ ResultFailed( "LL_ADD", "lVP1 != lV + 1", largeValPlusOne, result);
+
+ /* Subtraction */
+ LL_SUB( result, bigOne, bigOne );
+ if ( LL_CMP( result, !=, bigZero ))
+ ResultFailed( "LL_SUB", "r != 1 - 1", bigZero, result);
+
+ LL_SUB( result, bigTwo, bigOne );
+ if ( LL_CMP( result, !=, bigOne ))
+ ResultFailed( "LL_SUB", "r != 2 - 1", bigOne, result);
+
+ LL_SUB( result, largeValPlusOne, bigOne );
+ if ( LL_CMP( result, !=, largeVal ))
+ ResultFailed( "LL_SUB", "r != lVP1 - 1", largeVal, result);
+
+
+ /* Multiply */
+ LL_MUL( result, largeVal, bigTwo );
+ if ( LL_CMP( result, !=, largeValTimesTwo ))
+ ResultFailed( "LL_MUL", "r != lV*2", largeValTimesTwo, result);
+
+ LL_MUL( result, largeMultCand, big64K );
+ if ( LL_CMP( result, !=, largeMultCandx64K ))
+ ResultFailed( "LL_MUL", "r != lV*64K", largeMultCandx64K, result);
+
+ LL_NEG( result2, largeMultCand );
+ LL_MUL( result, largeMultCand, bigMinusOne );
+ if ( LL_CMP( result, !=, result2 ))
+ ResultFailed( "LL_MUL", "r != -lMC", result2, result);
+
+ LL_SHL( result2, bigZeroFox, 5);
+ LL_MUL( result, bigZeroFox, bigThirtyTwo );
+ if ( LL_CMP( result, !=, largeNumSHL5 ))
+ ResultFailed( "LL_MUL", "r != 0f<<5", largeNumSHL5, result );
+
+
+
+ /* LL_DIV() Division */
+ LL_DIV( result, bigOne, bigOne);
+ if ( LL_CMP( result, !=, bigOne ))
+ ResultFailed( "LL_DIV", "1 != 1", bigOne, result);
+
+ LL_DIV( result, bigNumber, bigOne );
+ if ( LL_CMP( result, !=, bigNumber ))
+ ResultFailed( "LL_DIV", "r != n / 1", bigNumber, result);
+
+ LL_DIV( result, bigNumber, bigMinusOne );
+ if ( LL_CMP( result, !=, bigMinusNumber ))
+ ResultFailed( "LL_DIV", "r != n / -1", bigMinusNumber, result);
+
+ LL_DIV( result, bigMinusNumber, bigMinusOne );
+ if ( LL_CMP( result, !=, bigNumber ))
+ ResultFailed( "LL_DIV", "r != -n / -1", bigNumber, result);
+
+ LL_SHL( result2, bigZeroFox, 5 );
+ LL_DIV( result, result2, bigOne );
+ if ( LL_CMP( result, !=, result2 ))
+ ResultFailed( "LL_DIV", "0f<<5 != 0f<<5", result2, result);
+
+ LL_SHL( result2, bigZeroFox, 5 );
+ LL_NEG( result2, result2 );
+ LL_DIV( result, result2, bigOne );
+ if ( LL_CMP( result, !=, result2 ))
+ ResultFailed( "LL_DIV", "-0f<<5 != -0f<<5", result2, result);
+
+ LL_SHL( result2, bigZeroFox, 17 );
+ LL_DIV( result, result2, bigMinusOne );
+ LL_NEG( result2, result2 );
+ if ( LL_CMP( result, !=, result2 ))
+ ResultFailed( "LL_DIV", "-0f<<17 != -0f<<17", result2, result);
+
+
+ /* LL_MOD() Modulo Division */
+ LL_ADD( result2, bigThirtyTwo, bigOne );
+ LL_MOD( result, result2, bigSixTeen );
+ if ( LL_CMP( result, !=, bigOne ))
+ ResultFailed( "LL_MOD", "r != 1", bigSixTeen, result);
+
+
+ LL_MUL( result2, bigZeroFox, bigThirtyTwo );
+ LL_ADD( result2, result2, bigSixTeen);
+ LL_MOD( result, result2, bigThirtyTwo );
+ if ( LL_CMP( result, !=, bigSixTeen ))
+ ResultFailed( "LL_MOD", "r != 16", bigSixTeen, result);
+
+ /* LL_UDIVMOD */
+ LL_DIV( result, bigOne, bigOne);
+ if ( LL_CMP( result, !=, bigOne ))
+ ResultFailed( "LL_DIV", "r != 16", bigSixTeen, result);
+
+
+ return;
+}
+
+static void TestWellknowns(void)
+{
+ PRInt64 max = LL_MAXINT, min = LL_MININT, zero = LL_ZERO;
+ PRInt64 mmax = LL_MaxInt(), mmin = LL_MinInt(), mzero = LL_Zero();
+ if (LL_NE(max, mmax))
+ ResultFailed( "max, mmax", "max != mmax", max, mmax);
+ if (LL_NE(min, mmin))
+ ResultFailed( "min, mmin", "min != mmin", max, mmin);
+ if (LL_NE(zero, mzero))
+ ResultFailed( "zero, mzero", "zero != mzero", zero, mzero);
+} /* TestWellknowns */
+
+/*
+** Initialize() -- Initialize the test case
+**
+** Parse command line options
+**
+*/
+static PRIntn
+Initialize( PRIntn argc, char **argv )
+{
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dvh");
+ PLOptStatus os;
+
+ /*
+ ** Parse command line options
+ */
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* set debug mode */
+ debugMode = PR_TRUE;
+ break;
+
+ case 'v': /* set verbose mode */
+ verboseMode = PR_TRUE;
+ debugMode = PR_TRUE;
+ break;
+
+ case 'h': /* user wants some guidance */
+ default:
+ PR_fprintf(output, "You get help.\n");
+ return(1);
+ }
+ }
+ PL_DestroyOptState(opt);
+ return(0);
+}
+
+PRIntn main( int argc, char **argv )
+{
+ PR_STDIO_INIT();
+ output = PR_GetSpecialFD(PR_StandardError);
+
+ if ( Initialize( argc, argv ))
+ return(1);
+
+ TestAssignment();
+ TestComparisons();
+ TestLogicalOperations();
+ TestConversion();
+ TestShift();
+ TestArithmetic();
+ TestWellknowns();
+
+ /*
+ ** That's all folks!
+ */
+ if ( failedAlready )
+ {
+ PR_fprintf(output, "FAIL\n");\
+ }
+ else
+ {
+ PR_fprintf(output, "PASS\n");\
+ }
+ return failedAlready;
+} /* end main() */
diff --git a/pr/tests/lock.c b/pr/tests/lock.c
new file mode 100644
index 00000000..6f4616dc
--- /dev/null
+++ b/pr/tests/lock.c
@@ -0,0 +1,528 @@
+/* -*- 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: lock.c
+** Purpose: test basic locking functions
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+**
+** 11-Aug-97 LarryH. Win16 port of NSPR.
+** - Added "PASS", "FAIL" messages on completion.
+** - Change stack variables to static scope variables
+** because of shadow-stack use by Win16
+** - Added PR_CALLBACK attribute to functions called by NSPR
+** - Added command line arguments:
+** - l <num> to control the number of loops
+** - c <num> to control the number of CPUs.
+** (was positional argv).
+**
+**
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "prio.h"
+#include "prcmon.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prprf.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prmon.h"
+#include "prmem.h"
+#include "prthread.h"
+#include "prtypes.h"
+
+#include "plstr.h"
+
+#include <stdlib.h>
+
+#if defined(XP_UNIX)
+#include <string.h>
+#endif
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+#endif
+
+static PRIntn failed_already=0;
+static PRFileDesc *std_err = NULL;
+static PRBool verbosity = PR_FALSE;
+static PRBool debug_mode = PR_FALSE;
+
+const static PRIntervalTime contention_interval = 50;
+
+typedef struct LockContentious_s {
+ PRLock *ml;
+ PRInt32 loops;
+ PRUint32 contender;
+ PRUint32 contentious;
+ PRIntervalTime overhead;
+ PRIntervalTime interval;
+} LockContentious_t;
+
+typedef struct MonitorContentious_s {
+ PRMonitor *ml;
+ PRInt32 loops;
+ PRUint32 contender;
+ PRUint32 contentious;
+ PRIntervalTime overhead;
+ PRIntervalTime interval;
+} MonitorContentious_t;
+
+
+static PRIntervalTime Sleeper(PRUint32 loops)
+{
+ PRIntervalTime predicted = 0;
+ while (loops-- > 0)
+ {
+ predicted += contention_interval;
+ (void)PR_Sleep(contention_interval);
+ }
+ return predicted;
+} /* Sleeper */
+
+/*
+** BASIC LOCKS
+*/
+static PRIntervalTime MakeLock(PRUint32 loops)
+{
+ PRLock *ml = NULL;
+ while (loops-- > 0)
+ {
+ ml = PR_NewLock();
+ PR_DestroyLock(ml);
+ ml = NULL;
+ }
+ return 0;
+} /* MakeLock */
+
+static PRIntervalTime NonContentiousLock(PRUint32 loops)
+{
+ PRLock *ml = NULL;
+ ml = PR_NewLock();
+ while (loops-- > 0)
+ {
+ PR_Lock(ml);
+ PR_Unlock(ml);
+ }
+ PR_DestroyLock(ml);
+ return 0;
+} /* NonContentiousLock */
+
+static void PR_CALLBACK LockContender(void *arg)
+{
+ LockContentious_t *contention = (LockContentious_t*)arg;
+ while (contention->loops-- > 0)
+ {
+ PR_Lock(contention->ml);
+ contention->contender+= 1;
+ contention->overhead += contention->interval;
+ PR_Sleep(contention->interval);
+ PR_Unlock(contention->ml);
+ }
+} /* LockContender */
+
+static PRIntervalTime ContentiousLock(PRUint32 loops)
+{
+ PRStatus status;
+ PRThread *thread = NULL;
+ LockContentious_t * contention;
+ PRIntervalTime rv, overhead, timein = PR_IntervalNow();
+
+ contention = PR_NEWZAP(LockContentious_t);
+ contention->loops = loops;
+ contention->overhead = 0;
+ contention->ml = PR_NewLock();
+ contention->interval = contention_interval;
+ thread = PR_CreateThread(
+ PR_USER_THREAD, LockContender, contention,
+ PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ PR_ASSERT(thread != NULL);
+
+ overhead = PR_IntervalNow() - timein;
+
+ while (contention->loops-- > 0)
+ {
+ PR_Lock(contention->ml);
+ contention->contentious+= 1;
+ contention->overhead += contention->interval;
+ PR_Sleep(contention->interval);
+ PR_Unlock(contention->ml);
+ }
+
+ timein = PR_IntervalNow();
+ status = PR_JoinThread(thread);
+ PR_DestroyLock(contention->ml);
+ overhead += (PR_IntervalNow() - timein);
+ rv = overhead + contention->overhead;
+ if (verbosity)
+ PR_fprintf(
+ std_err, "Access ratio: %u to %u\n",
+ contention->contentious, contention->contender);
+ PR_Free(contention);
+ return rv;
+} /* ContentiousLock */
+
+/*
+** MONITORS
+*/
+static PRIntervalTime MakeMonitor(PRUint32 loops)
+{
+ PRMonitor *ml = NULL;
+ while (loops-- > 0)
+ {
+ ml = PR_NewMonitor();
+ PR_DestroyMonitor(ml);
+ ml = NULL;
+ }
+ return 0;
+} /* MakeMonitor */
+
+static PRIntervalTime NonContentiousMonitor(PRUint32 loops)
+{
+ PRMonitor *ml = NULL;
+ ml = PR_NewMonitor();
+ while (loops-- > 0)
+ {
+ PR_EnterMonitor(ml);
+ PR_ExitMonitor(ml);
+ }
+ PR_DestroyMonitor(ml);
+ return 0;
+} /* NonContentiousMonitor */
+
+static void PR_CALLBACK TryEntry(void *arg)
+{
+ PRMonitor *ml = (PRMonitor*)arg;
+ if (debug_mode) PR_fprintf(std_err, "Reentrant thread created\n");
+ PR_EnterMonitor(ml);
+ if (debug_mode) PR_fprintf(std_err, "Reentrant thread acquired monitor\n");
+ PR_ExitMonitor(ml);
+ if (debug_mode) PR_fprintf(std_err, "Reentrant thread released monitor\n");
+} /* TryEntry */
+
+static PRIntervalTime ReentrantMonitor(PRUint32 loops)
+{
+ PRStatus status;
+ PRThread *thread;
+ PRMonitor *ml = PR_NewMonitor();
+ if (debug_mode) PR_fprintf(std_err, "\nMonitor created for reentrant test\n");
+
+ PR_EnterMonitor(ml);
+ PR_EnterMonitor(ml);
+ if (debug_mode) PR_fprintf(std_err, "Monitor acquired twice\n");
+
+ thread = PR_CreateThread(
+ PR_USER_THREAD, TryEntry, ml,
+ PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ PR_ASSERT(thread != NULL);
+ PR_Sleep(PR_SecondsToInterval(1));
+
+ PR_ExitMonitor(ml);
+ if (debug_mode) PR_fprintf(std_err, "Monitor released first time\n");
+
+ PR_ExitMonitor(ml);
+ if (debug_mode) PR_fprintf(std_err, "Monitor released second time\n");
+
+ status = PR_JoinThread(thread);
+ if (debug_mode) PR_fprintf(std_err,
+ "Reentrant thread joined %s\n",
+ (status == PR_SUCCESS) ? "successfully" : "in error");
+
+ PR_DestroyMonitor(ml);
+ return 0;
+} /* ReentrantMonitor */
+
+static void PR_CALLBACK MonitorContender(void *arg)
+{
+ MonitorContentious_t *contention = (MonitorContentious_t*)arg;
+ while (contention->loops-- > 0)
+ {
+ PR_EnterMonitor(contention->ml);
+ contention->contender+= 1;
+ contention->overhead += contention->interval;
+ PR_Sleep(contention->interval);
+ PR_ExitMonitor(contention->ml);
+ }
+} /* MonitorContender */
+
+static PRUint32 ContentiousMonitor(PRUint32 loops)
+{
+ PRStatus status;
+ PRThread *thread = NULL;
+ MonitorContentious_t * contention;
+ PRIntervalTime rv, overhead, timein = PR_IntervalNow();
+
+ contention = PR_NEWZAP(MonitorContentious_t);
+ contention->loops = loops;
+ contention->overhead = 0;
+ contention->ml = PR_NewMonitor();
+ contention->interval = contention_interval;
+ thread = PR_CreateThread(
+ PR_USER_THREAD, MonitorContender, contention,
+ PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ PR_ASSERT(thread != NULL);
+
+ overhead = PR_IntervalNow() - timein;
+
+ while (contention->loops-- > 0)
+ {
+ PR_EnterMonitor(contention->ml);
+ contention->contentious+= 1;
+ contention->overhead += contention->interval;
+ PR_Sleep(contention->interval);
+ PR_ExitMonitor(contention->ml);
+ }
+
+ timein = PR_IntervalNow();
+ status = PR_JoinThread(thread);
+ PR_DestroyMonitor(contention->ml);
+ overhead += (PR_IntervalNow() - timein);
+ rv = overhead + contention->overhead;
+ if (verbosity)
+ PR_fprintf(
+ std_err, "Access ratio: %u to %u\n",
+ contention->contentious, contention->contender);
+ PR_Free(contention);
+ return rv;
+} /* ContentiousMonitor */
+
+/*
+** CACHED MONITORS
+*/
+static PRIntervalTime NonContentiousCMonitor(PRUint32 loops)
+{
+ MonitorContentious_t contention;
+ while (loops-- > 0)
+ {
+ PR_CEnterMonitor(&contention);
+ PR_CExitMonitor(&contention);
+ }
+ return 0;
+} /* NonContentiousCMonitor */
+
+static void PR_CALLBACK Contender(void *arg)
+{
+ MonitorContentious_t *contention = (MonitorContentious_t*)arg;
+ while (contention->loops-- > 0)
+ {
+ PR_CEnterMonitor(contention);
+ contention->contender+= 1;
+ contention->overhead += contention->interval;
+ PR_Sleep(contention->interval);
+ PR_CExitMonitor(contention);
+ }
+} /* Contender */
+
+static PRIntervalTime ContentiousCMonitor(PRUint32 loops)
+{
+ PRStatus status;
+ PRThread *thread = NULL;
+ MonitorContentious_t * contention;
+ PRIntervalTime overhead, timein = PR_IntervalNow();
+
+ contention = PR_NEWZAP(MonitorContentious_t);
+ contention->ml = NULL;
+ contention->loops = loops;
+ contention->interval = contention_interval;
+ thread = PR_CreateThread(
+ PR_USER_THREAD, Contender, contention,
+ PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ PR_ASSERT(thread != NULL);
+
+ overhead = PR_IntervalNow() - timein;
+
+ while (contention->loops-- > 0)
+ {
+ PR_CEnterMonitor(contention);
+ contention->contentious+= 1;
+ contention->overhead += contention->interval;
+ PR_Sleep(contention->interval);
+ PR_CExitMonitor(contention);
+ }
+
+ timein = PR_IntervalNow();
+ status = PR_JoinThread(thread);
+ overhead += (PR_IntervalNow() - timein);
+ overhead += overhead + contention->overhead;
+ if (verbosity)
+ PR_fprintf(
+ std_err, "Access ratio: %u to %u\n",
+ contention->contentious, contention->contender);
+ PR_Free(contention);
+ return overhead;
+} /* ContentiousCMonitor */
+
+static PRIntervalTime Test(
+ const char* msg, PRUint32 (*test)(PRUint32 loops),
+ PRUint32 loops, PRIntervalTime overhead)
+{
+ /*
+ * overhead - overhead not measured by the test.
+ * duration - wall clock time it took to perform test.
+ * predicted - extra time test says should not be counted
+ *
+ * Time accountable to the test is duration - overhead - predicted
+ * All times are Intervals and accumulated for all iterations.
+ */
+ PRFloat64 elapsed;
+ PRIntervalTime accountable, duration;
+ PRUintn spaces = PL_strlen(msg);
+ PRIntervalTime timeout, timein = PR_IntervalNow();
+ PRIntervalTime predicted = test(loops);
+ timeout = PR_IntervalNow();
+ duration = timeout - timein;
+
+ if (debug_mode)
+ {
+ accountable = duration - predicted;
+ accountable -= overhead;
+ elapsed = (PRFloat64)PR_IntervalToMicroseconds(accountable);
+ PR_fprintf(PR_STDOUT, "%s:", msg);
+ while (spaces++ < 50) PR_fprintf(PR_STDOUT, " ");
+ if ((PRInt32)accountable < 0)
+ PR_fprintf(PR_STDOUT, "*****.** usecs/iteration\n");
+ else
+ PR_fprintf(PR_STDOUT, "%8.2f usecs/iteration\n", elapsed/loops);
+ }
+ return duration;
+} /* Test */
+
+int main(int argc, char **argv)
+{
+ PRBool rv = PR_TRUE;
+ PRIntervalTime duration;
+ PRUint32 cpu, cpus = 2, loops = 100;
+
+
+ PR_STDIO_INIT();
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ {
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Command line argument -l <num> sets the number of loops.
+ Command line argument -c <num> sets the number of cpus.
+ Usage: lock [-d] [-l <num>] [-c <num>]
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dvl:c:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'v': /* debug mode */
+ verbosity = PR_TRUE;
+ break;
+ case 'l': /* number of loops */
+ loops = atoi(opt->value);
+ break;
+ case 'c': /* number of cpus */
+ cpus = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ }
+
+ /* main test */
+ PR_SetConcurrency(8);
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("lock.log");
+ debug_mode = 1;
+#endif
+
+ if (loops == 0) loops = 100;
+ if (debug_mode)
+ {
+ std_err = PR_STDERR;
+ PR_fprintf(std_err, "Lock: Using %d loops\n", loops);
+ }
+
+ if (cpus == 0) cpus = 2;
+ if (debug_mode) PR_fprintf(std_err, "Lock: Using %d cpu(s)\n", cpus);
+
+ (void)Sleeper(10); /* try filling in the caches */
+
+ for (cpu = 1; cpu <= cpus; ++cpu)
+ {
+ if (debug_mode) PR_fprintf(std_err, "\nLock: Using %d CPU(s)\n", cpu);
+ PR_SetConcurrency(cpu);
+
+ duration = Test("Overhead of PR_Sleep", Sleeper, loops, 0);
+ duration = 0;
+
+ (void)Test("Lock creation/deletion", MakeLock, loops, 0);
+ (void)Test("Lock non-contentious locking/unlocking", NonContentiousLock, loops, 0);
+ (void)Test("Lock contentious locking/unlocking", ContentiousLock, loops, duration);
+ (void)Test("Monitor creation/deletion", MakeMonitor, loops, 0);
+ (void)Test("Monitor non-contentious locking/unlocking", NonContentiousMonitor, loops, 0);
+ (void)Test("Monitor contentious locking/unlocking", ContentiousMonitor, loops, duration);
+
+ (void)Test("Cached monitor non-contentious locking/unlocking", NonContentiousCMonitor, loops, 0);
+ (void)Test("Cached monitor contentious locking/unlocking", ContentiousCMonitor, loops, duration);
+
+ (void)ReentrantMonitor(loops);
+ }
+
+ if (debug_mode)
+ PR_fprintf(
+ std_err, "%s: test %s\n", "Lock(mutex) test",
+ ((rv) ? "passed" : "failed"));
+ else {
+ if (!rv)
+ failed_already=1;
+ }
+
+ if(failed_already)
+ {
+ PR_fprintf(PR_STDOUT, "FAIL\n");
+ return 1;
+ }
+ else
+ {
+ PR_fprintf(PR_STDOUT, "PASS\n");
+ return 0;
+ }
+
+} /* main */
+
+/* testlock.c */
diff --git a/pr/tests/lockfile.c b/pr/tests/lockfile.c
new file mode 100644
index 00000000..9008b602
--- /dev/null
+++ b/pr/tests/lockfile.c
@@ -0,0 +1,260 @@
+/* -*- 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: lockfile.c
+** Purpose: test basic locking functions
+** Just because this times stuff, don't think its a perforamnce
+** test!!!
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "prcmon.h"
+#include "prerror.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prmon.h"
+#include "prthread.h"
+#include "prtypes.h"
+
+#ifndef XP_MAC
+#include "private/pprio.h"
+#else
+#include "pprio.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+#endif
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+const static PRIntervalTime contention_interval = 50;
+
+typedef struct LockContentious_s {
+ PRLock *ml;
+ PRUint32 loops;
+ PRIntervalTime overhead;
+ PRIntervalTime interval;
+} LockContentious_t;
+
+#define LOCKFILE "prlock.fil"
+
+
+
+static PRIntervalTime NonContentiousLock(PRUint32 loops)
+{
+ PRFileDesc *_lockfile;
+ while (loops-- > 0)
+ {
+ _lockfile = PR_Open(LOCKFILE, PR_CREATE_FILE|PR_RDWR, 0666);
+ if (!_lockfile) {
+ if (debug_mode) printf(
+ "could not create lockfile: %d [%d]\n",
+ PR_GetError(), PR_GetOSError());
+ return PR_INTERVAL_NO_TIMEOUT;
+ }
+ PR_LockFile(_lockfile);
+ PR_UnlockFile(_lockfile);
+ PR_Close(_lockfile);
+ }
+ return 0;
+} /* NonContentiousLock */
+
+static void PR_CALLBACK LockContender(void *arg)
+{
+ LockContentious_t *contention = (LockContentious_t*)arg;
+ PRFileDesc *_lockfile;
+ while (contention->loops-- > 0)
+ {
+ _lockfile = PR_Open(LOCKFILE, PR_CREATE_FILE|PR_RDWR, 0666);
+ if (!_lockfile) {
+ if (debug_mode) printf(
+ "could not create lockfile: %d [%d]\n",
+ PR_GetError(), PR_GetOSError());
+ break;
+ }
+ PR_LockFile(_lockfile);
+ PR_Sleep(contention->interval);
+ PR_UnlockFile(_lockfile);
+ PR_Close(_lockfile);
+ }
+
+} /* LockContender */
+
+/*
+** Win16 requires things passed to Threads not be on the stack
+*/
+static LockContentious_t contention;
+
+static PRIntervalTime ContentiousLock(PRUint32 loops)
+{
+ PRStatus status;
+ PRThread *thread = NULL;
+ PRIntervalTime overhead, timein = PR_IntervalNow();
+
+ contention.loops = loops;
+ contention.overhead = 0;
+ contention.ml = PR_NewLock();
+ contention.interval = contention_interval;
+ thread = PR_CreateThread(
+ PR_USER_THREAD, LockContender, &contention,
+ PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ PR_ASSERT(thread != NULL);
+
+ overhead = PR_IntervalNow() - timein;
+
+ while (contention.loops > 0)
+ {
+ PR_Lock(contention.ml);
+ contention.overhead += contention.interval;
+ PR_Sleep(contention.interval);
+ PR_Unlock(contention.ml);
+ }
+
+ timein = PR_IntervalNow();
+ status = PR_JoinThread(thread);
+ PR_DestroyLock(contention.ml);
+ overhead += (PR_IntervalNow() - timein);
+ return overhead + contention.overhead;
+} /* ContentiousLock */
+
+static PRIntervalTime Test(
+ const char* msg, PRIntervalTime (*test)(PRUint32 loops),
+ PRUint32 loops, PRIntervalTime overhead)
+{
+ /*
+ * overhead - overhead not measured by the test.
+ * duration - wall clock time it took to perform test.
+ * predicted - extra time test says should not be counted
+ *
+ * Time accountable to the test is duration - overhead - predicted
+ * All times are Intervals and accumulated for all iterations.
+ */
+ PRFloat64 elapsed;
+ PRIntervalTime accountable, duration;
+ PRUintn spaces = strlen(msg);
+ PRIntervalTime timeout, timein = PR_IntervalNow();
+ PRIntervalTime predicted = test(loops);
+ timeout = PR_IntervalNow();
+ duration = timeout - timein;
+ accountable = duration - predicted;
+ accountable -= overhead;
+ elapsed = (PRFloat64)PR_IntervalToMicroseconds(accountable);
+ if (debug_mode) printf("%s:", msg);
+ while (spaces++ < 50) if (debug_mode) printf(" ");
+ if ((PRInt32)accountable < 0)
+ if (debug_mode) printf("*****.** usecs/iteration\n");
+ else
+ if (debug_mode) printf("%8.2f usecs/iteration\n", elapsed/loops);
+ return duration;
+} /* Test */
+
+int main(int argc, char **argv)
+{
+ PRBool rv = PR_TRUE;
+ PRIntervalTime duration;
+ PRUint32 cpu, cpus = 2, loops = 100;
+
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("lockfile.log");
+ debug_mode = 1;
+#endif
+
+ if (argc > 1) loops = atoi(argv[1]);
+ if (loops == 0) loops = 100;
+ if (debug_mode) printf("Lock: Using %d loops\n", loops);
+
+ cpus = (argc < 3) ? 2 : atoi(argv[2]);
+ if (cpus == 0) cpus = 2;
+ if (debug_mode) printf("Lock: Using %d cpu(s)\n", cpus);
+
+
+ for (cpu = 1; cpu <= cpus; ++cpu)
+ {
+ if (debug_mode) printf("\nLockFile: Using %d CPU(s)\n", cpu);
+ PR_SetConcurrency(cpu);
+
+ (void)Test("LockFile non-contentious locking/unlocking", NonContentiousLock, loops, 0);
+ (void)Test("LockFile contentious locking/unlocking", ContentiousLock, loops, duration);
+ }
+
+ PR_Delete(LOCKFILE); /* try to get rid of evidence */
+
+ if (debug_mode) printf("%s: test %s\n", "Lock(mutex) test", ((rv) ? "passed" : "failed"));
+ else {
+ if (!rv)
+ failed_already=1;
+ }
+ if(failed_already)
+ return 1;
+ else
+ return 0;
+} /* main */
+
+/* testlock.c */
diff --git a/pr/tests/logger.c b/pr/tests/logger.c
new file mode 100644
index 00000000..38780273
--- /dev/null
+++ b/pr/tests/logger.c
@@ -0,0 +1,143 @@
+/* -*- 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: logger.c
+ * Description: test program for logging's basic functions
+ */
+
+#include "prinit.h"
+#include "prlog.h"
+#include "prlock.h"
+#include "prcvar.h"
+#include "prthread.h"
+#include "prinrval.h"
+
+#include <stdio.h>
+
+#ifdef XP_MAC
+extern void SetupMacPrintfLog(char *logFile);
+#endif
+
+/* lth. re-define PR_LOG() */
+#if 0
+#undef PR_LOG_TEST
+#undef PR_LOG
+#define PR_LOG_TEST(_module,_level) ((_module)->level <= (_level))
+#define PR_LOG(_module,_level,_args) \
+ { \
+ if (PR_LOG_TEST(_module,_level)) \
+ PR_LogPrint _args ; \
+ }
+#endif
+
+
+static void Error(const char* msg)
+{
+ printf("\t%s\n", msg);
+} /* Error */
+
+static void PR_CALLBACK forked(void *arg)
+{
+ PRIntn i;
+ PRLock *ml;
+ PRCondVar *cv;
+
+ PR_LogPrint("%s logging creating mutex\n", (const char*)arg);
+ ml = PR_NewLock();
+ PR_LogPrint("%s logging creating condition variable\n", (const char*)arg);
+ cv = PR_NewCondVar(ml);
+
+ PR_LogPrint("%s waiting on condition timeout 10 times\n", (const char*)arg);
+ for (i = 0; i < 10; ++i)
+ {
+ PR_Lock(ml);
+ PR_WaitCondVar(cv, PR_SecondsToInterval(1));
+ PR_Unlock(ml);
+ }
+
+ PR_LogPrint("%s logging destroying condition variable\n", (const char*)arg);
+ PR_DestroyCondVar(cv);
+ PR_LogPrint("%s logging destroying mutex\n", (const char*)arg);
+ PR_DestroyLock(ml);
+ PR_LogPrint("%s forked thread exiting\n", (const char*)arg);
+}
+
+static void UserLogStuff( void )
+{
+ PRLogModuleInfo *myLM;
+ PRIntn i;
+
+ myLM = PR_NewLogModule( "userStuff" );
+ if (! myLM )
+ {
+ printf("UserLogStuff(): can't create new log module\n" );
+ return;
+ }
+
+ PR_LOG( myLM, PR_LOG_NOTICE, ("Log a Notice %d\n", 1 ));
+
+ for (i = 0; i < 10 ; i++ )
+ {
+ PR_LOG( myLM, PR_LOG_DEBUG, ("Log Debug number: %d\n", i));
+ PR_Sleep( 300 );
+ }
+
+} /* end UserLogStuff() */
+
+int main(PRIntn argc, const char **argv)
+{
+ PRThread *thread;
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+#ifndef XP_MAC
+ if (argc > 1)
+ {
+ if (!PR_SetLogFile(argv[1]))
+ {
+ Error("Access: Cannot create log file");
+ goto exit;
+ }
+ }
+#else
+ SetupMacPrintfLog("logger.log");
+#endif
+
+ /* Start logging something here */
+ PR_LogPrint("%s logging into %s\n", argv[0], argv[1]);
+
+ PR_LogPrint("%s creating new thread\n", argv[0]);
+ thread = PR_CreateThread(
+ PR_USER_THREAD, forked, (void*)argv[0], PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ PR_LogPrint("%s joining thread\n", argv[0]);
+
+ UserLogStuff();
+
+ PR_JoinThread(thread);
+
+ PR_LogFlush();
+ return 0;
+
+exit:
+ return -1;
+}
+
+/* logger.c */
diff --git a/pr/tests/many_cv.c b/pr/tests/many_cv.c
new file mode 100644
index 00000000..1f964458
--- /dev/null
+++ b/pr/tests/many_cv.c
@@ -0,0 +1,129 @@
+/* -*- 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 "prinit.h"
+#include "prprf.h"
+#include "prthread.h"
+#include "prcvar.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prmem.h"
+
+#include "primpl.h"
+
+#include "plgetopt.h"
+
+#include <stdlib.h>
+
+static PRInt32 Random(void)
+{
+ PRInt32 ran = rand() >> 16;
+ return ran;
+} /* Random */
+
+static void Help(void)
+{
+ PRFileDesc *err = PR_GetSpecialFD(PR_StandardError);
+ PR_fprintf(err, "many_cv usage: [-c n] [-l n] [-h]\n");
+ PR_fprintf(err, "\t-c n Number of conditions per lock (default: 10)\n");
+ PR_fprintf(err, "\t-l n Number of times to loop the test (default: 1)\n");
+ PR_fprintf(err, "\t-h This message and nothing else\n");
+} /* Help */
+
+static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv )
+{
+ PLOptStatus os;
+ PRIntn index, nl;
+ PRLock *ml = NULL;
+ PRCondVar **cv = NULL;
+ PRBool stats = PR_FALSE;
+ PRIntn nc, loops = 1, cvs = 10;
+ PRFileDesc *err = PR_GetSpecialFD(PR_StandardError);
+ PLOptState *opt = PL_CreateOptState(argc, argv, "hsc:l:");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 's': /* number of CVs to association with lock */
+ stats = PR_TRUE;
+ break;
+ case 'c': /* number of CVs to association with lock */
+ cvs = atoi(opt->value);
+ break;
+ case 'l': /* number of times to run the tests */
+ loops = atoi(opt->value);
+ break;
+ case 'h': /* user wants some guidance */
+ default:
+ Help(); /* so give him an earful */
+ return 2; /* but not a lot else */
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_fprintf(err, "Settings\n");
+ PR_fprintf(err, "\tConditions / lock: %d\n", cvs);
+ PR_fprintf(err, "\tLoops to run test: %d\n", loops);
+
+ ml = PR_NewLock();
+ PR_ASSERT(NULL != ml);
+
+ cv = (PRCondVar**)PR_CALLOC(sizeof(PRCondVar*) * cvs);
+ PR_ASSERT(NULL != cv);
+
+ for (index = 0; index < cvs; ++index)
+ {
+ cv[index] = PR_NewCondVar(ml);
+ PR_ASSERT(NULL != cv[index]);
+ }
+
+ for (index = 0; index < loops; ++index)
+ {
+ PR_Lock(ml);
+ for (nl = 0; nl < cvs; ++nl)
+ {
+ PRInt32 ran = Random() % 8;
+ if (0 == ran) PR_NotifyAllCondVar(cv[nl]);
+ else for (nc = 0; nc < ran; ++nc)
+ PR_NotifyCondVar(cv[nl]);
+ }
+ PR_Unlock(ml);
+ }
+
+ for (index = 0; index < cvs; ++index)
+ PR_DestroyCondVar(cv[index]);
+
+ PR_DestroyLock(ml);
+
+ printf("PASS\n");
+
+ PT_FPrintStats(err, "\nPThread Statistics\n");
+ return 0;
+}
+
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
diff --git a/pr/tests/multiwait.c b/pr/tests/multiwait.c
new file mode 100644
index 00000000..ad961b17
--- /dev/null
+++ b/pr/tests/multiwait.c
@@ -0,0 +1,704 @@
+/* -*- 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 "prio.h"
+#include "prprf.h"
+#include "prlog.h"
+#include "prmem.h"
+#include "pratom.h"
+#include "prlock.h"
+#include "prmwait.h"
+#include "prclist.h"
+#include "prerror.h"
+#include "prinrval.h"
+#include "prnetdb.h"
+#include "prthread.h"
+
+#include "plstr.h"
+#include "plerror.h"
+#include "plgetopt.h"
+
+typedef struct Shared
+{
+ const char *title;
+ PRLock *list_lock;
+ PRWaitGroup *group;
+ PRIntervalTime timeout;
+} Shared;
+
+typedef enum Verbosity {silent, quiet, chatty, noisy} Verbosity;
+
+static PRFileDesc *debug = NULL;
+static PRInt32 desc_allocated = 0;
+static PRUint16 default_port = 12273;
+static enum Verbosity verbosity = quiet;
+static PRInt32 ops_required = 1000, ops_done = 0;
+static PRThreadScope thread_scope = PR_LOCAL_THREAD;
+static PRIntn client_threads = 20, worker_threads = 2, wait_objects = 50;
+
+#if defined(DEBUG)
+#define MW_ASSERT(_expr) \
+ ((_expr)?((void)0):_MW_Assert(# _expr,__FILE__,__LINE__))
+static void _MW_Assert(const char *s, const char *file, PRIntn ln)
+{
+ if (NULL != debug) PL_FPrintError(debug, NULL);
+ PR_Assert(s, file, ln);
+} /* _MW_Assert */
+#else
+#define MW_ASSERT(_expr)
+#endif
+
+static void PrintRecvDesc(PRRecvWait *desc, const char *msg)
+{
+ const char *tag[] = {
+ "PR_MW_INTERRUPT", "PR_MW_TIMEOUT",
+ "PR_MW_FAILURE", "PR_MW_SUCCESS", "PR_MW_PENDING"};
+ PR_fprintf(
+ debug, "%s: PRRecvWait(@0x%x): {fd: 0x%x, outcome: %s, tmo: %u}\n",
+ msg, desc, desc->fd, tag[desc->outcome + 3], desc->timeout);
+} /* PrintRecvDesc */
+
+static Shared *MakeShared(const char *title)
+{
+ Shared *shared = PR_NEWZAP(Shared);
+ shared->group = PR_CreateWaitGroup(1);
+ shared->timeout = PR_SecondsToInterval(1);
+ shared->list_lock = PR_NewLock();
+ shared->title = title;
+ return shared;
+} /* MakeShared */
+
+static void DestroyShared(Shared *shared)
+{
+ PRStatus rv;
+ if (verbosity > quiet)
+ PR_fprintf(debug, "%s: destroying group\n", shared->title);
+ rv = PR_DestroyWaitGroup(shared->group);
+ MW_ASSERT(PR_SUCCESS == rv);
+ PR_DestroyLock(shared->list_lock);
+ PR_DELETE(shared);
+} /* DestroyShared */
+
+static PRRecvWait *CreateRecvWait(PRFileDesc *fd, PRIntervalTime timeout)
+{
+ PRRecvWait *desc_out = PR_NEWZAP(PRRecvWait);
+ MW_ASSERT(NULL != desc_out);
+
+ MW_ASSERT(NULL != fd);
+ desc_out->fd = fd;
+ desc_out->timeout = timeout;
+ desc_out->buffer.length = 120;
+ desc_out->buffer.start = PR_CALLOC(120);
+
+ PR_AtomicIncrement(&desc_allocated);
+
+ if (verbosity > chatty)
+ PrintRecvDesc(desc_out, "Allocated");
+ return desc_out;
+} /* CreateRecvWait */
+
+static void DestroyRecvWait(PRRecvWait *desc_out)
+{
+ if (verbosity > chatty)
+ PrintRecvDesc(desc_out, "Destroying");
+ PR_Close(desc_out->fd);
+ if (NULL != desc_out->buffer.start)
+ PR_DELETE(desc_out->buffer.start);
+ PR_Free(desc_out);
+ (void)PR_AtomicDecrement(&desc_allocated);
+} /* DestroyRecvWait */
+
+static void CancelGroup(Shared *shared)
+{
+ PRRecvWait *desc_out;
+
+ if (verbosity > quiet)
+ PR_fprintf(debug, "%s Reclaiming wait descriptors\n", shared->title);
+
+ do
+ {
+ desc_out = PR_CancelWaitGroup(shared->group);
+ if (NULL != desc_out) DestroyRecvWait(desc_out);
+ } while (NULL != desc_out);
+
+ MW_ASSERT(0 == desc_allocated);
+ MW_ASSERT(PR_GROUP_EMPTY_ERROR == PR_GetError());
+} /* CancelGroup */
+
+static void PR_CALLBACK ClientThread(void* arg)
+{
+ PRStatus rv;
+ PRInt32 bytes;
+ PRIntn empty_flags = 0;
+ PRNetAddr server_address;
+ unsigned char buffer[100];
+ Shared *shared = (Shared*)arg;
+ PRFileDesc *server = PR_NewTCPSocket();
+ if ((NULL == server)
+ && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) return;
+ MW_ASSERT(NULL != server);
+
+ if (verbosity > chatty)
+ PR_fprintf(debug, "%s: Server socket @0x%x\n", shared->title, server);
+
+ /* Initialize the buffer so that Purify won't complain */
+ memset(buffer, 0, sizeof(buffer));
+
+ rv = PR_InitializeNetAddr(PR_IpAddrLoopback, default_port, &server_address);
+ MW_ASSERT(PR_SUCCESS == rv);
+
+ if (verbosity > quiet)
+ PR_fprintf(debug, "%s: Client opening connection\n", shared->title);
+ rv = PR_Connect(server, &server_address, PR_INTERVAL_NO_TIMEOUT);
+
+ if (PR_FAILURE == rv)
+ {
+ if (verbosity > silent) PL_FPrintError(debug, "Client connect failed");
+ return;
+ }
+
+ while (ops_done < ops_required)
+ {
+ bytes = PR_Send(
+ server, buffer, sizeof(buffer), empty_flags, PR_INTERVAL_NO_TIMEOUT);
+ if ((-1 == bytes) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) break;
+ MW_ASSERT(sizeof(buffer) == bytes);
+ if (verbosity > chatty)
+ PR_fprintf(
+ debug, "%s: Client sent %d bytes\n",
+ shared->title, sizeof(buffer));
+ bytes = PR_Recv(
+ server, buffer, sizeof(buffer), empty_flags, PR_INTERVAL_NO_TIMEOUT);
+ if (verbosity > chatty)
+ PR_fprintf(
+ debug, "%s: Client received %d bytes\n",
+ shared->title, sizeof(buffer));
+ if ((-1 == bytes) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) break;
+ MW_ASSERT(sizeof(buffer) == bytes);
+ PR_Sleep(shared->timeout);
+ }
+ rv = PR_Close(server);
+ MW_ASSERT(PR_SUCCESS == rv);
+
+} /* ClientThread */
+
+static void OneInThenCancelled(Shared *shared)
+{
+ PRStatus rv;
+ PRRecvWait *desc_out, *desc_in = PR_NEWZAP(PRRecvWait);
+
+ shared->timeout = PR_INTERVAL_NO_TIMEOUT;
+
+ desc_in->fd = PR_NewTCPSocket();
+ desc_in->timeout = shared->timeout;
+
+ if (verbosity > chatty) PrintRecvDesc(desc_in, "Adding desc");
+
+ rv = PR_AddWaitFileDesc(shared->group, desc_in);
+ MW_ASSERT(PR_SUCCESS == rv);
+
+ if (verbosity > chatty) PrintRecvDesc(desc_in, "Cancelling");
+ rv = PR_CancelWaitFileDesc(shared->group, desc_in);
+ MW_ASSERT(PR_SUCCESS == rv);
+
+ desc_out = PR_WaitRecvReady(shared->group);
+ MW_ASSERT(desc_out == desc_in);
+ MW_ASSERT(PR_MW_INTERRUPT == desc_out->outcome);
+ MW_ASSERT(PR_PENDING_INTERRUPT_ERROR == PR_GetError());
+ if (verbosity > chatty) PrintRecvDesc(desc_out, "Ready");
+
+ rv = PR_Close(desc_in->fd);
+ MW_ASSERT(PR_SUCCESS == rv);
+
+ if (verbosity > quiet)
+ PR_fprintf(debug, "%s: destroying group\n", shared->title);
+
+ PR_DELETE(desc_in);
+} /* OneInThenCancelled */
+
+static void OneOpOneThread(Shared *shared)
+{
+ PRStatus rv;
+ PRRecvWait *desc_out, *desc_in = PR_NEWZAP(PRRecvWait);
+
+ desc_in->fd = PR_NewTCPSocket();
+ desc_in->timeout = shared->timeout;
+
+ if (verbosity > chatty) PrintRecvDesc(desc_in, "Adding desc");
+
+ rv = PR_AddWaitFileDesc(shared->group, desc_in);
+ MW_ASSERT(PR_SUCCESS == rv);
+ desc_out = PR_WaitRecvReady(shared->group);
+ MW_ASSERT(desc_out == desc_in);
+ MW_ASSERT(PR_MW_TIMEOUT == desc_out->outcome);
+ MW_ASSERT(PR_IO_TIMEOUT_ERROR == PR_GetError());
+ if (verbosity > chatty) PrintRecvDesc(desc_out, "Ready");
+
+ rv = PR_Close(desc_in->fd);
+ MW_ASSERT(PR_SUCCESS == rv);
+
+ PR_DELETE(desc_in);
+} /* OneOpOneThread */
+
+static void ManyOpOneThread(Shared *shared)
+{
+ PRStatus rv;
+ PRIntn index;
+ PRRecvWait *desc_in;
+ PRRecvWait *desc_out;
+
+ if (verbosity > quiet)
+ PR_fprintf(debug, "%s: adding %d descs\n", shared->title, wait_objects);
+
+ for (index = 0; index < wait_objects; ++index)
+ {
+ desc_in = CreateRecvWait(PR_NewTCPSocket(), shared->timeout);
+
+ rv = PR_AddWaitFileDesc(shared->group, desc_in);
+ MW_ASSERT(PR_SUCCESS == rv);
+ }
+
+ while (ops_done < ops_required)
+ {
+ desc_out = PR_WaitRecvReady(shared->group);
+ MW_ASSERT(PR_MW_TIMEOUT == desc_out->outcome);
+ MW_ASSERT(PR_IO_TIMEOUT_ERROR == PR_GetError());
+ if (verbosity > chatty) PrintRecvDesc(desc_out, "Ready/readding");
+ rv = PR_AddWaitFileDesc(shared->group, desc_out);
+ MW_ASSERT(PR_SUCCESS == rv);
+ (void)PR_AtomicIncrement(&ops_done);
+ }
+
+ CancelGroup(shared);
+} /* ManyOpOneThread */
+
+static void PR_CALLBACK SomeOpsThread(void *arg)
+{
+ PRRecvWait *desc_out;
+ PRStatus rv = PR_SUCCESS;
+ Shared *shared = (Shared*)arg;
+ do /* until interrupted */
+ {
+ desc_out = PR_WaitRecvReady(shared->group);
+ if (NULL == desc_out)
+ {
+ MW_ASSERT(PR_PENDING_INTERRUPT_ERROR == PR_GetError());
+ if (verbosity > quiet) PR_fprintf(debug, "Aborted\n");
+ break;
+ }
+ MW_ASSERT(PR_MW_TIMEOUT == desc_out->outcome);
+ MW_ASSERT(PR_IO_TIMEOUT_ERROR == PR_GetError());
+ if (verbosity > chatty) PrintRecvDesc(desc_out, "Ready");
+
+ if (verbosity > chatty) PrintRecvDesc(desc_out, "Re-Adding");
+ desc_out->timeout = shared->timeout;
+ rv = PR_AddWaitFileDesc(shared->group, desc_out);
+ PR_AtomicIncrement(&ops_done);
+ if (ops_done > ops_required) break;
+ } while (PR_SUCCESS == rv);
+ MW_ASSERT(PR_SUCCESS == rv);
+} /* SomeOpsThread */
+
+static void SomeOpsSomeThreads(Shared *shared)
+{
+ PRStatus rv;
+ PRThread **thread;
+ PRIntn index;
+ PRRecvWait *desc_in;
+
+ thread = (PRThread**)PR_CALLOC(sizeof(PRThread*) * worker_threads);
+
+ /* Create some threads */
+
+ if (verbosity > quiet)
+ PR_fprintf(debug, "%s: creating threads\n", shared->title);
+ for (index = 0; index < worker_threads; ++index)
+ {
+ thread[index] = PR_CreateThread(
+ PR_USER_THREAD, SomeOpsThread, shared,
+ PR_PRIORITY_HIGH, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ }
+
+ /* then create some operations */
+ if (verbosity > quiet)
+ PR_fprintf(debug, "%s: creating desc\n", shared->title);
+ for (index = 0; index < wait_objects; ++index)
+ {
+ desc_in = CreateRecvWait(PR_NewTCPSocket(), shared->timeout);
+ rv = PR_AddWaitFileDesc(shared->group, desc_in);
+ MW_ASSERT(PR_SUCCESS == rv);
+ }
+
+ if (verbosity > quiet)
+ PR_fprintf(debug, "%s: sleeping\n", shared->title);
+ while (ops_done < ops_required) PR_Sleep(shared->timeout);
+
+ if (verbosity > quiet)
+ PR_fprintf(debug, "%s: interrupting/joining threads\n", shared->title);
+ for (index = 0; index < worker_threads; ++index)
+ {
+ rv = PR_Interrupt(thread[index]);
+ MW_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(thread[index]);
+ MW_ASSERT(PR_SUCCESS == rv);
+ }
+ PR_DELETE(thread);
+
+ CancelGroup(shared);
+} /* SomeOpsSomeThreads */
+
+static PRStatus ServiceRequest(Shared *shared, PRRecvWait *desc)
+{
+ PRInt32 bytes_out;
+
+ if (verbosity > chatty)
+ PR_fprintf(
+ debug, "%s: Service received %d bytes\n",
+ shared->title, desc->bytesRecv);
+
+ if (0 == desc->bytesRecv) goto quitting;
+ if ((-1 == desc->bytesRecv)
+ && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) goto aborted;
+
+ bytes_out = PR_Send(
+ desc->fd, desc->buffer.start, desc->bytesRecv, 0, shared->timeout);
+ if (verbosity > chatty)
+ PR_fprintf(
+ debug, "%s: Service sent %d bytes\n",
+ shared->title, bytes_out);
+
+ if ((-1 == bytes_out)
+ && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) goto aborted;
+ MW_ASSERT(bytes_out == desc->bytesRecv);
+
+ return PR_SUCCESS;
+
+aborted:
+quitting:
+ return PR_FAILURE;
+} /* ServiceRequest */
+
+static void PR_CALLBACK ServiceThread(void *arg)
+{
+ PRStatus rv = PR_SUCCESS;
+ PRRecvWait *desc_out = NULL;
+ Shared *shared = (Shared*)arg;
+ do /* until interrupted */
+ {
+ if (NULL != desc_out)
+ {
+ desc_out->timeout = PR_INTERVAL_NO_TIMEOUT;
+ if (verbosity > chatty)
+ PrintRecvDesc(desc_out, "Service re-adding");
+ rv = PR_AddWaitFileDesc(shared->group, desc_out);
+ MW_ASSERT(PR_SUCCESS == rv);
+ }
+
+ desc_out = PR_WaitRecvReady(shared->group);
+ if (NULL == desc_out)
+ {
+ MW_ASSERT(PR_PENDING_INTERRUPT_ERROR == PR_GetError());
+ break;
+ }
+
+ switch (desc_out->outcome)
+ {
+ case PR_MW_SUCCESS:
+ {
+ PR_AtomicIncrement(&ops_done);
+ if (verbosity > chatty)
+ PrintRecvDesc(desc_out, "Service ready");
+ rv = ServiceRequest(shared, desc_out);
+ break;
+ }
+ case PR_MW_INTERRUPT:
+ MW_ASSERT(PR_PENDING_INTERRUPT_ERROR == PR_GetError());
+ rv = PR_FAILURE; /* if interrupted, then exit */
+ break;
+ case PR_MW_TIMEOUT:
+ MW_ASSERT(PR_IO_TIMEOUT_ERROR == PR_GetError());
+ case PR_MW_FAILURE:
+ if (verbosity > silent)
+ PL_FPrintError(debug, "RecvReady failure");
+ break;
+ default:
+ break;
+ }
+ } while (PR_SUCCESS == rv);
+
+ if (NULL != desc_out) DestroyRecvWait(desc_out);
+
+} /* ServiceThread */
+
+static void PR_CALLBACK EnumerationThread(void *arg)
+{
+ PRStatus rv;
+ PRIntn count;
+ PRRecvWait *desc;
+ Shared *shared = (Shared*)arg;
+ PRIntervalTime five_seconds = PR_SecondsToInterval(5);
+ PRMWaitEnumerator *enumerator = PR_CreateMWaitEnumerator(shared->group);
+ MW_ASSERT(NULL != enumerator);
+
+ while (PR_SUCCESS == PR_Sleep(five_seconds))
+ {
+ count = 0;
+ desc = NULL;
+ while (NULL != (desc = PR_EnumerateWaitGroup(enumerator, desc)))
+ {
+ if (verbosity > chatty) PrintRecvDesc(desc, shared->title);
+ count += 1;
+ }
+ if (verbosity > silent)
+ PR_fprintf(debug,
+ "%s Enumerated %d objects\n", shared->title, count);
+ }
+
+ MW_ASSERT(PR_PENDING_INTERRUPT_ERROR == PR_GetError());
+
+
+ rv = PR_DestroyMWaitEnumerator(enumerator);
+ MW_ASSERT(PR_SUCCESS == rv);
+} /* EnumerationThread */
+
+static void PR_CALLBACK ServerThread(void *arg)
+{
+ PRStatus rv;
+ PRIntn index;
+ PRRecvWait *desc_in;
+ PRThread **worker_thread;
+ Shared *shared = (Shared*)arg;
+ PRFileDesc *listener, *service;
+ PRNetAddr server_address, client_address;
+
+ worker_thread = (PRThread**)PR_CALLOC(sizeof(PRThread*) * worker_threads);
+ if (verbosity > quiet)
+ PR_fprintf(debug, "%s: Server creating worker_threads\n", shared->title);
+ for (index = 0; index < worker_threads; ++index)
+ {
+ worker_thread[index] = PR_CreateThread(
+ PR_USER_THREAD, ServiceThread, shared,
+ PR_PRIORITY_HIGH, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ }
+
+ rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &server_address);
+ MW_ASSERT(PR_SUCCESS == rv);
+
+ listener = PR_NewTCPSocket(); MW_ASSERT(NULL != listener);
+ if (verbosity > chatty)
+ PR_fprintf(
+ debug, "%s: Server listener socket @0x%x\n",
+ shared->title, listener);
+ rv = PR_Bind(listener, &server_address); MW_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Listen(listener, 10); MW_ASSERT(PR_SUCCESS == rv);
+ while (ops_done < ops_required)
+ {
+ if (verbosity > quiet)
+ PR_fprintf(debug, "%s: Server accepting connection\n", shared->title);
+ service = PR_Accept(listener, &client_address, PR_INTERVAL_NO_TIMEOUT);
+ if (NULL == service)
+ {
+ if (PR_PENDING_INTERRUPT_ERROR == PR_GetError()) break;
+ PL_PrintError("Accept failed");
+ MW_ASSERT(!"Accept failed");
+ }
+ else
+ {
+ desc_in = CreateRecvWait(service, shared->timeout);
+ desc_in->timeout = PR_INTERVAL_NO_TIMEOUT;
+ if (verbosity > chatty)
+ PrintRecvDesc(desc_in, "Service adding");
+ rv = PR_AddWaitFileDesc(shared->group, desc_in);
+ MW_ASSERT(PR_SUCCESS == rv);
+ }
+ }
+
+ if (verbosity > quiet)
+ PR_fprintf(debug, "%s: Server interrupting worker_threads\n", shared->title);
+ for (index = 0; index < worker_threads; ++index)
+ {
+ rv = PR_Interrupt(worker_thread[index]);
+ MW_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(worker_thread[index]);
+ MW_ASSERT(PR_SUCCESS == rv);
+ }
+ PR_DELETE(worker_thread);
+
+ PR_Close(listener);
+
+ CancelGroup(shared);
+
+} /* ServerThread */
+
+static void RealOneGroupIO(Shared *shared)
+{
+ /*
+ ** Create a server that listens for connections and then services
+ ** requests that come in over those connections. The server never
+ ** deletes a connection and assumes a basic RPC model of operation.
+ **
+ ** Use worker_threads threads to service how every many open ports
+ ** there might be.
+ **
+ ** Oh, ya. Almost forget. Create (some) clients as well.
+ */
+ PRStatus rv;
+ PRIntn index;
+ PRThread *server_thread, *enumeration_thread, **client_thread;
+
+ if (verbosity > quiet)
+ PR_fprintf(debug, "%s: creating server_thread\n", shared->title);
+
+ server_thread = PR_CreateThread(
+ PR_USER_THREAD, ServerThread, shared,
+ PR_PRIORITY_HIGH, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+
+ if (verbosity > quiet)
+ PR_fprintf(debug, "%s: creating enumeration_thread\n", shared->title);
+
+ enumeration_thread = PR_CreateThread(
+ PR_USER_THREAD, EnumerationThread, shared,
+ PR_PRIORITY_HIGH, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+
+ if (verbosity > quiet)
+ PR_fprintf(debug, "%s: snoozing before creating clients\n", shared->title);
+ PR_Sleep(5 * shared->timeout);
+
+ if (verbosity > quiet)
+ PR_fprintf(debug, "%s: creating client_threads\n", shared->title);
+ client_thread = (PRThread**)PR_CALLOC(sizeof(PRThread*) * client_threads);
+ for (index = 0; index < client_threads; ++index)
+ {
+ client_thread[index] = PR_CreateThread(
+ PR_USER_THREAD, ClientThread, shared,
+ PR_PRIORITY_NORMAL, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ }
+
+ while (ops_done < ops_required) PR_Sleep(shared->timeout);
+
+ if (verbosity > quiet)
+ PR_fprintf(debug, "%s: interrupting/joining client_threads\n", shared->title);
+ for (index = 0; index < client_threads; ++index)
+ {
+ rv = PR_Interrupt(client_thread[index]);
+ MW_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(client_thread[index]);
+ MW_ASSERT(PR_SUCCESS == rv);
+ }
+ PR_DELETE(client_thread);
+
+ if (verbosity > quiet)
+ PR_fprintf(debug, "%s: interrupting/joining enumeration_thread\n", shared->title);
+ rv = PR_Interrupt(enumeration_thread);
+ MW_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(enumeration_thread);
+ MW_ASSERT(PR_SUCCESS == rv);
+
+ if (verbosity > quiet)
+ PR_fprintf(debug, "%s: interrupting/joining server_thread\n", shared->title);
+ rv = PR_Interrupt(server_thread);
+ MW_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(server_thread);
+ MW_ASSERT(PR_SUCCESS == rv);
+} /* RealOneGroupIO */
+
+static void RunThisOne(
+ void (*func)(Shared*), const char *name, const char *test_name)
+{
+ Shared *shared;
+ if ((NULL == test_name) || (0 == PL_strcmp(name, test_name)))
+ {
+ if (verbosity > silent)
+ PR_fprintf(debug, "%s()\n", name);
+ shared = MakeShared(name);
+ ops_done = 0;
+ func(shared); /* run the test */
+ MW_ASSERT(0 == desc_allocated);
+ DestroyShared(shared);
+ }
+} /* RunThisOne */
+
+static Verbosity ChangeVerbosity(Verbosity verbosity, PRIntn delta)
+{
+ PRIntn verbage = (PRIntn)verbosity;
+ return (Verbosity)(verbage += delta);
+} /* ChangeVerbosity */
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PLOptStatus os;
+ const char *test_name = NULL;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dqGc:o:p:t:w:");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 0:
+ test_name = opt->value;
+ break;
+ case 'd': /* debug mode */
+ if (verbosity < noisy)
+ verbosity = ChangeVerbosity(verbosity, 1);
+ break;
+ case 'q': /* debug mode */
+ if (verbosity > silent)
+ verbosity = ChangeVerbosity(verbosity, -1);
+ break;
+ case 'G': /* use global threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'c': /* number of client threads */
+ client_threads = atoi(opt->value);
+ break;
+ case 'o': /* operations to compelete */
+ ops_required = atoi(opt->value);
+ break;
+ case 'p': /* default port */
+ default_port = atoi(opt->value);
+ break;
+ case 't': /* number of threads waiting */
+ worker_threads = atoi(opt->value);
+ break;
+ case 'w': /* number of wait objects */
+ wait_objects = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (verbosity > 0)
+ debug = PR_GetSpecialFD(PR_StandardError);
+
+ RunThisOne(OneInThenCancelled, "OneInThenCancelled", test_name);
+ RunThisOne(OneOpOneThread, "OneOpOneThread", test_name);
+ RunThisOne(ManyOpOneThread, "ManyOpOneThread", test_name);
+ RunThisOne(SomeOpsSomeThreads, "SomeOpsSomeThreads", test_name);
+ RunThisOne(RealOneGroupIO, "RealOneGroupIO", test_name);
+ return 0;
+} /* main */
+
+/* multwait.c */
diff --git a/pr/tests/nbconn.c b/pr/tests/nbconn.c
new file mode 100644
index 00000000..885ba3b6
--- /dev/null
+++ b/pr/tests/nbconn.c
@@ -0,0 +1,572 @@
+/* -*- 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.
+ */
+
+/*
+ * A test for nonblocking connect. Functions tested include PR_Connect,
+ * PR_Poll, and PR_GetConnectStatus.
+ *
+ * The test should be invoked with a host name, for example:
+ * nbconn www.netscape.com
+ * It will do a nonblocking connect to port 80 (HTTP) on that host,
+ * and when connected, issue the "GET /" HTTP command.
+ *
+ * You should run this test in three ways:
+ * 1. To a known web site, such as www.netscape.com. The HTML of the
+ * top-level page at the web site should be printed.
+ * 2. To a machine not running a web server at port 80. This test should
+ * fail. Ideally the error code should be PR_CONNECT_REFUSED_ERROR.
+ * But it is possible to return PR_UNKNOWN_ERROR on certain platforms.
+ * 3. To an unreachable machine, for example, a machine that is off line.
+ * The test should fail after the connect times out. Ideally the
+ * error code should be PR_IO_TIMEOUT_ERROR, but it is possible to
+ * return PR_UNKNOWN_ERROR on certain platforms.
+ */
+
+#include "nspr.h"
+#include "plgetopt.h"
+#include <stdio.h>
+
+#ifdef XP_MAC
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+static char *hosts[4] = {"cynic", "warp", "gandalf", "neon"};
+#endif
+
+#define SERVER_MAX_BIND_COUNT 100
+#define DATA_BUF_SIZE 256
+#define TCP_SERVER_PORT 10000
+#define TCP_UNUSED_PORT 211
+
+typedef struct Server_Param {
+ PRFileDesc *sp_fd; /* server port */
+} Server_Param;
+static void PR_CALLBACK TCP_Server(void *arg);
+
+int _debug_on;
+#define DPRINTF(arg) if (_debug_on) printf arg
+
+static PRIntn connection_success_test();
+static PRIntn connection_failure_test();
+
+int main(int argc, char **argv)
+{
+ PRHostEnt he;
+ char buf[1024];
+ PRNetAddr addr;
+ PRPollDesc pd;
+ PRStatus rv;
+ PRSocketOptionData optData;
+ const char *hostname = NULL;
+ PRIntn default_case, n, bytes_read, bytes_sent;
+ PRInt32 failed_already = 0;
+#ifdef XP_MAC
+ int index;
+ PRIntervalTime timeout;
+#endif
+
+ /*
+ * -d debug mode
+ */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 0: /* debug mode */
+ hostname = opt->value;
+ break;
+ case 'd': /* debug mode */
+ _debug_on = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("nbconn.log");
+ for (index=0; index<4; index++) {
+ argv[1] = hosts[index];
+ timeout = PR_INTERVAL_NO_TIMEOUT;
+ if (index == 3)
+ timeout = PR_SecondsToInterval(10UL);
+#endif
+
+
+ PR_STDIO_INIT();
+#ifndef XP_MAC
+ if (hostname)
+ default_case = 0;
+ else
+ default_case = 1;
+#endif
+
+ if (default_case) {
+
+ /*
+ * In the default case the following tests are executed:
+ * 1. successful connection: a server thread accepts a connection
+ * from the main thread
+ * 2. unsuccessful connection: the main thread tries to connect to a
+ * non-existent port and expects to get an error
+ */
+ rv = connection_success_test();
+ if (rv == 0)
+ rv = connection_failure_test();
+ return rv;
+ } else {
+ PRFileDesc *sock;
+
+ if (PR_GetHostByName(argv[1], buf, sizeof(buf), &he) == PR_FAILURE) {
+ printf( "Unknown host: %s\n", argv[1]);
+ exit(1);
+ } else {
+ printf( "host: %s\n", buf);
+ }
+ PR_EnumerateHostEnt(0, &he, 80, &addr);
+
+ sock = PR_NewTCPSocket();
+ optData.option = PR_SockOpt_Nonblocking;
+ optData.value.non_blocking = PR_TRUE;
+ PR_SetSocketOption(sock, &optData);
+ rv = PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT);
+ if (rv == PR_FAILURE && PR_GetError() == PR_IN_PROGRESS_ERROR) {
+ printf( "Connect in progress\n");
+ }
+
+ pd.fd = sock;
+ pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
+#ifndef XP_MAC
+ n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+#else
+ n = PR_Poll(&pd, 1, timeout);
+#endif
+ if (n == -1) {
+ printf( "PR_Poll failed\n");
+ exit(1);
+ }
+ printf( "PR_Poll returns %d\n", n);
+ if (pd.out_flags & PR_POLL_READ) {
+ printf( "PR_POLL_READ\n");
+ }
+ if (pd.out_flags & PR_POLL_WRITE) {
+ printf( "PR_POLL_WRITE\n");
+ }
+ if (pd.out_flags & PR_POLL_EXCEPT) {
+ printf( "PR_POLL_EXCEPT\n");
+ }
+ if (pd.out_flags & PR_POLL_ERR) {
+ printf( "PR_POLL_ERR\n");
+ }
+ if (pd.out_flags & PR_POLL_NVAL) {
+ printf( "PR_POLL_NVAL\n");
+ }
+
+ if (PR_GetConnectStatus(&pd) == PR_SUCCESS) {
+ printf("PR_GetConnectStatus: connect succeeded\n");
+ /* Mac and Win16 have trouble printing to the console. */
+#if !defined(XP_MAC) && !defined(WIN16)
+ PR_Write(sock, "GET /\r\n\r\n", 9);
+ PR_Shutdown(sock, PR_SHUTDOWN_SEND);
+ pd.in_flags = PR_POLL_READ;
+ while (1) {
+ n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+ printf( "poll returns %d\n", n);
+ n = PR_Read(sock, buf, sizeof(buf));
+ printf( "read returns %d\n", n);
+ if (n <= 0) {
+ break;
+ }
+ PR_Write(PR_STDOUT, buf, n);
+ }
+#endif
+ } else {
+ if (PR_GetError() == PR_IN_PROGRESS_ERROR) {
+ printf( "PR_GetConnectStatus: connect still in progress\n");
+ exit(1);
+ }
+ printf( "PR_GetConnectStatus: connect failed: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ }
+ PR_Close(sock);
+#ifdef XP_MAC
+ } /* end of for loop */
+#endif
+ printf( "PASS\n");
+ return 0;
+
+ }
+}
+
+
+/*
+ * TCP Server
+ * Server Thread
+ * Accept a connection from the client and write some data
+ */
+static void PR_CALLBACK
+TCP_Server(void *arg)
+{
+ Server_Param *sp = (Server_Param *) arg;
+ PRFileDesc *sockfd, *newsockfd;
+ char data_buf[DATA_BUF_SIZE];
+ PRIntn rv, bytes_read;
+
+ sockfd = sp->sp_fd;
+ if ((newsockfd = PR_Accept(sockfd, NULL,
+ PR_INTERVAL_NO_TIMEOUT)) == NULL) {
+ fprintf(stderr,"ERROR - PR_Accept failed: (%d,%d)\n",
+ PR_GetError(), PR_GetOSError());
+ return;
+ }
+ bytes_read = 0;
+ while (bytes_read != DATA_BUF_SIZE) {
+ rv = PR_Read(newsockfd, data_buf + bytes_read ,
+ DATA_BUF_SIZE - bytes_read);
+ if (rv < 0) {
+ fprintf(stderr,"Error - PR_Read failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ PR_Close(newsockfd);
+ return;
+ }
+ PR_ASSERT(rv != 0);
+ bytes_read += rv;
+ }
+ DPRINTF(("Bytes read from client - %d\n",bytes_read));
+ rv = PR_Write(newsockfd, data_buf,DATA_BUF_SIZE);
+ if (rv < 0) {
+ fprintf(stderr,"Error - PR_Write failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ PR_Close(newsockfd);
+ return;
+ }
+ PR_ASSERT(rv == DATA_BUF_SIZE);
+ DPRINTF(("Bytes written to client - %d\n",rv));
+ PR_Close(newsockfd);
+}
+
+
+/*
+ * test for successful connection using a non-blocking socket
+ */
+static PRIntn
+connection_success_test()
+{
+ PRFileDesc *sockfd = NULL, *conn_fd = NULL;
+ PRNetAddr netaddr;
+ PRInt32 i, rv;
+ PRPollDesc pd;
+ PRSocketOptionData optData;
+ PRThread *thr = NULL;
+ Server_Param sp;
+ char send_buf[DATA_BUF_SIZE], recv_buf[DATA_BUF_SIZE];
+ PRIntn default_case, n, bytes_read, bytes_sent;
+ PRIntn failed_already = 0;
+
+ /*
+ * Create a tcp socket
+ */
+ if ((sockfd = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr,"Error - PR_NewTCPSocket failed\n");
+ failed_already=1;
+ goto def_exit;
+ }
+ memset(&netaddr, 0 , sizeof(netaddr));
+ netaddr.inet.family = PR_AF_INET;
+ netaddr.inet.port = PR_htons(TCP_SERVER_PORT);
+ netaddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ /*
+ * try a few times to bind server's address, if addresses are in
+ * use
+ */
+ i = 0;
+ while (PR_Bind(sockfd, &netaddr) < 0) {
+ if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) {
+ netaddr.inet.port += 2;
+ if (i++ < SERVER_MAX_BIND_COUNT)
+ continue;
+ }
+ fprintf(stderr,"ERROR - PR_Bind failed: (%d,%d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+
+ if (PR_Listen(sockfd, 32) < 0) {
+ fprintf(stderr,"ERROR - PR_Listen failed: (%d,%d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+
+ if (PR_GetSockName(sockfd, &netaddr) < 0) {
+ fprintf(stderr,"ERROR - PR_GetSockName failed: (%d,%d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+ if ((conn_fd = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr,"Error - PR_NewTCPSocket failed\n");
+ failed_already=1;
+ goto def_exit;
+ }
+ optData.option = PR_SockOpt_Nonblocking;
+ optData.value.non_blocking = PR_TRUE;
+ PR_SetSocketOption(conn_fd, &optData);
+ rv = PR_Connect(conn_fd, &netaddr, PR_INTERVAL_NO_TIMEOUT);
+ if (rv == PR_FAILURE) {
+ if (PR_GetError() == PR_IN_PROGRESS_ERROR) {
+ DPRINTF(("Connect in progress\n"));
+ } else {
+ fprintf(stderr,"Error - PR_Connect failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+ }
+ /*
+ * Now create a thread to accept a connection
+ */
+ sp.sp_fd = sockfd;
+ thr = PR_CreateThread(PR_USER_THREAD, TCP_Server, (void *)&sp,
+ PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ if (thr == NULL) {
+ fprintf(stderr,"Error - PR_CreateThread failed: (%d,%d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+ DPRINTF(("Created TCP_Server thread [0x%x]\n",thr));
+ pd.fd = conn_fd;
+ pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
+#ifndef XP_MAC
+ n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+#else
+ n = PR_Poll(&pd, 1, timeout);
+#endif
+ if (n == -1) {
+ fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+ if (PR_GetConnectStatus(&pd) == PR_SUCCESS) {
+ PRInt32 rv;
+
+ DPRINTF(("Connection successful\n"));
+
+ /*
+ * Write some data, read it back and check data integrity to
+ * make sure the connection is good
+ */
+ pd.in_flags = PR_POLL_WRITE;
+ bytes_sent = 0;
+ memset(send_buf, 'a', DATA_BUF_SIZE);
+ while (bytes_sent != DATA_BUF_SIZE) {
+ rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+ if (rv < 0) {
+ fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+ PR_ASSERT((rv == 1) && (pd.out_flags == PR_POLL_WRITE));
+ rv = PR_Write(conn_fd, send_buf + bytes_sent,
+ DATA_BUF_SIZE - bytes_sent);
+ if (rv < 0) {
+ fprintf(stderr,"Error - PR_Write failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+ PR_ASSERT(rv > 0);
+ bytes_sent += rv;
+ }
+ DPRINTF(("Bytes written to server - %d\n",bytes_sent));
+ PR_Shutdown(conn_fd, PR_SHUTDOWN_SEND);
+ pd.in_flags = PR_POLL_READ;
+ bytes_read = 0;
+ memset(recv_buf, 0, DATA_BUF_SIZE);
+ while (bytes_read != DATA_BUF_SIZE) {
+ rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+ if (rv < 0) {
+ fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+ PR_ASSERT((rv == 1) && (pd.out_flags == PR_POLL_READ));
+ rv = PR_Read(conn_fd, recv_buf + bytes_read ,
+ DATA_BUF_SIZE - bytes_read);
+ if (rv < 0) {
+ fprintf(stderr,"Error - PR_Read failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+ PR_ASSERT(rv != 0);
+ bytes_read += rv;
+ }
+ DPRINTF(("Bytes read from server - %d\n",bytes_read));
+ /*
+ * verify the data read
+ */
+ if (memcmp(send_buf, recv_buf, DATA_BUF_SIZE) != 0) {
+ fprintf(stderr,"ERROR - data corruption\n");
+ failed_already=1;
+ goto def_exit;
+ }
+ DPRINTF(("Data integrity verified\n"));
+ } else {
+ fprintf(stderr,"PR_GetConnectStatus: connect failed: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already = 1;
+ goto def_exit;
+ }
+def_exit:
+ if (thr) {
+ PR_JoinThread(thr);
+ thr = NULL;
+ }
+ if (sockfd) {
+ PR_Close(sockfd);
+ sockfd = NULL;
+ }
+ if (conn_fd) {
+ PR_Close(conn_fd);
+ conn_fd = NULL;
+ }
+ if (failed_already)
+ return 1;
+ else
+ return 0;
+
+}
+
+/*
+ * test for connection to a non-existent port using a non-blocking socket
+ */
+static PRIntn
+connection_failure_test()
+{
+ PRFileDesc *sockfd = NULL, *conn_fd = NULL;
+ PRNetAddr netaddr;
+ PRInt32 i, rv;
+ PRPollDesc pd;
+ PRSocketOptionData optData;
+ PRIntn n, failed_already = 0;
+
+ /*
+ * Create a tcp socket
+ */
+ if ((sockfd = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr,"Error - PR_NewTCPSocket failed\n");
+ failed_already=1;
+ goto def_exit;
+ }
+ memset(&netaddr, 0 , sizeof(netaddr));
+ netaddr.inet.family = PR_AF_INET;
+ netaddr.inet.port = PR_htons(TCP_SERVER_PORT);
+ netaddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ /*
+ * try a few times to bind server's address, if addresses are in
+ * use
+ */
+ i = 0;
+ while (PR_Bind(sockfd, &netaddr) < 0) {
+ if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) {
+ netaddr.inet.port += 2;
+ if (i++ < SERVER_MAX_BIND_COUNT)
+ continue;
+ }
+ fprintf(stderr,"ERROR - PR_Bind failed: (%d,%d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+
+ if (PR_GetSockName(sockfd, &netaddr) < 0) {
+ fprintf(stderr,"ERROR - PR_GetSockName failed: (%d,%d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+#ifdef AIX
+ /*
+ * On AIX, set to unused/reserved port
+ */
+ netaddr.inet.port = PR_htons(TCP_UNUSED_PORT);
+#endif
+ if ((conn_fd = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr,"Error - PR_NewTCPSocket failed\n");
+ failed_already=1;
+ goto def_exit;
+ }
+ optData.option = PR_SockOpt_Nonblocking;
+ optData.value.non_blocking = PR_TRUE;
+ PR_SetSocketOption(conn_fd, &optData);
+ rv = PR_Connect(conn_fd, &netaddr, PR_INTERVAL_NO_TIMEOUT);
+ if (rv == PR_FAILURE) {
+ DPRINTF(("PR_Connect to a non-listen port failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError()));
+ } else {
+ PR_ASSERT(rv == PR_SUCCESS);
+ fprintf(stderr,"Error - PR_Connect succeeded, expected to fail\n");
+ failed_already=1;
+ goto def_exit;
+ }
+ pd.fd = conn_fd;
+ pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
+#ifndef XP_MAC
+ n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+#else
+ n = PR_Poll(&pd, 1, timeout);
+#endif
+ if (n == -1) {
+ fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ goto def_exit;
+ }
+ if (PR_GetConnectStatus(&pd) == PR_SUCCESS) {
+ PRInt32 rv;
+ fprintf(stderr,"PR_GetConnectStatus succeeded, expected to fail\n");
+ failed_already = 1;
+ goto def_exit;
+ }
+ rv = PR_GetError();
+ DPRINTF(("Connection failed, successfully with PR_Error %d\n",rv));
+def_exit:
+ if (sockfd) {
+ PR_Close(sockfd);
+ sockfd = NULL;
+ }
+ if (conn_fd) {
+ PR_Close(conn_fd);
+ conn_fd = NULL;
+ }
+ if (failed_already)
+ return 1;
+ else
+ return 0;
+
+}
diff --git a/pr/tests/nblayer.c b/pr/tests/nblayer.c
new file mode 100644
index 00000000..5b304d2c
--- /dev/null
+++ b/pr/tests/nblayer.c
@@ -0,0 +1,635 @@
+/* -*- 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 "prio.h"
+#include "prmem.h"
+#include "prprf.h"
+#include "prlog.h"
+#include "prerror.h"
+#include "prnetdb.h"
+#include "prthread.h"
+
+#include "plerror.h"
+#include "plgetopt.h"
+#include "prwin16.h"
+
+#include <stdlib.h>
+
+/*
+** Testing layering of I/O
+**
+** The layered server
+** A thread that acts as a server. It creates a TCP listener with a dummy
+** layer pushed on top. Then listens for incoming connections. Each connection
+** request for connection will be layered as well, accept one request, echo
+** it back and close.
+**
+** The layered client
+** Pretty much what you'd expect.
+*/
+
+static PRFileDesc *logFile;
+static PRDescIdentity identity;
+static PRNetAddr server_address;
+
+static PRIOMethods myMethods;
+
+typedef enum {rcv_get_debit, rcv_send_credit, rcv_data} RcvState;
+typedef enum {xmt_send_debit, xmt_recv_credit, xmt_data} XmtState;
+
+struct PRFilePrivate
+{
+ RcvState rcvstate;
+ XmtState xmtstate;
+ PRInt32 rcvreq, rcvinprogress;
+ PRInt32 xmtreq, xmtinprogress;
+};
+
+typedef enum Verbosity {silent, quiet, chatty, noisy} Verbosity;
+
+static PRIntn minor_iterations = 5;
+static PRIntn major_iterations = 1;
+static Verbosity verbosity = quiet;
+static PRUint16 default_port = 12273;
+
+static PRFileDesc *PushLayer(PRFileDesc *stack)
+{
+ PRStatus rv;
+ PRFileDesc *layer = PR_CreateIOLayerStub(identity, &myMethods);
+ layer->secret = PR_NEWZAP(PRFilePrivate);
+ rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer);
+ PR_ASSERT(PR_SUCCESS == rv);
+ if (verbosity > quiet)
+ PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer, stack);
+ return stack;
+} /* PushLayer */
+
+static PRFileDesc *PopLayer(PRFileDesc *stack)
+{
+ PRFileDesc *popped = PR_PopIOLayer(stack, identity);
+ if (verbosity > quiet)
+ PR_fprintf(logFile, "Popped layer(0x%x) from stack(0x%x)\n", popped, stack);
+ PR_DELETE(popped->secret);
+ popped->dtor(popped);
+ return stack;
+} /* PopLayer */
+
+static void PR_CALLBACK Client(void *arg)
+{
+ PRStatus rv;
+ PRIntn mits;
+ PRInt32 ready;
+ PRUint8 buffer[100];
+ PRPollDesc polldesc;
+ PRIntn empty_flags = 0;
+ PRIntn bytes_read, bytes_sent;
+ PRFileDesc *stack = (PRFileDesc*)arg;
+
+ /* Initialize the buffer so that Purify won't complain */
+ memset(buffer, 0, sizeof(buffer));
+
+ rv = PR_Connect(stack, &server_address, PR_INTERVAL_NO_TIMEOUT);
+ if ((PR_FAILURE == rv) && (PR_IN_PROGRESS_ERROR == PR_GetError()))
+ {
+ if (verbosity > quiet)
+ PR_fprintf(logFile, "Client connect 'in progress'\n");
+ do
+ {
+ polldesc.fd = stack;
+ polldesc.out_flags = 0;
+ polldesc.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
+ ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT);
+ if ((1 != ready) /* if not 1, then we're dead */
+ || (0 == (polldesc.in_flags & polldesc.out_flags)))
+ { PR_ASSERT(!"Whoa!"); break; }
+ if (verbosity > quiet)
+ PR_fprintf(
+ logFile, "Client connect 'in progress' [0x%x]\n",
+ polldesc.out_flags);
+ rv = PR_GetConnectStatus(&polldesc);
+ if ((PR_FAILURE == rv)
+ && (PR_IN_PROGRESS_ERROR != PR_GetError())) break;
+ } while (PR_FAILURE == rv);
+ }
+ PR_ASSERT(PR_SUCCESS == rv);
+ if (verbosity > chatty)
+ PR_fprintf(logFile, "Client created connection\n");
+
+ for (mits = 0; mits < minor_iterations; ++mits)
+ {
+ bytes_sent = 0;
+ if (verbosity > quiet)
+ PR_fprintf(logFile, "Client sending %d bytes\n", sizeof(buffer));
+ do
+ {
+ if (verbosity > chatty)
+ PR_fprintf(
+ logFile, "Client sending %d bytes\n",
+ sizeof(buffer) - bytes_sent);
+ ready = PR_Send(
+ stack, buffer + bytes_sent, sizeof(buffer) - bytes_sent,
+ empty_flags, PR_INTERVAL_NO_TIMEOUT);
+ if (verbosity > chatty)
+ PR_fprintf(logFile, "Client send status [%d]\n", ready);
+ if (0 < ready) bytes_sent += ready;
+ else if ((-1 == ready) && (PR_WOULD_BLOCK_ERROR == PR_GetError()))
+ {
+ polldesc.fd = stack;
+ polldesc.out_flags = 0;
+ polldesc.in_flags = PR_POLL_WRITE;
+ ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT);
+ if ((1 != ready) /* if not 1, then we're dead */
+ || (0 == (polldesc.in_flags & polldesc.out_flags)))
+ { PR_ASSERT(!"Whoa!"); break; }
+ }
+ else break;
+ } while (bytes_sent < sizeof(buffer));
+ PR_ASSERT(sizeof(buffer) == bytes_sent);
+
+ bytes_read = 0;
+ do
+ {
+ if (verbosity > chatty)
+ PR_fprintf(
+ logFile, "Client receiving %d bytes\n",
+ bytes_sent - bytes_read);
+ ready = PR_Recv(
+ stack, buffer + bytes_read, bytes_sent - bytes_read,
+ empty_flags, PR_INTERVAL_NO_TIMEOUT);
+ if (verbosity > chatty)
+ PR_fprintf(
+ logFile, "Client receive status [%d]\n", ready);
+ if (0 < ready) bytes_read += ready;
+ else if ((-1 == ready) && (PR_WOULD_BLOCK_ERROR == PR_GetError()))
+ {
+ polldesc.fd = stack;
+ polldesc.out_flags = 0;
+ polldesc.in_flags = PR_POLL_READ;
+ ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT);
+ if ((1 != ready) /* if not 1, then we're dead */
+ || (0 == (polldesc.in_flags & polldesc.out_flags)))
+ { PR_ASSERT(!"Whoa!"); break; }
+ }
+ else break;
+ } while (bytes_read < bytes_sent);
+ if (verbosity > chatty)
+ PR_fprintf(logFile, "Client received %d bytes\n", bytes_read);
+ PR_ASSERT(bytes_read == bytes_sent);
+ }
+
+ if (verbosity > quiet)
+ PR_fprintf(logFile, "Client shutting down stack\n");
+
+ rv = PR_Shutdown(stack, PR_SHUTDOWN_BOTH); PR_ASSERT(PR_SUCCESS == rv);
+} /* Client */
+
+static void PR_CALLBACK Server(void *arg)
+{
+ PRStatus rv;
+ PRInt32 ready;
+ PRUint8 buffer[100];
+ PRFileDesc *service;
+ PRUintn empty_flags = 0;
+ struct PRPollDesc polldesc;
+ PRIntn bytes_read, bytes_sent;
+ PRFileDesc *stack = (PRFileDesc*)arg;
+ PRNetAddr any_address, client_address;
+
+ rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ rv = PR_Bind(stack, &any_address); PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Listen(stack, 10); PR_ASSERT(PR_SUCCESS == rv);
+
+ do
+ {
+ if (verbosity > chatty)
+ PR_fprintf(logFile, "Server accepting connection\n");
+ service = PR_Accept(stack, &client_address, PR_INTERVAL_NO_TIMEOUT);
+ if (verbosity > chatty)
+ PR_fprintf(logFile, "Server accept status [0x%p]\n", service);
+ if ((NULL == service) && (PR_WOULD_BLOCK_ERROR == PR_GetError()))
+ {
+ polldesc.fd = stack;
+ polldesc.out_flags = 0;
+ polldesc.in_flags = PR_POLL_READ | PR_POLL_EXCEPT;
+ ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT);
+ if ((1 != ready) /* if not 1, then we're dead */
+ || (0 == (polldesc.in_flags & polldesc.out_flags)))
+ { PR_ASSERT(!"Whoa!"); break; }
+ }
+ } while (NULL == service);
+ PR_ASSERT(NULL != service);
+
+ if (verbosity > quiet)
+ PR_fprintf(logFile, "Server accepting connection\n");
+
+ do
+ {
+ bytes_read = 0;
+ do
+ {
+ if (verbosity > chatty)
+ PR_fprintf(
+ logFile, "Server receiving %d bytes\n",
+ sizeof(buffer) - bytes_read);
+ ready = PR_Recv(
+ service, buffer + bytes_read, sizeof(buffer) - bytes_read,
+ empty_flags, PR_INTERVAL_NO_TIMEOUT);
+ if (verbosity > chatty)
+ PR_fprintf(logFile, "Server receive status [%d]\n", ready);
+ if (0 < ready) bytes_read += ready;
+ else if ((-1 == ready) && (PR_WOULD_BLOCK_ERROR == PR_GetError()))
+ {
+ polldesc.fd = service;
+ polldesc.out_flags = 0;
+ polldesc.in_flags = PR_POLL_READ;
+ ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT);
+ if ((1 != ready) /* if not 1, then we're dead */
+ || (0 == (polldesc.in_flags & polldesc.out_flags)))
+ { PR_ASSERT(!"Whoa!"); break; }
+ }
+ else break;
+ } while (bytes_read < sizeof(buffer));
+
+ if (0 != bytes_read)
+ {
+ if (verbosity > chatty)
+ PR_fprintf(logFile, "Server received %d bytes\n", bytes_read);
+ PR_ASSERT(bytes_read > 0);
+
+ bytes_sent = 0;
+ do
+ {
+ ready = PR_Send(
+ service, buffer + bytes_sent, bytes_read - bytes_sent,
+ empty_flags, PR_INTERVAL_NO_TIMEOUT);
+ if (0 < ready)
+ {
+ bytes_sent += ready;
+ }
+ else if ((-1 == ready) && (PR_WOULD_BLOCK_ERROR == PR_GetError()))
+ {
+ polldesc.fd = service;
+ polldesc.out_flags = 0;
+ polldesc.in_flags = PR_POLL_WRITE;
+ ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT);
+ if ((1 != ready) /* if not 1, then we're dead */
+ || (0 == (polldesc.in_flags & polldesc.out_flags)))
+ { PR_ASSERT(!"Whoa!"); break; }
+ }
+ else break;
+ } while (bytes_sent < bytes_read);
+ PR_ASSERT(bytes_read == bytes_sent);
+ if (verbosity > chatty)
+ PR_fprintf(logFile, "Server sent %d bytes\n", bytes_sent);
+ }
+ } while (0 != bytes_read);
+
+ if (verbosity > quiet)
+ PR_fprintf(logFile, "Server shutting down stack\n");
+ rv = PR_Shutdown(service, PR_SHUTDOWN_BOTH); PR_ASSERT(PR_SUCCESS == rv);
+
+} /* Server */
+
+static PRStatus PR_CALLBACK MyClose(PRFileDesc *fd)
+{
+ PR_DELETE(fd->secret); /* manage my secret file object */
+ return (PR_GetDefaultIOMethods())->close(fd); /* let him do all the work */
+} /* MyClose */
+
+static PRInt16 PR_CALLBACK MyPoll(
+ PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
+{
+ PRInt16 my_flags, new_flags;
+ PRFilePrivate *mine = (PRFilePrivate*)fd->secret;
+ if (0 != (PR_POLL_READ & in_flags))
+ {
+ /* client thinks he's reading */
+ switch (mine->rcvstate)
+ {
+ case rcv_send_credit:
+ my_flags = (in_flags & ~PR_POLL_READ) | PR_POLL_WRITE;
+ break;
+ case rcv_data:
+ case rcv_get_debit:
+ my_flags = in_flags;
+ default: break;
+ }
+ }
+ else if (0 != (PR_POLL_WRITE & in_flags))
+ {
+ /* client thinks he's writing */
+ switch (mine->xmtstate)
+ {
+ case xmt_recv_credit:
+ my_flags = (in_flags & ~PR_POLL_WRITE) | PR_POLL_READ;
+ break;
+ case xmt_send_debit:
+ case xmt_data:
+ my_flags = in_flags;
+ default: break;
+ }
+ }
+ else PR_ASSERT(!"How'd I get here?");
+ new_flags = (fd->lower->methods->poll)(fd->lower, my_flags, out_flags);
+ if (verbosity > chatty)
+ PR_fprintf(
+ logFile, "Poll [i: 0x%x, m: 0x%x, o: 0x%x, n: 0x%x]\n",
+ in_flags, my_flags, *out_flags, new_flags);
+ return new_flags;
+} /* MyPoll */
+
+static PRInt32 PR_CALLBACK MyRecv(
+ PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ char *b;
+ PRInt32 rv;
+ PRFileDesc *lo = fd->lower;
+ PRFilePrivate *mine = (PRFilePrivate*)fd->secret;
+
+ do
+ {
+ switch (mine->rcvstate)
+ {
+ case rcv_get_debit:
+ b = (char*)&mine->rcvreq;
+ mine->rcvreq = amount;
+ rv = lo->methods->recv(
+ lo, b + mine->rcvinprogress,
+ sizeof(mine->rcvreq) - mine->rcvinprogress, flags, timeout);
+ if (0 == rv) goto closed;
+ if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) break;
+ mine->rcvinprogress += rv; /* accumulate the read */
+ if (mine->rcvinprogress < sizeof(mine->rcvreq)) break; /* loop */
+ mine->rcvstate = rcv_send_credit;
+ mine->rcvinprogress = 0;
+ case rcv_send_credit:
+ b = (char*)&mine->rcvreq;
+ rv = lo->methods->send(
+ lo, b + mine->rcvinprogress,
+ sizeof(mine->rcvreq) - mine->rcvinprogress, flags, timeout);
+ if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) break;
+ mine->rcvinprogress += rv; /* accumulate the read */
+ if (mine->rcvinprogress < sizeof(mine->rcvreq)) break; /* loop */
+ mine->rcvstate = rcv_data;
+ mine->rcvinprogress = 0;
+ case rcv_data:
+ b = (char*)buf;
+ rv = lo->methods->recv(
+ lo, b + mine->rcvinprogress,
+ mine->rcvreq - mine->rcvinprogress, flags, timeout);
+ if (0 == rv) goto closed;
+ if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) break;
+ mine->rcvinprogress += rv; /* accumulate the read */
+ if (mine->rcvinprogress < amount) break; /* loop */
+ mine->rcvstate = rcv_get_debit;
+ mine->rcvinprogress = 0;
+ return mine->rcvreq; /* << -- that's it! */
+ default:
+ break;
+ }
+ } while (-1 != rv);
+ return rv;
+closed:
+ mine->rcvinprogress = 0;
+ mine->rcvstate = rcv_get_debit;
+ return 0;
+} /* MyRecv */
+
+static PRInt32 PR_CALLBACK MySend(
+ PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ char *b;
+ PRInt32 rv;
+ PRFileDesc *lo = fd->lower;
+ PRFilePrivate *mine = (PRFilePrivate*)fd->secret;
+
+ do
+ {
+ switch (mine->xmtstate)
+ {
+ case xmt_send_debit:
+ b = (char*)&mine->xmtreq;
+ mine->xmtreq = amount;
+ rv = lo->methods->send(
+ lo, b - mine->xmtinprogress,
+ sizeof(mine->xmtreq) - mine->xmtinprogress, flags, timeout);
+ if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) break;
+ mine->xmtinprogress += rv;
+ if (mine->xmtinprogress < sizeof(mine->xmtreq)) break;
+ mine->xmtstate = xmt_recv_credit;
+ mine->xmtinprogress = 0;
+ case xmt_recv_credit:
+ b = (char*)&mine->xmtreq;
+ rv = lo->methods->recv(
+ lo, b + mine->xmtinprogress,
+ sizeof(mine->xmtreq) - mine->xmtinprogress, flags, timeout);
+ if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) break;
+ mine->xmtinprogress += rv;
+ if (mine->xmtinprogress < sizeof(mine->xmtreq)) break;
+ mine->xmtstate = xmt_data;
+ mine->xmtinprogress = 0;
+ case xmt_data:
+ b = (char*)buf;
+ rv = lo->methods->send(
+ lo, b + mine->xmtinprogress,
+ mine->xmtreq - mine->xmtinprogress, flags, timeout);
+ if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) break;
+ mine->xmtinprogress += rv;
+ if (mine->xmtinprogress < amount) break;
+ mine->xmtstate = xmt_send_debit;
+ mine->xmtinprogress = 0;
+ return mine->xmtreq; /* <<-- That's the one! */
+ default:
+ break;
+ }
+ } while (-1 != rv);
+ return rv;
+} /* MySend */
+
+static Verbosity ChangeVerbosity(Verbosity verbosity, PRIntn delta)
+{
+ PRIntn verbage = (PRIntn)verbosity + delta;
+ if (verbage < (PRIntn)silent) verbage = (PRIntn)silent;
+ else if (verbage > (PRIntn)noisy) verbage = (PRIntn)noisy;
+ return (Verbosity)verbage;
+} /* ChangeVerbosity */
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PRStatus rv;
+ PLOptStatus os;
+ PRFileDesc *client, *service;
+ const char *server_name = NULL;
+ const PRIOMethods *stubMethods;
+ PRThread *client_thread, *server_thread;
+ PRThreadScope thread_scope = PR_LOCAL_THREAD;
+ PRSocketOptionData socket_noblock, socket_nodelay;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dqGC:c:p:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 0:
+ server_name = opt->value;
+ break;
+ case 'd': /* debug mode */
+ if (verbosity < noisy)
+ verbosity = ChangeVerbosity(verbosity, 1);
+ break;
+ case 'q': /* debug mode */
+ if (verbosity > silent)
+ verbosity = ChangeVerbosity(verbosity, -1);
+ break;
+ case 'G': /* use global threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'C': /* number of threads waiting */
+ major_iterations = atoi(opt->value);
+ break;
+ case 'c': /* number of client threads */
+ minor_iterations = atoi(opt->value);
+ break;
+ case 'p': /* default port */
+ default_port = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ PR_STDIO_INIT();
+
+ logFile = PR_GetSpecialFD(PR_StandardError);
+ identity = PR_GetUniqueIdentity("Dummy");
+ stubMethods = PR_GetDefaultIOMethods();
+
+ /*
+ ** The protocol we're going to implement is one where in order to initiate
+ ** a send, the sender must first solicit permission. Therefore, every
+ ** send is really a send - receive - send sequence.
+ */
+ myMethods = *stubMethods; /* first get the entire batch */
+ myMethods.recv = MyRecv; /* then override the ones we care about */
+ myMethods.send = MySend; /* then override the ones we care about */
+ myMethods.close = MyClose; /* then override the ones we care about */
+ myMethods.poll = MyPoll; /* then override the ones we care about */
+
+ if (NULL == server_name)
+ rv = PR_InitializeNetAddr(
+ PR_IpAddrLoopback, default_port, &server_address);
+ else
+ {
+ rv = PR_StringToNetAddr(server_name, &server_address);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_InitializeNetAddr(
+ PR_IpAddrNull, default_port, &server_address);
+ }
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ socket_noblock.value.non_blocking = PR_TRUE;
+ socket_noblock.option = PR_SockOpt_Nonblocking;
+ socket_nodelay.value.no_delay = PR_TRUE;
+ socket_nodelay.option = PR_SockOpt_NoDelay;
+
+ /* one type w/o layering */
+
+ while (major_iterations-- > 0)
+ {
+ if (verbosity > silent)
+ PR_fprintf(logFile, "Beginning non-layered test\n");
+
+ client = PR_NewTCPSocket(); PR_ASSERT(NULL != client);
+ service = PR_NewTCPSocket(); PR_ASSERT(NULL != service);
+
+ rv = PR_SetSocketOption(client, &socket_noblock);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_SetSocketOption(service, &socket_noblock);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_SetSocketOption(client, &socket_nodelay);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_SetSocketOption(service, &socket_nodelay);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ server_thread = PR_CreateThread(
+ PR_USER_THREAD, Server, service,
+ PR_PRIORITY_HIGH, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ PR_ASSERT(NULL != server_thread);
+
+ client_thread = PR_CreateThread(
+ PR_USER_THREAD, Client, client,
+ PR_PRIORITY_NORMAL, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ PR_ASSERT(NULL != client_thread);
+
+ rv = PR_JoinThread(client_thread);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(server_thread);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ rv = PR_Close(client); PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv);
+ if (verbosity > silent)
+ PR_fprintf(logFile, "Ending non-layered test\n");
+
+ /* with layering */
+ if (verbosity > silent)
+ PR_fprintf(logFile, "Beginning layered test\n");
+ client = PR_NewTCPSocket(); PR_ASSERT(NULL != client);
+ service = PR_NewTCPSocket(); PR_ASSERT(NULL != service);
+
+ rv = PR_SetSocketOption(client, &socket_noblock);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_SetSocketOption(service, &socket_noblock);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_SetSocketOption(client, &socket_nodelay);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_SetSocketOption(service, &socket_nodelay);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ server_thread = PR_CreateThread(
+ PR_USER_THREAD, Server, PushLayer(service),
+ PR_PRIORITY_HIGH, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ PR_ASSERT(NULL != server_thread);
+
+ client_thread = PR_CreateThread(
+ PR_USER_THREAD, Client, PushLayer(client),
+ PR_PRIORITY_NORMAL, thread_scope,
+ PR_JOINABLE_THREAD, 16 * 1024);
+ PR_ASSERT(NULL != client_thread);
+
+ rv = PR_JoinThread(client_thread);
+ PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_JoinThread(server_thread);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ rv = PR_Close(PopLayer(client)); PR_ASSERT(PR_SUCCESS == rv);
+ rv = PR_Close(PopLayer(service)); PR_ASSERT(PR_SUCCESS == rv);
+ if (verbosity > silent)
+ PR_fprintf(logFile, "Ending layered test\n");
+ }
+ return 0;
+} /* main */
+
+/* nblayer.c */
diff --git a/pr/tests/nonblock.c b/pr/tests/nonblock.c
new file mode 100644
index 00000000..9c44135e
--- /dev/null
+++ b/pr/tests/nonblock.c
@@ -0,0 +1,251 @@
+/* -*- 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 "nspr.h"
+#include "prio.h"
+#include "prerror.h"
+#include "prlog.h"
+#include "prprf.h"
+#include "prnetdb.h"
+#include "plerror.h"
+#ifndef XP_MAC
+#include "obsolete/probslet.h"
+#else
+#include "probslet.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define NUMBER_ROUNDS 5
+
+#if defined(WIN16)
+/*
+** Make win16 unit_time interval 300 milliseconds, others get 100
+*/
+#define UNIT_TIME 200 /* unit time in milliseconds */
+#else
+#define UNIT_TIME 100 /* unit time in milliseconds */
+#endif
+#define CHUNK_SIZE 10
+#undef USE_PR_SELECT /* If defined, we use PR_Select.
+ * If not defined, use PR_Poll instead. */
+
+#if defined(USE_PR_SELECT)
+#include "pprio.h"
+#endif
+
+#ifdef XP_MAC
+int fprintf(FILE *stream, const char *fmt, ...)
+{
+PR_LogPrint(fmt);
+return 0;
+}
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+#endif
+
+static void PR_CALLBACK
+clientThreadFunc(void *arg)
+{
+ PRUintn port = (PRUintn)arg;
+ PRFileDesc *sock;
+ PRNetAddr addr;
+ char buf[CHUNK_SIZE];
+ int i;
+ PRIntervalTime unitTime = PR_MillisecondsToInterval(UNIT_TIME);
+ PRIntn optval = 1;
+ PRStatus retVal;
+ PRInt32 nBytes;
+
+ /* Initialize the buffer so that Purify won't complain */
+ memset(buf, 0, sizeof(buf));
+
+ addr.inet.family = PR_AF_INET;
+ addr.inet.port = PR_htons((PRUint16)port);
+ addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+ PR_snprintf(buf, sizeof(buf), "%hu", addr.inet.ip);
+
+ /* time 1 */
+ PR_Sleep(unitTime);
+ sock = PR_NewTCPSocket();
+ PR_SetSockOpt(sock, PR_SockOpt_Nonblocking, &optval, sizeof(optval));
+ retVal = PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT);
+ if (retVal == PR_FAILURE && PR_GetError() == PR_IN_PROGRESS_ERROR) {
+#if !defined(USE_PR_SELECT)
+ PRPollDesc pd;
+ PRInt32 n;
+ fprintf(stderr, "connect: EWOULDBLOCK, good\n");
+ pd.fd = sock;
+ pd.in_flags = PR_POLL_WRITE;
+ n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(n == 1);
+ PR_ASSERT(pd.out_flags == PR_POLL_WRITE);
+#else
+ PR_fd_set writeSet;
+ PRInt32 n;
+ fprintf(stderr, "connect: EWOULDBLOCK, good\n");
+ PR_FD_ZERO(&writeSet);
+ PR_FD_SET(sock, &writeSet);
+ n = PR_Select(0, NULL, &writeSet, NULL, PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(n == 1);
+ PR_ASSERT(PR_FD_ISSET(sock, &writeSet));
+#endif
+ }
+ printf("client connected\n");
+ fflush(stdout);
+
+ /* time 4, 7, 11, etc. */
+ for (i = 0; i < NUMBER_ROUNDS; i++) {
+ PR_Sleep(3 * unitTime);
+ nBytes = PR_Write(sock, buf, sizeof(buf));
+ if (nBytes == -1) {
+ if (PR_GetError() == PR_WOULD_BLOCK_ERROR) {
+ fprintf(stderr, "write: EWOULDBLOCK\n");
+ exit(1);
+ } else {
+ fprintf(stderr, "write: failed\n");
+ }
+ }
+ printf("client sent %d bytes\n", nBytes);
+ fflush(stdout);
+ }
+
+ PR_Close(sock);
+}
+
+static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv )
+{
+ PRFileDesc *listenSock, *sock;
+ PRUint16 listenPort;
+ PRNetAddr addr;
+ char buf[CHUNK_SIZE];
+ PRThread *clientThread;
+ PRInt32 retVal;
+ PRIntn optval = 1;
+ PRIntn i;
+ PRIntervalTime unitTime = PR_MillisecondsToInterval(UNIT_TIME);
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("nonblock.log");
+#endif
+
+ /* Create a listening socket */
+ if ((listenSock = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ exit(1);
+ }
+ addr.inet.family = PR_AF_INET;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ exit(1);
+ }
+ if (PR_GetSockName(listenSock, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ exit(1);
+ }
+ listenPort = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ exit(1);
+ }
+
+ PR_snprintf(buf, sizeof(buf),
+ "The server thread is listening on port %hu\n\n",
+ listenPort);
+ printf("%s", buf);
+
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ clientThreadFunc, (void *) listenPort,
+ PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+ if (clientThread == NULL) {
+ fprintf(stderr, "can't create thread\n");
+ exit(1);
+ }
+
+ printf("client thread created.\n");
+
+ PR_SetSockOpt(listenSock, PR_SockOpt_Nonblocking, &optval,
+ sizeof(PRIntn));
+ /* time 0 */
+ sock = PR_Accept(listenSock, NULL, PR_INTERVAL_NO_TIMEOUT);
+ if (sock != NULL || PR_GetError() != PR_WOULD_BLOCK_ERROR) {
+ PL_PrintError("First Accept\n");
+ fprintf(stderr, "First PR_Accept() xxx\n" );
+ exit(1);
+ }
+ printf("accept: EWOULDBLOCK, good\n");
+ fflush(stdout);
+ /* time 2 */
+ PR_Sleep(2 * unitTime);
+ sock = PR_Accept(listenSock, NULL, PR_INTERVAL_NO_TIMEOUT);
+ if (sock == NULL) {
+ PL_PrintError("Second Accept\n");
+ fprintf(stderr, "Second PR_Accept() failed: (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ printf("accept: succeeded, good\n");
+ fflush(stdout);
+ PR_Close(listenSock);
+
+ PR_SetSockOpt(sock, PR_SockOpt_Nonblocking, &optval, sizeof(PRIntn));
+
+ /* time 3, 5, 6, 8, etc. */
+ for (i = 0; i < NUMBER_ROUNDS; i++) {
+ PR_Sleep(unitTime);
+ retVal = PR_Recv(sock, buf, sizeof(buf), 0, PR_INTERVAL_NO_TIMEOUT);
+ if (retVal != -1 || PR_GetError() != PR_WOULD_BLOCK_ERROR) {
+ PL_PrintError("First Receive:\n");
+ fprintf(stderr, "First PR_Recv: retVal: %ld, Error: %ld\n",
+ retVal, PR_GetError());
+ exit(1);
+ }
+ printf("read: EWOULDBLOCK, good\n");
+ fflush(stdout);
+ PR_Sleep(2 * unitTime);
+ retVal = PR_Recv(sock, buf, sizeof(buf), 0, PR_INTERVAL_NO_TIMEOUT);
+ if (retVal != CHUNK_SIZE) {
+ PL_PrintError("Second Receive:\n");
+ fprintf(stderr, "Second PR_Recv: retVal: %ld, Error: %ld\n",
+ retVal, PR_GetError());
+ exit(1);
+ }
+ printf("read: %d bytes, good\n", retVal);
+ fflush(stdout);
+ }
+ PR_Close(sock);
+
+ printf("All tests finished\n");
+ printf("PASS\n");
+ return 0;
+}
+
+PRIntn main(PRIntn argc, char *argv[])
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
+
diff --git a/pr/tests/op_2long.c b/pr/tests/op_2long.c
new file mode 100644
index 00000000..81ef29a2
--- /dev/null
+++ b/pr/tests/op_2long.c
@@ -0,0 +1,96 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+**
+** Name: op_2long.c
+**
+** Description: Test Program to verify the PR_NAME_TOO_LONG_ERROR
+**
+** Modification History:
+** 03-June-97 AGarcia- Initial version
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "prinit.h"
+#include "prmem.h"
+#include "prio.h"
+#include "prerror.h"
+#include <stdio.h>
+#include "plerror.h"
+#include "plgetopt.h"
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+#else
+#endif
+
+static PRFileDesc *t1;
+PRIntn error_code;
+
+/*
+ * 4096 should exceed any system's maximum file name length
+ */
+#define TOO_LONG 4096
+
+int main(int argc, char **argv)
+{
+ char nameTooLong[TOO_LONG];
+ int i;
+
+ /* Generate a really long pathname */
+ for (i = 0; i < TOO_LONG - 1; i++) {
+ if (i % 10 == 0) {
+ nameTooLong[i] = '/';
+ } else {
+ nameTooLong[i] = 'a';
+ }
+ }
+ nameTooLong[TOO_LONG - 1] = 0;
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("pr_open_re.log");
+#endif
+
+ PR_STDIO_INIT();
+ t1 = PR_Open(nameTooLong, PR_RDWR, 0666);
+ if (t1 == NULL) {
+ if (PR_GetError() == PR_NAME_TOO_LONG_ERROR) {
+ PL_PrintError("error code is");
+ printf ("PASS\n");
+ return 0;
+ }
+ else {
+ PL_PrintError("error code is");
+ printf ("FAIL\n");
+ return 1;
+ }
+ }
+
+ else {
+ printf ("Test passed\n");
+ return 0;
+ }
+
+
+
+}
diff --git a/pr/tests/op_filnf.c b/pr/tests/op_filnf.c
new file mode 100644
index 00000000..860bb5f5
--- /dev/null
+++ b/pr/tests/op_filnf.c
@@ -0,0 +1,72 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+**
+** Name: op_filnf.c
+**
+** Description: Test Program to verify the PR_FILE_NOT_FOUND_ERROR
+** This test program also uses the TRUNCATE option
+**
+** Modification History:
+** 03-June-97 AGarcia- Initial version
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "prinit.h"
+#include "prmem.h"
+#include "prio.h"
+#include "prerror.h"
+#include <stdio.h>
+#include "plgetopt.h"
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+#else
+#endif
+
+static PRFileDesc *t1;
+PRIntn error_code;
+
+int main(int argc, char **argv)
+{
+
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("pr_open_re.log");
+#endif
+
+
+ PR_STDIO_INIT();
+ t1 = PR_Open("/usr/tmp/ttools/err03.tmp", PR_TRUNCATE | PR_RDWR, 0666);
+ if (t1 == NULL)
+ if (PR_GetError() == PR_FILE_NOT_FOUND_ERROR) {
+ printf ("error code is %d \n", PR_GetError());
+ printf ("PASS\n");
+ return 0;
+ }
+ else {
+ printf ("error code is %d \n", PR_GetError());
+ printf ("FAIL\n");
+ return 1;
+ }
+}
diff --git a/pr/tests/op_filok.c b/pr/tests/op_filok.c
new file mode 100644
index 00000000..ae540ac7
--- /dev/null
+++ b/pr/tests/op_filok.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.
+ */
+
+/***********************************************************************
+**
+** Name: op_filok.c
+**
+** Description: Test Program to verify the PR_Open finding an existing file.
+**
+** Modification History:
+** 03-June-97 AGarcia- Initial version
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "prinit.h"
+#include "prmem.h"
+#include "prio.h"
+#include "prerror.h"
+#include <stdio.h>
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+#else
+#endif
+
+/*
+ * The name of a file that is guaranteed to exist
+ * on every machine of a particular OS.
+ */
+#ifdef XP_UNIX
+#define EXISTING_FILENAME "/bin/sh"
+#elif defined(WIN32)
+#define EXISTING_FILENAME "c:/boot.ini"
+#else
+#error "Unknown OS"
+#endif
+
+static PRFileDesc *t1;
+
+int main(int argc, char **argv)
+{
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("pr_open_re.log");
+#endif
+
+ PR_STDIO_INIT();
+ t1 = PR_Open(EXISTING_FILENAME, PR_RDONLY, 0666);
+ if (t1 == NULL) {
+ printf ("error code is %d \n", PR_GetError());
+ printf ("File %s should be found\n",
+ EXISTING_FILENAME);
+ return 1;
+ } else {
+ if (PR_Close(t1) == PR_SUCCESS) {
+ printf ("Test passed \n");
+ return 0;
+ } else {
+ printf ("cannot close file\n");
+ printf ("error code is %d\n", PR_GetError());
+ return 1;
+ }
+ }
+}
diff --git a/pr/tests/op_noacc.c b/pr/tests/op_noacc.c
new file mode 100644
index 00000000..b45fce2e
--- /dev/null
+++ b/pr/tests/op_noacc.c
@@ -0,0 +1,75 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+**
+** Name: op_noacc.c
+**
+** Description: Test Program to verify the PR_NO_ACCESS_RIGHTS_ERROR in PR_Open
+**
+** Modification History:
+** 03-June-97 AGarcia- Initial version
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "prinit.h"
+#include "prmem.h"
+#include "prio.h"
+#include "prerror.h"
+#include <stdio.h>
+#include "plgetopt.h"
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+#else
+#endif
+
+static PRFileDesc *err01;
+PRIntn error_code;
+
+int main(int argc, char **argv)
+{
+
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("pr_open_re.log");
+#endif
+
+#ifdef XP_PC
+ printf("op_noacc: Test not valid on MS-Windows.\n\tNo concept of 'mode' on Open() call\n");
+ return(0);
+#endif
+
+
+ PR_STDIO_INIT();
+ err01 = PR_Open("err01.tmp", PR_CREATE_FILE | PR_RDWR, 0);
+ if (err01 == NULL)
+ if (PR_GetError() == PR_NO_ACCESS_RIGHTS_ERROR) {
+ printf ("error code is %d\n",PR_GetError());
+ printf ("PASS\n");
+ return 0;
+ }
+ else {
+ printf ("FAIL\n");
+ return 1;
+ }
+}
diff --git a/pr/tests/op_nofil.c b/pr/tests/op_nofil.c
new file mode 100644
index 00000000..3cb3adef
--- /dev/null
+++ b/pr/tests/op_nofil.c
@@ -0,0 +1,80 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+**
+** Name: op_nofil.c
+**
+** Description: Test Program to verify the PR_FILE_NOT_FOUND_ERROR
+**
+** Modification History:
+** 03-June-97 AGarcia- Initial version
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "prinit.h"
+#include "prmem.h"
+#include "prio.h"
+#include "prerror.h"
+#include <stdio.h>
+#include "plgetopt.h"
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+#else
+#endif
+
+/*
+ * A file name that cannot exist
+ */
+#define NO_SUCH_FILE "/no/such/file.tmp"
+
+static PRFileDesc *t1;
+
+int main(int argc, char **argv)
+{
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("pr_open_re.log");
+#endif
+
+ PR_STDIO_INIT();
+ t1 = PR_Open(NO_SUCH_FILE, PR_RDONLY, 0666);
+ if (t1 == NULL) {
+ if (PR_GetError() == PR_FILE_NOT_FOUND_ERROR) {
+ printf ("error code is PR_FILE_NOT_FOUND_ERROR, as expected\n");
+ printf ("PASS\n");
+ return 0;
+ } else {
+ printf ("error code is %d \n", PR_GetError());
+ printf ("FAIL\n");
+ return 1;
+ }
+ }
+ printf ("File %s exists on this machine!?\n", NO_SUCH_FILE);
+ if (PR_Close(t1) == PR_FAILURE) {
+ printf ("cannot close file\n");
+ printf ("error code is %d \n", PR_GetError());
+ }
+ printf ("FAIL\n");
+ return 1;
+}
diff --git a/pr/tests/parent.c b/pr/tests/parent.c
new file mode 100644
index 00000000..d5fef9ec
--- /dev/null
+++ b/pr/tests/parent.c
@@ -0,0 +1,129 @@
+/* -*- 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: parent.c
+** description: test the process machinery
+** see also child.c
+*/
+
+#include "prmem.h"
+#include "prprf.h"
+#include "prinit.h"
+#include "prproces.h"
+#include "prinrval.h"
+
+typedef struct Child
+{
+ const char *name;
+ PRProcess *process;
+ PRProcessAttr *attr;
+} Child;
+
+static void PrintUsage(void)
+{
+ PR_fprintf(PR_GetSpecialFD(PR_StandardError),
+ "Usage: parent [-d] child [options]\n");
+}
+
+PRIntn main (PRIntn argc, char **argv)
+{
+ PRStatus rv;
+ PRInt32 test_status = 1;
+ PRIntervalTime t_start, t_elapsed;
+ PRFileDesc *debug = NULL;
+ Child *child = PR_NEWZAP(Child);
+
+ if (1 == argc)
+ {
+ PrintUsage();
+ return 2;
+ }
+
+ argv += 1; /* don't care about our program name */
+ while (argv[0][0] == '-')
+ {
+ if (argv[0][1] == 'd')
+ debug = PR_GetSpecialFD(PR_StandardError);
+ else
+ {
+ PrintUsage();
+ return 2; /* not sufficient */
+ }
+ argv += 1;
+ }
+
+ if (NULL == *argv)
+ {
+ PrintUsage();
+ return 2;
+ }
+
+ child->name = *argv;
+ if (NULL != debug) PR_fprintf(debug, "Forking %s\n", child->name);
+
+ child->attr = PR_NewProcessAttr();
+ PR_ProcessAttrSetStdioRedirect(
+ child->attr, PR_StandardOutput,
+ PR_GetSpecialFD(PR_StandardOutput));
+ PR_ProcessAttrSetStdioRedirect(
+ child->attr, PR_StandardError,
+ PR_GetSpecialFD(PR_StandardError));
+
+ t_start = PR_IntervalNow();
+ child->process = PR_CreateProcess(
+ child->name, argv, NULL, child->attr);
+ t_elapsed = (PRIntervalTime) (PR_IntervalNow() - t_start);
+
+ test_status = (NULL == child->process) ? 1 : 0;
+ if (NULL != debug)
+ {
+ PR_fprintf(
+ debug, "Child was %sforked\n",
+ (0 == test_status) ? "" : "NOT ");
+ if (0 == test_status)
+ PR_fprintf(
+ debug, "PR_CreateProcess took %lu microseconds\n",
+ PR_IntervalToMicroseconds(t_elapsed));
+ }
+
+ if (0 == test_status)
+ {
+ if (NULL != debug) PR_fprintf(debug, "Waiting for child to exit\n");
+ rv = PR_WaitProcess(child->process, &test_status);
+ if (PR_SUCCESS == rv)
+ {
+ if (NULL != debug)
+ PR_fprintf(
+ debug, "Child exited %s\n",
+ (0 == test_status) ? "successfully" : "with error");
+ }
+ else
+ {
+ test_status = 1;
+ if (NULL != debug)
+ PR_fprintf(debug, "PR_WaitProcess failed\n");
+ }
+ }
+ PR_Cleanup();
+ return test_status;
+
+} /* main */
+
+/* parent.c */
+
diff --git a/pr/tests/perf.c b/pr/tests/perf.c
new file mode 100644
index 00000000..b6785d75
--- /dev/null
+++ b/pr/tests/perf.c
@@ -0,0 +1,466 @@
+/* -*- 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 "nspr.h"
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int _debug_on = 0;
+#define DPRINTF(arg) if (_debug_on) printf arg
+
+#ifdef XP_MAC
+#include "prlog.h"
+#include "prsem.h"
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+#else
+#include "obsolete/prsem.h"
+#endif
+
+PRLock *lock;
+PRMonitor *mon;
+PRMonitor *mon2;
+
+#define DEFAULT_COUNT 1000
+
+PRInt32 count;
+
+static void nop(int a, int b, int c)
+{
+}
+
+static void LocalProcedureCall(void)
+{
+ PRInt32 i;
+
+ for (i = 0; i < count; i++) {
+ nop(i, i, 5);
+ }
+}
+
+static void DLLProcedureCall(void)
+{
+ PRInt32 i;
+ PRThreadState state;
+ PRThread *self = PR_CurrentThread();
+
+ for (i = 0; i < count; i++) {
+ state = PR_GetThreadState(self);
+ }
+}
+
+static void Now(void)
+{
+ PRInt32 i;
+ PRTime time;
+
+ for (i = 0; i < count; i++) {
+ time = PR_Now();
+ }
+}
+
+static void Interval(void)
+{
+ PRInt32 i;
+ PRIntervalTime time;
+
+ for (i = 0; i < count; i++) {
+ time = PR_IntervalNow();
+ }
+}
+
+static void IdleLock(void)
+{
+ PRInt32 i;
+
+ for (i = 0; i < count; i++) {
+ PR_Lock(lock);
+ PR_Unlock(lock);
+ }
+}
+
+static void IdleMonitor(void)
+{
+ PRInt32 i;
+
+ for (i = 0; i < count; i++) {
+ PR_EnterMonitor(mon);
+ PR_ExitMonitor(mon);
+ }
+}
+
+static void IdleCMonitor(void)
+{
+ PRInt32 i;
+
+ for (i = 0; i < count; i++) {
+ PR_CEnterMonitor((void*)7);
+ PR_CExitMonitor((void*)7);
+ }
+}
+
+/************************************************************************/
+
+static void PR_CALLBACK dull(void *arg)
+{
+}
+
+static void CDThread(void)
+{
+ PRInt32 i;
+ int num_threads = count;
+
+ /*
+ * Cannot create too many threads
+ */
+ if (num_threads > 1000)
+ num_threads = 1000;
+
+ for (i = 0; i < num_threads; i++) {
+ PRThread *t = PR_CreateThread(PR_USER_THREAD,
+ dull, 0,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (NULL == t) {
+ fprintf(stderr, "CDThread: cannot create thread %3d\n", i);
+ } else {
+ DPRINTF(("CDThread: created thread %3d \n",i));
+ }
+ PR_Sleep(0);
+ }
+}
+
+static int alive;
+static int cxq;
+
+static void PR_CALLBACK CXReader(void *arg)
+{
+ PRInt32 i, n;
+
+ PR_EnterMonitor(mon);
+ n = count / 2;
+ for (i = 0; i < n; i++) {
+ while (cxq == 0) {
+ DPRINTF(("CXReader: thread = 0x%lx waiting\n",
+ PR_GetCurrentThread()));
+ PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
+ }
+ --cxq;
+ PR_Notify(mon);
+ }
+ PR_ExitMonitor(mon);
+
+ PR_EnterMonitor(mon2);
+ --alive;
+ PR_Notify(mon2);
+ PR_ExitMonitor(mon2);
+ DPRINTF(("CXReader: thread = 0x%lx exiting\n", PR_GetCurrentThread()));
+}
+
+static void PR_CALLBACK CXWriter(void *arg)
+{
+ PRInt32 i, n;
+
+ PR_EnterMonitor(mon);
+ n = count / 2;
+ for (i = 0; i < n; i++) {
+ while (cxq == 1) {
+ DPRINTF(("CXWriter: thread = 0x%lx waiting\n",
+ PR_GetCurrentThread()));
+ PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
+ }
+ ++cxq;
+ PR_Notify(mon);
+ }
+ PR_ExitMonitor(mon);
+
+ PR_EnterMonitor(mon2);
+ --alive;
+ PR_Notify(mon2);
+ PR_ExitMonitor(mon2);
+ DPRINTF(("CXWriter: thread = 0x%lx exiting\n", PR_GetCurrentThread()));
+}
+
+static void ContextSwitch(PRThreadScope scope1, PRThreadScope scope2)
+{
+ PRThread *t1, *t2;
+
+ PR_EnterMonitor(mon2);
+ alive = 2;
+ cxq = 0;
+
+ t1 = PR_CreateThread(PR_USER_THREAD,
+ CXReader, 0,
+ PR_PRIORITY_NORMAL,
+ scope1,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (NULL == t1) {
+ fprintf(stderr, "ContextSwitch: cannot create thread\n");
+ } else {
+ DPRINTF(("ContextSwitch: created %s thread = 0x%lx\n",
+ (scope1 == PR_GLOBAL_THREAD ?
+ "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
+ t1));
+ }
+ t2 = PR_CreateThread(PR_USER_THREAD,
+ CXWriter, 0,
+ PR_PRIORITY_NORMAL,
+ scope2,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (NULL == t2) {
+ fprintf(stderr, "ContextSwitch: cannot create thread\n");
+ } else {
+ DPRINTF(("ContextSwitch: created %s thread = 0x%lx\n",
+ (scope2 == PR_GLOBAL_THREAD ?
+ "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
+ t2));
+ }
+
+ /* Wait for both of the threads to exit */
+ while (alive) {
+ PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_ExitMonitor(mon2);
+}
+
+static void ContextSwitchUU(void)
+{
+ ContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
+}
+
+static void ContextSwitchUK(void)
+{
+ ContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
+}
+
+static void ContextSwitchKU(void)
+{
+ ContextSwitch(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
+}
+
+static void ContextSwitchKK(void)
+{
+ ContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
+}
+
+/************************************************************************/
+
+static void PR_CALLBACK SemaThread(void *argSema)
+{
+ PRSemaphore **sem = (PRSemaphore **)argSema;
+ PRInt32 i, n;
+
+ n = count / 2;
+ for (i = 0; i < n; i++) {
+ DPRINTF(("SemaThread: thread = 0x%lx waiting on sem = 0x%lx\n",
+ PR_GetCurrentThread(), sem[0]));
+ PR_WaitSem(sem[0]);
+ DPRINTF(("SemaThread: thread = 0x%lx posting on sem = 0x%lx\n",
+ PR_GetCurrentThread(), sem[1]));
+ PR_PostSem(sem[1]);
+ }
+
+ PR_EnterMonitor(mon2);
+ --alive;
+ PR_Notify(mon2);
+ PR_ExitMonitor(mon2);
+ DPRINTF(("SemaThread: thread = 0x%lx exiting\n", PR_GetCurrentThread()));
+}
+
+static PRSemaphore *sem_set1[2];
+static PRSemaphore *sem_set2[2];
+
+static void SemaContextSwitch(PRThreadScope scope1, PRThreadScope scope2)
+{
+ PRThread *t1, *t2;
+ sem_set1[0] = PR_NewSem(1);
+ sem_set1[1] = PR_NewSem(0);
+ sem_set2[0] = sem_set1[1];
+ sem_set2[1] = sem_set1[0];
+
+ PR_EnterMonitor(mon2);
+ alive = 2;
+ cxq = 0;
+
+ t1 = PR_CreateThread(PR_USER_THREAD,
+ SemaThread,
+ sem_set1,
+ PR_PRIORITY_NORMAL,
+ scope1,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (NULL == t1) {
+ fprintf(stderr, "SemaContextSwitch: cannot create thread\n");
+ } else {
+ DPRINTF(("SemaContextSwitch: created %s thread = 0x%lx\n",
+ (scope1 == PR_GLOBAL_THREAD ?
+ "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
+ t1));
+ }
+ t2 = PR_CreateThread(PR_USER_THREAD,
+ SemaThread,
+ sem_set2,
+ PR_PRIORITY_NORMAL,
+ scope2,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (NULL == t2) {
+ fprintf(stderr, "SemaContextSwitch: cannot create thread\n");
+ } else {
+ DPRINTF(("SemaContextSwitch: created %s thread = 0x%lx\n",
+ (scope2 == PR_GLOBAL_THREAD ?
+ "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"),
+ t2));
+ }
+
+ /* Wait for both of the threads to exit */
+ while (alive) {
+ PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_ExitMonitor(mon2);
+
+ PR_DestroySem(sem_set1[0]);
+ PR_DestroySem(sem_set1[1]);
+}
+
+static void SemaContextSwitchUU(void)
+{
+ SemaContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
+}
+
+static void SemaContextSwitchUK(void)
+{
+ SemaContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
+}
+
+static void SemaContextSwitchKU(void)
+{
+ SemaContextSwitch(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
+}
+
+static void SemaContextSwitchKK(void)
+{
+ SemaContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
+}
+
+
+/************************************************************************/
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow() - start;
+ d = (double)PR_IntervalToMicroseconds(stop);
+
+ printf("%40s: %6.2f usec\n", msg, d / count);
+}
+
+int main(int argc, char **argv)
+{
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dc:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ _debug_on = 1;
+ break;
+ case 'c': /* loop count */
+ count = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (0 == count) count = DEFAULT_COUNT;
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_BlockClockInterrupts();
+ PR_UnblockClockInterrupts();
+ PR_STDIO_INIT();
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("perf.log");
+#endif
+
+ lock = PR_NewLock();
+ mon = PR_NewMonitor();
+ mon2 = PR_NewMonitor();
+
+ Measure(LocalProcedureCall, "local procedure call overhead");
+ Measure(DLLProcedureCall, "DLL procedure call overhead");
+ Measure(Now, "current calendar time");
+ Measure(Interval, "interval time");
+ Measure(IdleLock, "idle lock lock/unlock pair");
+ Measure(IdleMonitor, "idle monitor entry/exit pair");
+ Measure(IdleCMonitor, "idle cache monitor entry/exit pair");
+ Measure(CDThread, "create/destroy thread pair");
+ Measure(ContextSwitchUU, "context switch - user/user");
+ Measure(ContextSwitchUK, "context switch - user/kernel");
+ Measure(ContextSwitchKU, "context switch - kernel/user");
+ Measure(ContextSwitchKK, "context switch - kernel/kernel");
+ Measure(SemaContextSwitchUU, "sema context switch - user/user");
+ Measure(SemaContextSwitchUK, "sema context switch - user/kernel");
+ Measure(SemaContextSwitchKU, "sema context switch - kernel/user");
+ Measure(SemaContextSwitchKK, "sema context switch - kernel/kernel");
+
+ printf("--------------\n");
+ printf("Adding 7 additional CPUs\n");
+
+ PR_SetConcurrency(8);
+ printf("--------------\n");
+
+ Measure(LocalProcedureCall, "local procedure call overhead");
+ Measure(DLLProcedureCall, "DLL procedure call overhead");
+ Measure(Now, "current calendar time");
+ Measure(Interval, "interval time");
+ Measure(IdleLock, "idle lock lock/unlock pair");
+ Measure(IdleMonitor, "idle monitor entry/exit pair");
+ Measure(IdleCMonitor, "idle cache monitor entry/exit pair");
+ Measure(CDThread, "create/destroy thread pair");
+ Measure(ContextSwitchUU, "context switch - user/user");
+ Measure(ContextSwitchUK, "context switch - user/kernel");
+ Measure(ContextSwitchKU, "context switch - kernel/user");
+ Measure(ContextSwitchKK, "context switch - kernel/kernel");
+ Measure(SemaContextSwitchUU, "sema context switch - user/user");
+ Measure(SemaContextSwitchUK, "sema context switch - user/kernel");
+ Measure(SemaContextSwitchKU, "sema context switch - kernel/user");
+ Measure(SemaContextSwitchKK, "sema context switch - kernel/kernel");
+
+ PR_DestroyLock(lock);
+ PR_DestroyMonitor(mon);
+ PR_DestroyMonitor(mon2);
+
+ PR_Cleanup();
+ return 0;
+}
diff --git a/pr/tests/poll_er.c b/pr/tests/poll_er.c
new file mode 100755
index 00000000..c766cb61
--- /dev/null
+++ b/pr/tests/poll_er.c
@@ -0,0 +1,214 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+**
+** Name: prpoll_err.c
+**
+** Description: This program tests PR_Poll with sockets.
+** error reporting operation is tested
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "primpl.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+static void
+ClientThreadFunc(void *arg)
+{
+ PRFileDesc *badFD = (PRFileDesc *) arg;
+ /*
+ * Make the fd invalid
+ */
+#if defined(XP_UNIX)
+ close(PR_FileDesc2NativeHandle(badFD));
+#elif defined(XP_OS2)
+ soclose(PR_FileDesc2NativeHandle(badFD));
+#elif defined(WIN32) || defined(WIN16)
+ closesocket(PR_FileDesc2NativeHandle(badFD));
+#elif defined(XP_MAC)
+ _PR_MD_CLOSE_SOCKET(PR_FileDesc2NativeHandle(badFD));
+#else
+#error "Unknown architecture"
+#endif
+}
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *listenSock1, *listenSock2;
+ PRFileDesc *badFD;
+ PRUint16 listenPort1, listenPort2;
+ PRNetAddr addr;
+ char buf[128];
+ PRPollDesc pds0[10], pds1[10], *pds, *other_pds;
+ PRIntn npds;
+ PRInt32 retVal;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ if (debug_mode) {
+ printf("This program tests PR_Poll with sockets.\n");
+ printf("error reporting is tested.\n\n");
+ }
+
+ /* Create two listening sockets */
+ if ((listenSock1 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ addr.inet.family = AF_INET;
+ addr.inet.ip = PR_htonl(INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ listenPort1 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock1, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+
+ if ((listenSock2 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ addr.inet.family = AF_INET;
+ addr.inet.ip = PR_htonl(INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ listenPort2 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock2, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ PR_snprintf(buf, sizeof(buf),
+ "The server thread is listening on ports %hu and %hu\n\n",
+ listenPort1, listenPort2);
+ if (debug_mode) printf("%s", buf);
+
+ /* Set up the poll descriptor array */
+ pds = pds0;
+ other_pds = pds1;
+ memset(pds, 0, sizeof(pds));
+ pds[0].fd = listenSock1;
+ pds[0].in_flags = PR_POLL_READ;
+ pds[1].fd = listenSock2;
+ pds[1].in_flags = PR_POLL_READ;
+ npds = 2;
+
+
+ /* Testing bad fd */
+ if (debug_mode) printf("PR_Poll should detect a bad file descriptor\n");
+ if ((badFD = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a TCP socket\n");
+ goto exit_now;
+ }
+
+ pds[2].fd = badFD;
+ pds[2].in_flags = PR_POLL_READ;
+ npds = 3;
+
+ if (PR_CreateThread(PR_USER_THREAD, ClientThreadFunc,
+ badFD, PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0) == NULL) {
+ fprintf(stderr, "cannot create thread\n");
+ exit(1);
+ }
+
+ retVal = PR_Poll(pds, npds, PR_INTERVAL_NO_TIMEOUT);
+ if (retVal != 1 || (unsigned short) pds[2].out_flags != PR_POLL_NVAL) {
+ fprintf(stderr, "Failed to detect the bad fd: "
+ "PR_Poll returns %d, out_flags is 0x%hx\n",
+ retVal, pds[2].out_flags);
+ failed_already=1;
+ goto exit_now;
+ }
+ if (debug_mode) printf("PR_Poll detected the bad fd. Test passed.\n\n");
+ PR_Cleanup();
+ goto exit_now;
+exit_now:
+ if(failed_already)
+ return 1;
+ else
+ return 0;
+}
diff --git a/pr/tests/poll_nm.c b/pr/tests/poll_nm.c
new file mode 100644
index 00000000..ab5c5286
--- /dev/null
+++ b/pr/tests/poll_nm.c
@@ -0,0 +1,377 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+**
+** Name: prpoll_norm.c
+**
+** Description: This program tests PR_Poll with sockets.
+** Normal operation are tested
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "prinit.h"
+#include "prio.h"
+#include "prlog.h"
+#include "prprf.h"
+#include "prnetdb.h"
+#ifndef XP_MAC
+#include "obsolete/probslet.h"
+#else
+#include "probslet.h"
+#endif
+
+#ifndef XP_MAC
+#include "private/pprio.h"
+#else
+#include "pprio.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+#define NUM_ITERATIONS 5
+
+#ifdef XP_MAC
+int fprintf(FILE *stream, const char *fmt, ...)
+{
+PR_LogPrint(fmt);
+return 0;
+}
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+#endif
+
+static void PR_CALLBACK
+clientThreadFunc(void *arg)
+{
+ PRUintn port = (PRUintn) arg;
+ PRFileDesc *sock;
+ PRNetAddr addr;
+ char buf[128];
+ int i;
+ PRStatus sts;
+ PRInt32 n;
+
+ addr.inet.family = PR_AF_INET;
+ addr.inet.port = PR_htons((PRUint16)port);
+ addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+ memset(buf, 0, sizeof(buf));
+ PR_snprintf(buf, sizeof(buf), "%hu", port);
+
+ for (i = 0; i < NUM_ITERATIONS; i++) {
+ sock = PR_NewTCPSocket();
+ PR_ASSERT(sock != NULL);
+
+ sts = PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(sts == PR_SUCCESS);
+
+ n = PR_Write(sock, buf, sizeof(buf));
+ PR_ASSERT(n >= 0);
+
+ sts = PR_Close(sock);
+ PR_ASSERT(sts == PR_SUCCESS);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *listenSock1 = NULL, *listenSock2 = NULL;
+ PRUint16 listenPort1, listenPort2;
+ PRNetAddr addr;
+ char buf[128];
+ PRThread *clientThread;
+ PRPollDesc pds0[20], pds1[20], *pds, *other_pds;
+ PRIntn npds;
+ PRInt32 retVal;
+ PRIntn i, j;
+ PRIntn optval = 1;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+#ifdef XP_MAC
+ debug_mode = 1;
+ SetupMacPrintfLog("poll_nm.log");
+#endif
+
+ if (debug_mode) {
+ printf("This program tests PR_Poll with sockets.\n");
+ printf("Normal operation are tested.\n\n");
+ }
+
+ /* Create two listening sockets */
+ if ((listenSock1 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ addr.inet.family = PR_AF_INET;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ listenPort1 = PR_ntohs(addr.inet.port);
+ PR_SetSockOpt(listenSock1, PR_SockOpt_Nonblocking, &optval, sizeof(optval));
+ if (PR_Listen(listenSock1, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+
+ if ((listenSock2 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ addr.inet.family = PR_AF_INET;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ listenPort2 = PR_ntohs(addr.inet.port);
+ PR_SetSockOpt(listenSock2, PR_SockOpt_Nonblocking, &optval, sizeof(optval));
+ if (PR_Listen(listenSock2, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ PR_snprintf(buf, sizeof(buf),
+ "The server thread is listening on ports %hu and %hu\n\n",
+ listenPort1, listenPort2);
+ if (debug_mode) printf("%s", buf);
+
+ /* Set up the poll descriptor array */
+ pds = pds0;
+ other_pds = pds1;
+ memset(pds, 0, sizeof(pds));
+ pds[0].fd = listenSock1;
+ pds[0].in_flags = PR_POLL_READ;
+ pds[1].fd = listenSock2;
+ pds[1].in_flags = PR_POLL_READ;
+ /* Add some unused entries to test if they are ignored by PR_Poll() */
+ memset(&pds[2], 0, sizeof(pds[2]));
+ memset(&pds[3], 0, sizeof(pds[3]));
+ memset(&pds[4], 0, sizeof(pds[4]));
+ npds = 5;
+
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ clientThreadFunc, (void *) listenPort1,
+ PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+ if (clientThread == NULL) {
+ fprintf(stderr, "can't create thread\n");
+ failed_already=1;
+ goto exit_now;
+ }
+
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ clientThreadFunc, (void *) listenPort2,
+ PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+ if (clientThread == NULL) {
+ fprintf(stderr, "can't create thread\n");
+ failed_already=1;
+ goto exit_now;
+ }
+
+ if (debug_mode) {
+ printf("Two client threads are created. Each of them will\n");
+ printf("send data to one of the two ports the server is listening on.\n");
+ printf("The data they send is the port number. Each of them send\n");
+ printf("the data five times, so you should see ten lines below,\n");
+ printf("interleaved in an arbitrary order.\n");
+ }
+
+ /* two clients, three events per iteration: accept, read, close */
+ i = 0;
+ while (i < 2 * 3 * NUM_ITERATIONS) {
+ PRPollDesc *tmp;
+ int nextIndex;
+ int nEvents = 0;
+
+ retVal = PR_Poll(pds, npds, PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(retVal != 0); /* no timeout */
+ if (retVal == -1) {
+ fprintf(stderr, "PR_Poll failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+
+ nextIndex = 2;
+ /* the two listening sockets */
+ for (j = 0; j < 2; j++) {
+ other_pds[j] = pds[j];
+ PR_ASSERT((pds[j].out_flags & PR_POLL_WRITE) == 0
+ && (pds[j].out_flags & PR_POLL_EXCEPT) == 0);
+ if (pds[j].out_flags & PR_POLL_READ) {
+ PRFileDesc *sock;
+
+ nEvents++;
+ sock = PR_Accept(pds[j].fd, NULL, PR_INTERVAL_NO_TIMEOUT);
+ if (sock == NULL) {
+ fprintf(stderr, "PR_Accept() failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ other_pds[nextIndex].fd = sock;
+ other_pds[nextIndex].in_flags = PR_POLL_READ;
+ nextIndex++;
+ } else if (pds[j].out_flags & PR_POLL_ERR) {
+ fprintf(stderr, "PR_Poll() indicates that an fd has error\n");
+ failed_already=1;
+ goto exit_now;
+ } else if (pds[j].out_flags & PR_POLL_NVAL) {
+ fprintf(stderr, "PR_Poll() indicates that fd %d is invalid\n",
+ PR_FileDesc2NativeHandle(pds[j].fd));
+ failed_already=1;
+ goto exit_now;
+ }
+ }
+
+ for (j = 2; j < npds; j++) {
+ if (NULL == pds[j].fd) {
+ /*
+ * Keep the unused entries in the poll descriptor array
+ * for testing purposes.
+ */
+ other_pds[nextIndex] = pds[j];
+ nextIndex++;
+ continue;
+ }
+
+ PR_ASSERT((pds[j].out_flags & PR_POLL_WRITE) == 0
+ && (pds[j].out_flags & PR_POLL_EXCEPT) == 0);
+ if (pds[j].out_flags & PR_POLL_READ) {
+ PRInt32 nAvail;
+ PRInt32 nRead;
+
+ nEvents++;
+ nAvail = PR_Available(pds[j].fd);
+ nRead = PR_Read(pds[j].fd, buf, sizeof(buf));
+ PR_ASSERT(nAvail == nRead);
+ if (nRead == -1) {
+ fprintf(stderr, "PR_Read() failed\n");
+ failed_already=1;
+ goto exit_now;
+ } else if (nRead == 0) {
+ PR_Close(pds[j].fd);
+ continue;
+ } else {
+ /* Just to be safe */
+ buf[127] = '\0';
+ if (debug_mode) printf("The server received \"%s\" from a client\n", buf);
+ }
+ } else if (pds[j].out_flags & PR_POLL_ERR) {
+ fprintf(stderr, "PR_Poll() indicates that an fd has error\n");
+ failed_already=1;
+ goto exit_now;
+ } else if (pds[j].out_flags & PR_POLL_NVAL) {
+ fprintf(stderr, "PR_Poll() indicates that an fd is invalid\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ other_pds[nextIndex] = pds[j];
+ nextIndex++;
+ }
+
+ PR_ASSERT(retVal == nEvents);
+ /* swap */
+ tmp = pds;
+ pds = other_pds;
+ other_pds = tmp;
+ npds = nextIndex;
+ i += nEvents;
+ }
+
+ if (debug_mode) printf("Tests passed\n");
+
+exit_now:
+
+ if (listenSock1) {
+ PR_Close(listenSock1);
+ }
+ if (listenSock2) {
+ PR_Close(listenSock2);
+ }
+
+ PR_Cleanup();
+
+ if(failed_already)
+ return 1;
+ else
+ return 0;
+
+}
diff --git a/pr/tests/poll_to.c b/pr/tests/poll_to.c
new file mode 100644
index 00000000..e617efd1
--- /dev/null
+++ b/pr/tests/poll_to.c
@@ -0,0 +1,188 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+**
+** Name: prpoll_to.c
+**
+** Description: This program tests PR_Poll with sockets.
+** Timeout operation is tested
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "prinit.h"
+#include "prio.h"
+#include "prlog.h"
+#include "prprf.h"
+#include "prnetdb.h"
+
+#ifndef XP_MAC
+#include "private/pprio.h"
+#else
+#include "pprio.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *listenSock1, *listenSock2;
+ PRUint16 listenPort1, listenPort2;
+ PRNetAddr addr;
+ char buf[128];
+ PRPollDesc pds0[10], pds1[10], *pds, *other_pds;
+ PRIntn npds;
+ PRInt32 retVal;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ if (debug_mode) {
+ printf("This program tests PR_Poll with sockets.\n");
+ printf("Timeout is tested.\n\n");
+ }
+
+ /* Create two listening sockets */
+ if ((listenSock1 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ if (!debug_mode) failed_already=1;
+ goto exit_now;
+ }
+ addr.inet.family = PR_AF_INET;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ if (!debug_mode) failed_already=1;
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ if (!debug_mode) failed_already=1;
+ goto exit_now;
+ }
+ listenPort1 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock1, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ if (!debug_mode) failed_already=1;
+ goto exit_now;
+ }
+
+ if ((listenSock2 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ if (!debug_mode) failed_already=1;
+ goto exit_now;
+ }
+ addr.inet.family = PR_AF_INET;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ if (!debug_mode) failed_already=1;
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ if (!debug_mode) failed_already=1;
+ goto exit_now;
+ }
+ listenPort2 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock2, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ if (!debug_mode) failed_already=1;
+ goto exit_now;
+ }
+ PR_snprintf(buf, sizeof(buf),
+ "The server thread is listening on ports %hu and %hu\n\n",
+ listenPort1, listenPort2);
+ if (debug_mode) printf("%s", buf);
+
+ /* Set up the poll descriptor array */
+ pds = pds0;
+ other_pds = pds1;
+ memset(pds, 0, sizeof(pds));
+ pds[0].fd = listenSock1;
+ pds[0].in_flags = PR_POLL_READ;
+ pds[1].fd = listenSock2;
+ pds[1].in_flags = PR_POLL_READ;
+ npds = 2;
+
+ /* Testing timeout */
+ if (debug_mode) printf("PR_Poll should time out in 5 seconds\n");
+ retVal = PR_Poll(pds, npds, PR_SecondsToInterval(5));
+ if (retVal != 0) {
+ PR_snprintf(buf, sizeof(buf),
+ "PR_Poll should time out and return 0, but it returns %ld\n",
+ retVal);
+ fprintf(stderr, "%s", buf);
+ if (!debug_mode) failed_already=1;
+ goto exit_now;
+ }
+ if (debug_mode) printf("PR_Poll timed out. Test passed.\n\n");
+
+ PR_Cleanup();
+ goto exit_now;
+exit_now:
+ if(failed_already)
+ return 1;
+ else
+ return 0;
+
+}
diff --git a/pr/tests/pollable.c b/pr/tests/pollable.c
new file mode 100644
index 00000000..f31d8a93
--- /dev/null
+++ b/pr/tests/pollable.c
@@ -0,0 +1,273 @@
+/* -*- 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.
+ */
+
+/*
+ * A test for the pollable events.
+ *
+ * A number of threads are in a ring configuration, each waiting on
+ * a pollable event that is set by its upstream neighbor.
+ */
+
+#include "prinit.h"
+#include "prio.h"
+#include "prthread.h"
+#include "prerror.h"
+#include "prmem.h"
+#include "prlog.h"
+#include "prprf.h"
+
+#include "plgetopt.h"
+
+#include <stdlib.h>
+
+#define DEFAULT_THREADS 10
+#define DEFAULT_LOOPS 100
+
+PRIntn numThreads = DEFAULT_THREADS;
+PRIntn numIterations = DEFAULT_LOOPS;
+PRIntervalTime dally = PR_INTERVAL_NO_WAIT;
+PRFileDesc *debug_out = NULL;
+PRBool debug_mode = PR_FALSE;
+PRBool verbosity = PR_FALSE;
+
+typedef struct ThreadData {
+ PRFileDesc *event;
+ int index;
+ struct ThreadData *next;
+} ThreadData;
+
+void ThreadRoutine(void *arg)
+{
+ ThreadData *data = (ThreadData *) arg;
+ PRIntn i;
+ PRPollDesc pd;
+ PRInt32 rv;
+
+ pd.fd = data->event;
+ pd.in_flags = PR_POLL_READ;
+
+ for (i = 0; i < numIterations; i++) {
+ rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+ if (rv == -1) {
+ PR_fprintf(PR_STDERR, "PR_Poll failed\n");
+ exit(1);
+ }
+ if (verbosity) {
+ PR_fprintf(debug_out, "thread %d awakened\n", data->index);
+ }
+ PR_ASSERT(rv != 0);
+ PR_ASSERT(pd.out_flags & PR_POLL_READ);
+ if (PR_WaitForPollableEvent(data->event) == PR_FAILURE) {
+ PR_fprintf(PR_STDERR, "consume event failed\n");
+ exit(1);
+ }
+ if (dally != PR_INTERVAL_NO_WAIT) {
+ PR_Sleep(dally);
+ }
+ if (verbosity) {
+ PR_fprintf(debug_out, "thread %d posting event\n", data->index);
+ }
+ if (PR_SetPollableEvent(data->next->event) == PR_FAILURE) {
+ PR_fprintf(PR_STDERR, "post event failed\n");
+ exit(1);
+ }
+ }
+}
+
+static void Help(void)
+{
+ debug_out = PR_STDOUT;
+
+ PR_fprintf(
+ debug_out, "Usage: pollable [-c n] [-t n] [-d] [-v] [-G] [-C n] [-D n]\n");
+ PR_fprintf(
+ debug_out, "-c n\tloops at thread level (default: %d)\n", DEFAULT_LOOPS);
+ PR_fprintf(
+ debug_out, "-t n\tnumber of threads (default: %d)\n", DEFAULT_THREADS);
+ PR_fprintf(debug_out, "-d\tturn on debugging output (default: FALSE)\n");
+ PR_fprintf(debug_out, "-v\tturn on verbose output (default: FALSE)\n");
+ PR_fprintf(debug_out, "-G\tglobal threads only (default: FALSE)\n");
+ PR_fprintf(debug_out, "-C n\tconcurrency setting (default: 1)\n");
+ PR_fprintf(debug_out, "-D n\tdally setting (msecs) (default: 0)\n");
+} /* Help */
+
+int main(int argc, char **argv)
+{
+ ThreadData selfData;
+ ThreadData *data;
+ PRThread **thread;
+ void *block;
+ PRIntn i;
+ PRIntervalTime timeStart, timeEnd;
+ PRPollDesc pd;
+ PRInt32 rv;
+ PRThreadScope thread_scope = PR_LOCAL_THREAD;
+ PRBool help = PR_FALSE;
+ PRUintn concurrency = 1;
+ PRUintn average;
+ PLOptStatus os;
+ PLOptState *opt;
+
+ PR_STDIO_INIT();
+
+ opt = PL_CreateOptState(argc, argv, "hdvc:t:C:GD:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) {
+ if (PL_OPT_BAD == os) {
+ continue;
+ }
+ switch (opt->option) {
+ case 'v': /* verbose mode */
+ verbosity = PR_TRUE;
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'c': /* loop counter */
+ numIterations = atoi(opt->value);
+ break;
+ case 't': /* thread limit */
+ numThreads = atoi(opt->value);
+ break;
+ case 'C': /* Concurrency limit */
+ concurrency = atoi(opt->value);
+ break;
+ case 'G': /* global threads only */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'D': /* dally */
+ dally = PR_MillisecondsToInterval(atoi(opt->value));
+ break;
+ case 'h': /* help message */
+ Help();
+ help = PR_TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (help) {
+ return 1;
+ }
+
+ if (concurrency > 1) {
+ PR_SetConcurrency(concurrency);
+ }
+
+ if (PR_TRUE == debug_mode) {
+ debug_out = PR_STDOUT;
+ PR_fprintf(debug_out, "Test parameters\n");
+ PR_fprintf(debug_out, "\tThreads involved: %d\n", numThreads);
+ PR_fprintf(debug_out, "\tIteration limit: %d\n", numIterations);
+ PR_fprintf(debug_out, "\tConcurrency: %d\n", concurrency);
+ PR_fprintf(debug_out, "\tThread type: %s\n",
+ (PR_GLOBAL_THREAD == thread_scope) ? "GLOBAL" : "LOCAL");
+ }
+
+ /*
+ * Malloc a block of memory and divide it into data and thread.
+ */
+ block = PR_MALLOC(numThreads * (sizeof(ThreadData) + sizeof(PRThread *)));
+ if (block == NULL) {
+ PR_fprintf(PR_STDERR, "cannot malloc, failed\n");
+ exit(1);
+ }
+ data = (ThreadData *) block;
+ thread = (PRThread **) &data[numThreads];
+
+ /* Pollable event */
+ selfData.event = PR_NewPollableEvent();
+ if (selfData.event == NULL) {
+ PR_fprintf(PR_STDERR, "cannot create event: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ selfData.next = &data[0];
+ for (i = 0; i < numThreads; i++) {
+ data[i].event = PR_NewPollableEvent();
+ if (data[i].event == NULL) {
+ PR_fprintf(PR_STDERR, "cannot create event: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ exit(1);
+ }
+ data[i].index = i;
+ if (i != numThreads - 1) {
+ data[i].next = &data[i + 1];
+ } else {
+ data[i].next = &selfData;
+ }
+
+ thread[i] = PR_CreateThread(PR_USER_THREAD,
+ ThreadRoutine, &data[i], PR_PRIORITY_NORMAL,
+ thread_scope, PR_JOINABLE_THREAD, 0);
+ if (thread[i] == NULL) {
+ PR_fprintf(PR_STDERR, "cannot create thread\n");
+ exit(1);
+ }
+ }
+
+ timeStart = PR_IntervalNow();
+ pd.fd = selfData.event;
+ pd.in_flags = PR_POLL_READ;
+ for (i = 0; i < numIterations; i++) {
+ if (dally != PR_INTERVAL_NO_WAIT) {
+ PR_Sleep(dally);
+ }
+ if (verbosity) {
+ PR_fprintf(debug_out, "main thread posting event\n");
+ }
+ if (PR_SetPollableEvent(selfData.next->event) == PR_FAILURE) {
+ PR_fprintf(PR_STDERR, "set event failed\n");
+ exit(1);
+ }
+ rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
+ if (rv == -1) {
+ PR_fprintf(PR_STDERR, "wait failed\n");
+ exit(1);
+ }
+ PR_ASSERT(rv != 0);
+ PR_ASSERT(pd.out_flags & PR_POLL_READ);
+ if (verbosity) {
+ PR_fprintf(debug_out, "main thread awakened\n");
+ }
+ if (PR_WaitForPollableEvent(selfData.event) == PR_FAILURE) {
+ PR_fprintf(PR_STDERR, "consume event failed\n");
+ exit(1);
+ }
+ }
+ timeEnd = PR_IntervalNow();
+
+ if (debug_mode) {
+ average = PR_IntervalToMicroseconds(timeEnd - timeStart)
+ / (numIterations * numThreads);
+ PR_fprintf(debug_out, "Average switch times %d usecs for %d threads\n",
+ average, numThreads);
+ }
+
+ for (i = 0; i < numThreads; i++) {
+ if (PR_JoinThread(thread[i]) == PR_FAILURE) {
+ PR_fprintf(PR_STDERR, "join thread failed\n");
+ exit(1);
+ }
+ PR_DestroyPollableEvent(data[i].event);
+ }
+ PR_DELETE(block);
+
+ PR_fprintf(PR_STDOUT, "PASSED\n");
+ return 0;
+}
diff --git a/pr/tests/prftest.c b/pr/tests/prftest.c
new file mode 100644
index 00000000..9d674778
--- /dev/null
+++ b/pr/tests/prftest.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.
+ */
+
+/*
+ * File: prftest.c
+ * Description:
+ * This is a simple test of the PR_snprintf() function defined
+ * in prprf.c.
+ */
+
+#include "prlong.h"
+#include "prprf.h"
+
+#include <string.h>
+
+#define BUF_SIZE 128
+
+int main() {
+ PRInt16 i16;
+ PRIntn n;
+ PRInt32 i32;
+ PRInt64 i64;
+ char buf[BUF_SIZE];
+ char answer[BUF_SIZE];
+ int i;
+
+ PR_STDIO_INIT();
+ i16 = -1;
+ n = -1;
+ i32 = -1;
+ LL_I2L(i64, i32);
+
+ PR_snprintf(buf, BUF_SIZE, "%hx %x %lx %llx", i16, n, i32, i64);
+ strcpy(answer, "ffff ");
+ for (i = PR_BYTES_PER_INT * 2; i; i--) {
+ strcat(answer, "f");
+ }
+ strcat(answer, " ffffffff ffffffffffffffff");
+
+ if (!strcmp(buf, answer)) {
+ printf("PR_snprintf test 1 passed\n");
+ } else {
+ printf("PR_snprintf test 1 failed\n");
+ printf("Converted string is %s\n", buf);
+ printf("Should be %s\n", answer);
+ }
+
+ i16 = -32;
+ n = 30;
+ i32 = 64;
+ LL_I2L(i64, 333);
+ PR_snprintf(buf, BUF_SIZE, "%d %hd %lld %ld", n, i16, i64, i32);
+ if (!strcmp(buf, "30 -32 333 64")) {
+ printf("PR_snprintf test 2 passed\n");
+ } else {
+ printf("PR_snprintf test 2 failed\n");
+ printf("Converted string is %s\n", buf);
+ printf("Should be 30 -32 333 64\n");
+ }
+
+ return 0;
+}
diff --git a/pr/tests/prftest1.c b/pr/tests/prftest1.c
new file mode 100644
index 00000000..501e21eb
--- /dev/null
+++ b/pr/tests/prftest1.c
@@ -0,0 +1,133 @@
+/* -*- 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: prftest1.c
+** Description:
+** This is a simple test of the PR_snprintf() function defined
+** in prprf.c.
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+***********************************************************************/
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+#include "prttools.h"
+
+#include "prinit.h"
+#include "prlong.h"
+#include "prprf.h"
+
+#include <string.h>
+
+#define BUF_SIZE 128
+
+/***********************************************************************
+** PRIVATE FUNCTION: Test_Result
+** DESCRIPTION: Used in conjunction with the regress tool, prints out the
+** status of the test case.
+** INPUTS: PASS/FAIL
+** OUTPUTS: None
+** RETURN: None
+** SIDE EFFECTS:
+**
+** RESTRICTIONS:
+** None
+** MEMORY: NA
+** ALGORITHM: Determine what the status is and print accordingly.
+**
+***********************************************************************/
+
+
+static void Test_Result (int result)
+{
+ if (result == PASS)
+ printf ("PASS\n");
+ else
+ printf ("FAIL\n");
+}
+
+int main( int argc, char *argv[])
+{
+ PRInt16 i16;
+ PRIntn n;
+ PRInt32 i32;
+ PRInt64 i64;
+ char buf[BUF_SIZE];
+ char answer[BUF_SIZE];
+ int i;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+ PR_STDIO_INIT();
+
+ i16 = -1;
+ n = -1;
+ i32 = -1;
+ LL_I2L(i64, i32);
+
+ PR_snprintf(buf, BUF_SIZE, "%hx %x %lx %llx", i16, n, i32, i64);
+ strcpy(answer, "ffff ");
+ for (i = PR_BYTES_PER_INT * 2; i; i--) {
+ strcat(answer, "f");
+ }
+ strcat(answer, " ffffffff ffffffffffffffff");
+
+ if (!strcmp(buf, answer)) {
+ if (debug_mode) printf("PR_snprintf test 1 passed\n");
+ else Test_Result (PASS);
+ } else {
+ if (debug_mode) {
+ printf("PR_snprintf test 1 failed\n");
+ printf("Converted string is %s\n", buf);
+ printf("Should be %s\n", answer);
+ }
+ else
+ Test_Result (FAIL);
+ }
+
+ return 0;
+}
diff --git a/pr/tests/prftest2.c b/pr/tests/prftest2.c
new file mode 100644
index 00000000..c3239905
--- /dev/null
+++ b/pr/tests/prftest2.c
@@ -0,0 +1,110 @@
+/* -*- 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: prftest2.c
+** Description:
+** This is a simple test of the PR_snprintf() function defined
+** in prprf.c.
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "prlong.h"
+#include "prinit.h"
+#include "prprf.h"
+
+#include <string.h>
+
+#define BUF_SIZE 128
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+int main( int argc, char *argv[])
+{
+ PRInt16 i16;
+ PRIntn n;
+ PRInt32 i32;
+ PRInt64 i64;
+ char buf[BUF_SIZE];
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+
+ PR_STDIO_INIT();
+ i16 = -32;
+ n = 30;
+ i32 = 64;
+ LL_I2L(i64, 333);
+ PR_snprintf(buf, BUF_SIZE, "%d %hd %lld %ld", n, i16, i64, i32);
+ if (!strcmp(buf, "30 -32 333 64")) {
+ if (debug_mode) printf("PR_snprintf test 2 passed\n");
+ } else {
+ if (debug_mode) {
+ printf("PR_snprintf test 2 failed\n");
+ printf("Converted string is %s\n", buf);
+ printf("Should be 30 -32 333 64\n");
+ }
+ else failed_already=1;
+ }
+ if(failed_already)
+ {
+ printf("FAILED\n");
+ return 1;
+ }
+ else
+ {
+ printf("PASSED\n");
+ return 0;
+ }
+}
diff --git a/pr/tests/priotest.c b/pr/tests/priotest.c
new file mode 100644
index 00000000..f9df1528
--- /dev/null
+++ b/pr/tests/priotest.c
@@ -0,0 +1,214 @@
+/* -*- 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: priotest.c
+ * Purpose: testing priorities
+ */
+
+#ifdef XP_MAC
+#error "This test does not run on Macintosh"
+#else
+
+
+#include "prcmon.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prmon.h"
+#include "prprf.h"
+#include "prthread.h"
+#include "prtypes.h"
+
+#include "plerror.h"
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define DEFAULT_DURATION 5
+
+static PRBool failed = PR_FALSE;
+static PRIntervalTime oneSecond;
+static PRFileDesc *debug_out = NULL;
+static PRBool debug_mode = PR_FALSE;
+
+static PRUint32 PerSecond(PRIntervalTime timein)
+{
+ PRUint32 loop = 0;
+ while (((PRIntervalTime)(PR_IntervalNow()) - timein) < oneSecond)
+ loop += 1;
+ return loop;
+} /* PerSecond */
+
+static void PR_CALLBACK Low(void *arg)
+{
+ PRUint32 t3 = 0, t2 = 0, t1 = 0, t0, *tn = (PRUint32*)arg;
+ while (1)
+ {
+ t0 = PerSecond(PR_IntervalNow());
+ *tn = (t3 + 3 * t2 + 3 * t1 + t0) / 8;
+ t3 = t2; t2 = t1; t1 = t0;
+ }
+} /* Low */
+
+static void PR_CALLBACK High(void *arg)
+{
+ PRUint32 t3 = 0, t2 = 0, t1 = 0, t0, *tn = (PRUint32*)arg;
+ while (1)
+ {
+ PRIntervalTime timein = PR_IntervalNow();
+ PR_Sleep(oneSecond >> 2); /* 0.25 seconds */
+ t0 = PerSecond(timein);
+ *tn = (t3 + 3 * t2 + 3 * t1 + t0) / 8;
+ t3 = t2; t2 = t1; t1 = t0;
+ }
+} /* High */
+
+static void Help(void)
+{
+ PR_fprintf(
+ debug_out, "Usage: priotest [-d] [-c n]\n");
+ PR_fprintf(
+ debug_out, "-c n\tduration of test in seconds (default: %d)\n", DEFAULT_DURATION);
+ PR_fprintf(
+ debug_out, "-d\tturn on debugging output (default: FALSE)\n");
+} /* Help */
+
+static void RudimentaryTests(void)
+{
+ /*
+ ** Try some rudimentary tests like setting valid priority and
+ ** getting it back, or setting invalid priorities and getting
+ ** back a valid answer.
+ */
+ PRThreadPriority priority;
+ PR_SetThreadPriority(PR_GetCurrentThread(), PR_PRIORITY_URGENT);
+ priority = PR_GetThreadPriority(PR_GetCurrentThread());
+ failed = ((PR_TRUE == failed) || (PR_PRIORITY_URGENT != priority))
+ ? PR_TRUE : PR_FALSE;
+ if (debug_mode && (PR_PRIORITY_URGENT != priority))
+ {
+ PR_fprintf(debug_out, "PR_[S/G]etThreadPriority() failed\n");
+ }
+
+
+ PR_SetThreadPriority(
+ PR_GetCurrentThread(), (PRThreadPriority)(PR_PRIORITY_FIRST - 1));
+ priority = PR_GetThreadPriority(PR_GetCurrentThread());
+ failed = ((PR_TRUE == failed) || (PR_PRIORITY_FIRST != priority))
+ ? PR_TRUE : PR_FALSE;
+ if (debug_mode && (PR_PRIORITY_FIRST != priority))
+ {
+ PR_fprintf(debug_out, "PR_SetThreadPriority(-1) failed\n");
+ }
+
+ PR_SetThreadPriority(
+ PR_GetCurrentThread(), (PRThreadPriority)(PR_PRIORITY_LAST + 1));
+ priority = PR_GetThreadPriority(PR_GetCurrentThread());
+ failed = ((PR_TRUE == failed) || (PR_PRIORITY_LAST != priority))
+ ? PR_TRUE : PR_FALSE;
+ if (debug_mode && (PR_PRIORITY_LAST != priority))
+ {
+ PR_fprintf(debug_out, "PR_SetThreadPriority(+1) failed\n");
+ }
+
+} /* RudimentataryTests */
+
+static void CreateThreads(PRUint32 *lowCount, PRUint32 *highCount)
+{
+ (void)PR_CreateThread(
+ PR_USER_THREAD, Low, lowCount, PR_PRIORITY_LOW,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ (void)PR_CreateThread(
+ PR_USER_THREAD, High, highCount, PR_PRIORITY_HIGH,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+} /* CreateThreads */
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PLOptStatus os;
+ PRIntn duration = DEFAULT_DURATION;
+ PRUint32 totalCount, highCount = 0, lowCount = 0;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "hdc:");
+
+ debug_out = PR_STDOUT;
+ oneSecond = PR_SecondsToInterval(1);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'c': /* test duration */
+ duration = atoi(opt->value);
+ break;
+ case 'h': /* help message */
+ default:
+ Help();
+ return 2;
+ }
+ }
+ PL_DestroyOptState(opt);
+ PR_STDIO_INIT();
+
+ if (duration == 0) duration = DEFAULT_DURATION;
+
+ RudimentaryTests();
+
+ printf("Priority test: running for %d seconds\n\n", duration);
+
+ (void)PerSecond(PR_IntervalNow());
+ totalCount = PerSecond(PR_IntervalNow());
+
+ PR_SetThreadPriority(PR_GetCurrentThread(), PR_PRIORITY_URGENT);
+
+ if (debug_mode)
+ {
+ PR_fprintf(debug_out,
+ "The high priority thread should get approximately three\n");
+ PR_fprintf( debug_out,
+ "times what the low priority thread manages. A maximum of \n");
+ PR_fprintf( debug_out, "%d cycles are available.\n\n", totalCount);
+ }
+
+ duration = (duration + 4) / 5;
+ CreateThreads(&lowCount, &highCount);
+ while (duration--)
+ {
+ PRIntn loop = 5;
+ while (loop--) PR_Sleep(oneSecond);
+ if (debug_mode)
+ PR_fprintf(debug_out, "high : low :: %d : %d\n", highCount, lowCount);
+ }
+
+
+ PR_ProcessExit((failed) ? 1 : 0);
+
+ PR_ASSERT(!"You can't get here -- but you did!");
+ return 1; /* or here */
+
+} /* main */
+
+#endif /* ifdef XP_MAC */
+
+/* priotest.c */
diff --git a/pr/tests/provider.c b/pr/tests/provider.c
new file mode 100644
index 00000000..eab7f8d9
--- /dev/null
+++ b/pr/tests/provider.c
@@ -0,0 +1,1378 @@
+/* -*- 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.
+ */
+
+/*
+ *
+ * Notes:
+ * [1] lth. The call to Sleep() is a hack to get the test case to run
+ * on Windows 95. Without it, the test case fails with an error
+ * WSAECONNRESET following a recv() call. The error is caused by the
+ * server side thread termination without a shutdown() or closesocket()
+ * call. Windows docmunentation suggests that this is predicted
+ * behavior; that other platforms get away with it is ... serindipity.
+ * The test case should shutdown() or closesocket() before
+ * thread termination. I didn't have time to figure out where or how
+ * to do it. The Sleep() call inserts enough delay to allow the
+ * client side to recv() all his data before the server side thread
+ * terminates. Whew! ...
+ *
+ ** Modification History:
+ * 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+ * The debug mode will print all of the printfs associated with this test.
+ * The regress mode will be the default mode. Since the regress tool limits
+ * the output to a one line status:PASS or FAIL,all of the printf statements
+ * have been handled with an if (debug_mode) statement.
+ */
+
+#include "prclist.h"
+#include "prcvar.h"
+#include "prerror.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prio.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prtime.h"
+#include "prmem.h"
+#include "prnetdb.h"
+#include "prprf.h"
+#include "prthread.h"
+
+#include "pprio.h"
+#include "primpl.h"
+
+#include "plstr.h"
+#include "plerror.h"
+#include "plgetopt.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+
+#if defined(XP_UNIX)
+#include <math.h>
+#endif
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+#endif
+
+/*
+** This is the beginning of the test
+*/
+
+#define RECV_FLAGS 0
+#define SEND_FLAGS 0
+#define BUFFER_SIZE 1024
+#define DEFAULT_BACKLOG 5
+#define DEFAULT_PORT 13000
+#define DEFAULT_CLIENTS 1
+#define ALLOWED_IN_ACCEPT 1
+#define DEFAULT_CLIPPING 1000
+#define DEFAULT_WORKERS_MIN 1
+#define DEFAULT_WORKERS_MAX 1
+#define DEFAULT_SERVER "localhost"
+#define DEFAULT_EXECUTION_TIME 10
+#define DEFAULT_CLIENT_TIMEOUT 4000
+#define DEFAULT_SERVER_TIMEOUT 4000
+#define DEFAULT_SERVER_PRIORITY PR_PRIORITY_HIGH
+
+typedef enum CSState_e {cs_init, cs_run, cs_stop, cs_exit} CSState_t;
+
+static void PR_CALLBACK Worker(void *arg);
+typedef struct CSPool_s CSPool_t;
+typedef struct CSWorker_s CSWorker_t;
+typedef struct CSServer_s CSServer_t;
+typedef enum Verbosity
+{
+ TEST_LOG_ALWAYS,
+ TEST_LOG_ERROR,
+ TEST_LOG_WARNING,
+ TEST_LOG_NOTICE,
+ TEST_LOG_INFO,
+ TEST_LOG_STATUS,
+ TEST_LOG_VERBOSE
+} Verbosity;
+
+static enum {
+ thread_nspr, thread_pthread, thread_sproc, thread_win32
+} thread_provider;
+
+static PRInt32 domain = AF_INET;
+static PRInt32 protocol = 6; /* TCP */
+static PRFileDesc *debug_out = NULL;
+static PRBool debug_mode = PR_FALSE;
+static PRBool pthread_stats = PR_FALSE;
+static Verbosity verbosity = TEST_LOG_ALWAYS;
+static PRThreadScope thread_scope = PR_LOCAL_THREAD;
+
+struct CSWorker_s
+{
+ PRCList element; /* list of the server's workers */
+
+ PRThread *thread; /* this worker objects thread */
+ CSServer_t *server; /* back pointer to server structure */
+};
+
+struct CSPool_s
+{
+ PRCondVar *exiting;
+ PRCondVar *acceptComplete;
+ PRUint32 accepting, active, workers;
+};
+
+struct CSServer_s
+{
+ PRCList list; /* head of worker list */
+
+ PRLock *ml;
+ PRThread *thread; /* the main server thread */
+ PRCondVar *stateChange;
+
+ PRUint16 port; /* port we're listening on */
+ PRUint32 backlog; /* size of our listener backlog */
+ PRFileDesc *listener; /* the fd accepting connections */
+
+ CSPool_t pool; /* statistics on worker threads */
+ CSState_t state; /* the server's state */
+ struct /* controlling worker counts */
+ {
+ PRUint32 minimum, maximum, accepting;
+ } workers;
+
+ /* statistics */
+ PRIntervalTime started, stopped;
+ PRUint32 operations, bytesTransferred;
+};
+
+typedef struct CSDescriptor_s
+{
+ PRInt32 size; /* size of transfer */
+ char filename[60]; /* filename, null padded */
+} CSDescriptor_t;
+
+typedef struct CSClient_s
+{
+ PRLock *ml;
+ PRThread *thread;
+ PRCondVar *stateChange;
+ PRNetAddr serverAddress;
+
+ CSState_t state;
+
+ /* statistics */
+ PRIntervalTime started, stopped;
+ PRUint32 operations, bytesTransferred;
+} CSClient_t;
+
+#define TEST_LOG(l, p, a) \
+ do { \
+ if (debug_mode || (p <= verbosity)) printf a; \
+ } while (0)
+
+PRLogModuleInfo *cltsrv_log_file = NULL;
+
+#define MY_ASSERT(_expr) \
+ ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__))
+
+#define TEST_ASSERT(_expr) \
+ ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__))
+
+static void _MY_Assert(const char *s, const char *file, PRIntn ln)
+{
+ PL_PrintError(NULL);
+#if DEBUG
+ PR_Assert(s, file, ln);
+#endif
+} /* _MW_Assert */
+
+static PRBool Aborted(PRStatus rv)
+{
+ return ((PR_FAILURE == rv) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) ?
+ PR_TRUE : PR_FALSE;
+}
+
+static void TimeOfDayMessage(const char *msg, PRThread* me)
+{
+ char buffer[100];
+ PRExplodedTime tod;
+ PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &tod);
+ (void)PR_FormatTime(buffer, sizeof(buffer), "%T", &tod);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("%s(0x%p): %s\n", msg, me, buffer));
+} /* TimeOfDayMessage */
+
+
+static void PR_CALLBACK Client(void *arg)
+{
+ PRStatus rv;
+ PRIntn index;
+ char buffer[1024];
+ PRFileDesc *fd = NULL;
+ PRUintn clipping = DEFAULT_CLIPPING;
+ CSClient_t *client = (CSClient_t*)arg;
+ PRThread *me = client->thread = PR_CurrentThread();
+ CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t);
+ PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_CLIENT_TIMEOUT);
+
+
+ for (index = 0; index < sizeof(buffer); ++index)
+ buffer[index] = (char)index;
+
+ client->started = PR_IntervalNow();
+
+ PR_Lock(client->ml);
+ client->state = cs_run;
+ PR_NotifyCondVar(client->stateChange);
+ PR_Unlock(client->ml);
+
+ TimeOfDayMessage("Client started at", me);
+
+ while (cs_run == client->state)
+ {
+ PRInt32 bytes, descbytes, filebytes, netbytes;
+
+ (void)PR_NetAddrToString(&client->serverAddress, buffer, sizeof(buffer));
+ TEST_LOG(cltsrv_log_file, TEST_LOG_INFO,
+ ("\tClient(0x%p): connecting to server at %s\n", me, buffer));
+
+ fd = PR_Socket(domain, SOCK_STREAM, protocol);
+ TEST_ASSERT(NULL != fd);
+ rv = PR_Connect(fd, &client->serverAddress, timeout);
+ if (PR_FAILURE == rv)
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): conection failed\n", me));
+ goto aborted;
+ }
+
+ memset(descriptor, 0, sizeof(*descriptor));
+ descriptor->size = PR_htonl(descbytes = rand() % clipping);
+ PR_snprintf(
+ descriptor->filename, sizeof(descriptor->filename),
+ "CS%p%p-%p.dat", client->started, me, client->operations);
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tClient(0x%p): sending descriptor for %u bytes\n", me, descbytes));
+ bytes = PR_Send(
+ fd, descriptor, sizeof(*descriptor), SEND_FLAGS, timeout);
+ if (sizeof(CSDescriptor_t) != bytes)
+ {
+ if (Aborted(PR_FAILURE)) goto aborted;
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): send descriptor timeout\n", me));
+ goto retry;
+ }
+ }
+ TEST_ASSERT(sizeof(*descriptor) == bytes);
+
+ netbytes = 0;
+ while (netbytes < descbytes)
+ {
+ filebytes = sizeof(buffer);
+ if ((descbytes - netbytes) < filebytes)
+ filebytes = descbytes - netbytes;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tClient(0x%p): sending %d bytes\n", me, filebytes));
+ bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout);
+ if (filebytes != bytes)
+ {
+ if (Aborted(PR_FAILURE)) goto aborted;
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): send data timeout\n", me));
+ goto retry;
+ }
+ }
+ TEST_ASSERT(bytes == filebytes);
+ netbytes += bytes;
+ }
+ filebytes = 0;
+ while (filebytes < descbytes)
+ {
+ netbytes = sizeof(buffer);
+ if ((descbytes - filebytes) < netbytes)
+ netbytes = descbytes - filebytes;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tClient(0x%p): receiving %d bytes\n", me, netbytes));
+ bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout);
+ if (-1 == bytes)
+ {
+ if (Aborted(PR_FAILURE))
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): receive data aborted\n", me));
+ goto aborted;
+ }
+ else if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): receive data timeout\n", me));
+ else
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tClient(0x%p): receive error (%d, %d)\n",
+ me, PR_GetError(), PR_GetOSError()));
+ goto retry;
+ }
+ if (0 == bytes)
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tClient(0x%p): unexpected end of stream\n",
+ PR_CurrentThread()));
+ break;
+ }
+ filebytes += bytes;
+ }
+
+ rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
+ if (Aborted(rv)) goto aborted;
+ TEST_ASSERT(PR_SUCCESS == rv);
+retry:
+ (void)PR_Close(fd); fd = NULL;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_INFO,
+ ("\tClient(0x%p): disconnected from server\n", me));
+
+ PR_Lock(client->ml);
+ client->operations += 1;
+ client->bytesTransferred += 2 * descbytes;
+ rv = PR_WaitCondVar(client->stateChange, rand() % clipping);
+ PR_Unlock(client->ml);
+ if (Aborted(rv)) break;
+ }
+
+aborted:
+ client->stopped = PR_IntervalNow();
+
+ PR_ClearInterrupt();
+ if (NULL != fd) rv = PR_Close(fd);
+
+ PR_Lock(client->ml);
+ client->state = cs_exit;
+ PR_NotifyCondVar(client->stateChange);
+ PR_Unlock(client->ml);
+ PR_DELETE(descriptor);
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("\tClient(0x%p): stopped after %u operations and %u bytes\n",
+ PR_CurrentThread(), client->operations, client->bytesTransferred));
+
+} /* Client */
+
+static PRStatus ProcessRequest(PRFileDesc *fd, CSServer_t *server)
+{
+ PRStatus drv, rv;
+ char buffer[1024];
+ PRFileDesc *file = NULL;
+ PRThread * me = PR_CurrentThread();
+ PRInt32 bytes, descbytes, netbytes, filebytes = 0;
+ CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t);
+ PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_SERVER_TIMEOUT);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tProcessRequest(0x%p): receiving desciptor\n", me));
+ bytes = PR_Recv(
+ fd, descriptor, sizeof(*descriptor), RECV_FLAGS, timeout);
+ if (-1 == bytes)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) goto exit;
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tProcessRequest(0x%p): receive timeout\n", me));
+ }
+ goto exit;
+ }
+ if (0 == bytes)
+ {
+ rv = PR_FAILURE;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tProcessRequest(0x%p): unexpected end of file\n", me));
+ goto exit;
+ }
+ descbytes = PR_ntohl(descriptor->size);
+ TEST_ASSERT(sizeof(*descriptor) == bytes);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tProcessRequest(0x%p): read descriptor {%d, %s}\n",
+ me, descbytes, descriptor->filename));
+
+ file = PR_Open(
+ descriptor->filename, (PR_CREATE_FILE | PR_WRONLY), 0666);
+ if (NULL == file)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) goto aborted;
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\tProcessRequest(0x%p): open file timeout\n", me));
+ goto aborted;
+ }
+ }
+ TEST_ASSERT(NULL != file);
+
+ filebytes = 0;
+ while (filebytes < descbytes)
+ {
+ netbytes = sizeof(buffer);
+ if ((descbytes - filebytes) < netbytes)
+ netbytes = descbytes - filebytes;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tProcessRequest(0x%p): receive %d bytes\n", me, netbytes));
+ bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout);
+ if (-1 == bytes)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) goto aborted;
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): receive data timeout\n", me));
+ goto aborted;
+ }
+ /*
+ * XXX: I got (PR_CONNECT_RESET_ERROR, ERROR_NETNAME_DELETED)
+ * on NT here. This is equivalent to ECONNRESET on Unix.
+ * -wtc
+ */
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_WARNING,
+ ("\t\tProcessRequest(0x%p): unexpected error (%d, %d)\n",
+ me, PR_GetError(), PR_GetOSError()));
+ goto aborted;
+ }
+ if(0 == bytes)
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_WARNING,
+ ("\t\tProcessRequest(0x%p): unexpected end of stream\n", me));
+ rv = PR_FAILURE;
+ goto aborted;
+ }
+ filebytes += bytes;
+ netbytes = bytes;
+ /* The byte count for PR_Write should be positive */
+ MY_ASSERT(netbytes > 0);
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tProcessRequest(0x%p): write %d bytes to file\n", me, netbytes));
+ bytes = PR_Write(file, buffer, netbytes);
+ if (netbytes != bytes)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) goto aborted;
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): write file timeout\n", me));
+ goto aborted;
+ }
+ }
+ TEST_ASSERT(bytes > 0);
+ }
+
+ PR_Lock(server->ml);
+ server->operations += 1;
+ server->bytesTransferred += filebytes;
+ PR_Unlock(server->ml);
+
+ rv = PR_Close(file); file = NULL;
+ if (Aborted(rv)) goto aborted;
+ TEST_ASSERT(PR_SUCCESS == rv);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tProcessRequest(0x%p): opening %s\n", me, descriptor->filename));
+ file = PR_Open(descriptor->filename, PR_RDONLY, 0);
+ if (NULL == file)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) goto aborted;
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): open file timeout\n",
+ PR_CurrentThread()));
+ goto aborted;
+ }
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): other file open error (%u, %u)\n",
+ me, PR_GetError(), PR_GetOSError()));
+ goto aborted;
+ }
+ TEST_ASSERT(NULL != file);
+
+ netbytes = 0;
+ while (netbytes < descbytes)
+ {
+ filebytes = sizeof(buffer);
+ if ((descbytes - netbytes) < filebytes)
+ filebytes = descbytes - netbytes;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tProcessRequest(0x%p): read %d bytes from file\n", me, filebytes));
+ bytes = PR_Read(file, buffer, filebytes);
+ if (filebytes != bytes)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) goto aborted;
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): read file timeout\n", me));
+ else
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): other file error (%d, %d)\n",
+ me, PR_GetError(), PR_GetOSError()));
+ goto aborted;
+ }
+ TEST_ASSERT(bytes > 0);
+ netbytes += bytes;
+ filebytes = bytes;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tProcessRequest(0x%p): sending %d bytes\n", me, filebytes));
+ bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout);
+ if (filebytes != bytes)
+ {
+ rv = PR_FAILURE;
+ if (Aborted(rv)) goto aborted;
+ if (PR_IO_TIMEOUT_ERROR == PR_GetError())
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tProcessRequest(0x%p): send data timeout\n", me));
+ goto aborted;
+ }
+ break;
+ }
+ TEST_ASSERT(bytes > 0);
+ }
+
+ PR_Lock(server->ml);
+ server->bytesTransferred += filebytes;
+ PR_Unlock(server->ml);
+
+ rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
+ if (Aborted(rv)) goto aborted;
+
+ rv = PR_Close(file); file = NULL;
+ if (Aborted(rv)) goto aborted;
+ TEST_ASSERT(PR_SUCCESS == rv);
+
+aborted:
+ PR_ClearInterrupt();
+ if (NULL != file) PR_Close(file);
+ drv = PR_Delete(descriptor->filename);
+ TEST_ASSERT(PR_SUCCESS == drv);
+exit:
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tProcessRequest(0x%p): Finished\n", me));
+
+ PR_DELETE(descriptor);
+
+#if defined(WIN95)
+ PR_Sleep(PR_MillisecondsToInterval(200)); /* lth. see note [1] */
+#endif
+ return rv;
+} /* ProcessRequest */
+
+typedef void (*StartFn)(void*);
+typedef struct StartObject
+{
+ StartFn start;
+ void *arg;
+} StartObject;
+
+#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
+#include "md/_pth.h"
+#include <pthread.h>
+
+static void *pthread_start(void *arg)
+{
+ StartObject *so = (StartObject*)arg;
+ StartFn start = so->start;
+ void *data = so->arg;
+ PR_Free(so);
+ start(data);
+ return NULL;
+} /* pthread_start */
+#endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
+
+#if defined(IRIX) && !defined(_PR_PTHREADS)
+#include <sys/types.h>
+#include <sys/prctl.h>
+static void sproc_start(void *arg, PRSize size)
+{
+ StartObject *so = (StartObject*)arg;
+ StartFn start = so->start;
+ void *data = so->arg;
+ PR_Free(so);
+ start(data);
+} /* sproc_start */
+#endif /* defined(IRIX) && !defined(_PR_PTHREADS) */
+
+#if defined(WIN32)
+#include <process.h> /* for _beginthreadex() */
+
+static PRUintn __stdcall windows_start(void *arg)
+{
+ StartObject *so = (StartObject*)arg;
+ StartFn start = so->start;
+ void *data = so->arg;
+ PR_Free(so);
+ start(data);
+ return 0;
+} /* windows_start */
+#endif /* defined(WIN32) */
+
+static PRStatus JoinThread(PRThread *thread)
+{
+ PRStatus rv;
+ switch (thread_provider)
+ {
+ case thread_nspr:
+ rv = PR_JoinThread(thread);
+ break;
+ case thread_pthread:
+#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
+ rv = PR_SUCCESS;
+ break;
+#endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
+ case thread_win32:
+#if defined(WIN32)
+ rv = PR_SUCCESS;
+ break;
+#endif
+ default:
+ rv = PR_FAILURE;
+ break;
+ }
+ return rv;
+} /* JoinThread */
+
+static PRStatus NewThread(
+ StartFn start, void *arg, PRThreadPriority prio, PRThreadState state)
+{
+ PRStatus rv;
+
+ switch (thread_provider)
+ {
+ case thread_nspr:
+ {
+ PRThread *thread = PR_CreateThread(
+ PR_USER_THREAD, start, arg,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD, 0);
+ rv = (NULL == thread) ? PR_FAILURE : PR_SUCCESS;
+ }
+ break;
+ case thread_pthread:
+#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
+ {
+ int rv;
+ pthread_t id;
+ pthread_attr_t tattr;
+ StartObject *start_object;
+ start_object = PR_NEW(StartObject);
+ PR_ASSERT(NULL != start_object);
+ start_object->start = start;
+ start_object->arg = arg;
+
+ rv = PTHREAD_ATTR_INIT(&tattr);
+ PR_ASSERT(0 == rv);
+
+ rv = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
+ PR_ASSERT(0 == rv);
+
+#if !defined(LINUX)
+ rv = pthread_attr_setstacksize(&tattr, 64 * 1024);
+ PR_ASSERT(0 == rv);
+#endif
+
+ rv = PTHREAD_CREATE(&id, tattr, pthread_start, start_object);
+ (void)PTHREAD_ATTR_DESTROY(&tattr);
+ return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
+ }
+#else
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ rv = PR_FAILURE;
+#endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
+ break;
+
+ case thread_sproc:
+#if defined(IRIX) && !defined(_PR_PTHREADS)
+ {
+ PRInt32 pid;
+ StartObject *start_object;
+ start_object = PR_NEW(StartObject);
+ PR_ASSERT(NULL != start_object);
+ start_object->start = start;
+ start_object->arg = arg;
+ pid = sprocsp(
+ sproc_start, PR_SALL, start_object, NULL, 64 * 1024);
+ rv = (0 < pid) ? PR_SUCCESS : PR_FAILURE;
+ }
+#else
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ rv = PR_FAILURE;
+#endif /* defined(IRIX) && !defined(_PR_PTHREADS) */
+ break;
+ case thread_win32:
+#if defined(WIN32)
+ {
+ void *th;
+ PRUintn id;
+ StartObject *start_object;
+ start_object = PR_NEW(StartObject);
+ PR_ASSERT(NULL != start_object);
+ start_object->start = start;
+ start_object->arg = arg;
+ th = (void*)_beginthreadex(
+ NULL, /* LPSECURITY_ATTRIBUTES - pointer to thread security attributes */
+ 0U, /* DWORD - initial thread stack size, in bytes */
+ windows_start, /* LPTHREAD_START_ROUTINE - pointer to thread function */
+ start_object, /* LPVOID - argument for new thread */
+ 0U, /*DWORD dwCreationFlags - creation flags */
+ &id /* LPDWORD - pointer to returned thread identifier */ );
+
+ rv = (NULL == th) ? PR_FAILURE : PR_SUCCESS;
+ }
+#else
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ rv = PR_FAILURE;
+#endif
+ break;
+ default:
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ rv = PR_FAILURE;
+ }
+ return rv;
+} /* NewThread */
+
+static PRStatus CreateWorker(CSServer_t *server, CSPool_t *pool)
+{
+ PRStatus rv;
+ CSWorker_t *worker = PR_NEWZAP(CSWorker_t);
+ worker->server = server;
+ PR_INIT_CLIST(&worker->element);
+ rv = NewThread(
+ Worker, worker, DEFAULT_SERVER_PRIORITY, PR_UNJOINABLE_THREAD);
+ if (PR_FAILURE == rv) PR_DELETE(worker);
+
+ TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS,
+ ("\tCreateWorker(0x%p): create new worker (0x%p)\n",
+ PR_CurrentThread(), worker->thread));
+
+ return rv;
+} /* CreateWorker */
+
+static void PR_CALLBACK Worker(void *arg)
+{
+ PRStatus rv;
+ PRNetAddr from;
+ PRFileDesc *fd = NULL;
+ CSWorker_t *worker = (CSWorker_t*)arg;
+ CSServer_t *server = worker->server;
+ CSPool_t *pool = &server->pool;
+
+ PRThread *me = worker->thread = PR_CurrentThread();
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("\t\tWorker(0x%p): started [%u]\n", me, pool->workers + 1));
+
+ PR_Lock(server->ml);
+ PR_APPEND_LINK(&worker->element, &server->list);
+ pool->workers += 1; /* define our existance */
+
+ while (cs_run == server->state)
+ {
+ while (pool->accepting >= server->workers.accepting)
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tWorker(0x%p): waiting for accept slot[%d]\n",
+ me, pool->accepting));
+ rv = PR_WaitCondVar(pool->acceptComplete, PR_INTERVAL_NO_TIMEOUT);
+ if (Aborted(rv) || (cs_run != server->state))
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("\tWorker(0x%p): has been %s\n",
+ me, (Aborted(rv) ? "interrupted" : "stopped")));
+ goto exit;
+ }
+ }
+ pool->accepting += 1; /* how many are really in accept */
+ PR_Unlock(server->ml);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\t\tWorker(0x%p): calling accept\n", me));
+ fd = PR_Accept(server->listener, &from, PR_INTERVAL_NO_TIMEOUT);
+
+ PR_Lock(server->ml);
+ pool->accepting -= 1;
+ PR_NotifyCondVar(pool->acceptComplete);
+
+ if ((NULL == fd) && Aborted(PR_FAILURE))
+ {
+ if (NULL != server->listener)
+ {
+ PR_Close(server->listener);
+ server->listener = NULL;
+ }
+ goto exit;
+ }
+
+ if (NULL != fd)
+ {
+ /*
+ ** Create another worker of the total number of workers is
+ ** less than the minimum specified or we have none left in
+ ** accept() AND we're not over the maximum.
+ ** This sort of presumes that the number allowed in accept
+ ** is at least as many as the minimum. Otherwise we'll keep
+ ** creating new threads and deleting them soon after.
+ */
+ PRBool another =
+ ((pool->workers < server->workers.minimum) ||
+ ((0 == pool->accepting)
+ && (pool->workers < server->workers.maximum))) ?
+ PR_TRUE : PR_FALSE;
+ pool->active += 1;
+ PR_Unlock(server->ml);
+
+ if (another) (void)CreateWorker(server, pool);
+
+ rv = ProcessRequest(fd, server);
+ if (PR_SUCCESS != rv)
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ERROR,
+ ("\t\tWorker(0x%p): server process ended abnormally\n", me));
+ (void)PR_Close(fd); fd = NULL;
+
+ PR_Lock(server->ml);
+ pool->active -= 1;
+ }
+ }
+
+exit:
+ PR_ClearInterrupt();
+ PR_Unlock(server->ml);
+
+ if (NULL != fd)
+ {
+ (void)PR_Shutdown(fd, PR_SHUTDOWN_BOTH);
+ (void)PR_Close(fd);
+ }
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("\t\tWorker(0x%p): exiting [%u]\n", PR_CurrentThread(), pool->workers));
+
+ PR_Lock(server->ml);
+ pool->workers -= 1; /* undefine our existance */
+ PR_REMOVE_AND_INIT_LINK(&worker->element);
+ PR_NotifyCondVar(pool->exiting);
+ PR_Unlock(server->ml);
+
+ PR_DELETE(worker); /* destruction of the "worker" object */
+
+} /* Worker */
+
+static void PR_CALLBACK Server(void *arg)
+{
+ PRStatus rv;
+ PRNetAddr serverAddress;
+ CSServer_t *server = (CSServer_t*)arg;
+ PRThread *me = server->thread = PR_CurrentThread();
+
+ server->listener = PR_Socket(domain, SOCK_STREAM, protocol);
+
+ rv = PR_InitializeNetAddr(PR_IpAddrAny, DEFAULT_PORT, &serverAddress);
+
+ rv = PR_Bind(server->listener, &serverAddress);
+ TEST_ASSERT(PR_SUCCESS == rv);
+
+ rv = PR_Listen(server->listener, server->backlog);
+ TEST_ASSERT(PR_SUCCESS == rv);
+
+ server->started = PR_IntervalNow();
+ TimeOfDayMessage("Server started at", me);
+
+ PR_Lock(server->ml);
+ server->state = cs_run;
+ PR_NotifyCondVar(server->stateChange);
+ PR_Unlock(server->ml);
+
+ /*
+ ** Create the first worker (actually, a thread that accepts
+ ** connections and then processes the work load as needed).
+ ** From this point on, additional worker threads are created
+ ** as they are needed by existing worker threads.
+ */
+ rv = CreateWorker(server, &server->pool);
+ TEST_ASSERT(PR_SUCCESS == rv);
+
+ /*
+ ** From here on this thread is merely hanging around as the contact
+ ** point for the main test driver. It's just waiting for the driver
+ ** to declare the test complete.
+ */
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tServer(0x%p): waiting for state change\n", me));
+
+ PR_Lock(server->ml);
+ while ((cs_run == server->state) && !Aborted(rv))
+ {
+ rv = PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(server->ml);
+ PR_ClearInterrupt();
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_INFO,
+ ("\tServer(0x%p): shutting down workers\n", me));
+
+ /*
+ ** Get all the worker threads to exit. They know how to
+ ** clean up after themselves, so this is just a matter of
+ ** waiting for clorine in the pool to take effect. During
+ ** this stage we're ignoring interrupts.
+ */
+ server->workers.minimum = server->workers.maximum = 0;
+
+ PR_Lock(server->ml);
+ while (!PR_CLIST_IS_EMPTY(&server->list))
+ {
+ PRCList *head = PR_LIST_HEAD(&server->list);
+ CSWorker_t *worker = (CSWorker_t*)head;
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("\tServer(0x%p): interrupting worker(0x%p)\n", me, worker));
+ rv = PR_Interrupt(worker->thread);
+ TEST_ASSERT(PR_SUCCESS == rv);
+ PR_REMOVE_AND_INIT_LINK(head);
+ }
+
+ while (server->pool.workers > 0)
+ {
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("\tServer(0x%p): waiting for %u workers to exit\n",
+ me, server->pool.workers));
+ (void)PR_WaitCondVar(server->pool.exiting, PR_INTERVAL_NO_TIMEOUT);
+ }
+
+ server->state = cs_exit;
+ PR_NotifyCondVar(server->stateChange);
+ PR_Unlock(server->ml);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("\tServer(0x%p): stopped after %u operations and %u bytes\n",
+ me, server->operations, server->bytesTransferred));
+
+ if (NULL != server->listener) PR_Close(server->listener);
+ server->stopped = PR_IntervalNow();
+
+} /* Server */
+
+static void WaitForCompletion(PRIntn execution)
+{
+ while (execution > 0)
+ {
+ PRIntn dally = (execution > 30) ? 30 : execution;
+ PR_Sleep(PR_SecondsToInterval(dally));
+ if (pthread_stats) PT_FPrintStats(debug_out, "\nPThread Statistics\n");
+ execution -= dally;
+ }
+} /* WaitForCompletion */
+
+static void Help(void)
+{
+ PR_fprintf(debug_out, "cltsrv test program usage:\n");
+ PR_fprintf(debug_out, "\t-a <n> threads allowed in accept (5)\n");
+ PR_fprintf(debug_out, "\t-b <n> backlock for listen (5)\n");
+ PR_fprintf(debug_out, "\t-c <threads> number of clients to create (1)\n");
+ PR_fprintf(debug_out, "\t-w <threads> minimal number of server threads (1)\n");
+ PR_fprintf(debug_out, "\t-W <threads> maximum number of server threads (1)\n");
+ PR_fprintf(debug_out, "\t-e <seconds> duration of the test in seconds (10)\n");
+ PR_fprintf(debug_out, "\t-s <string> dsn name of server (localhost)\n");
+ PR_fprintf(debug_out, "\t-G use GLOBAL threads (LOCAL)\n");
+ PR_fprintf(debug_out, "\t-T <string> thread provider ('n' | 'p' | 'w')(n)\n");
+ PR_fprintf(debug_out, "\t-X use XTP as transport (TCP)\n");
+#ifdef _PR_INET6
+ PR_fprintf(debug_out, "\t-6 Use IPv6 (IPv4)\n");
+#endif /* _PR_INET6 */
+ PR_fprintf(debug_out, "\t-v verbosity (accumulative) (0)\n");
+ PR_fprintf(debug_out, "\t-p pthread statistics (FALSE)\n");
+ PR_fprintf(debug_out, "\t-d debug mode (FALSE)\n");
+ PR_fprintf(debug_out, "\t-h this message\n");
+} /* Help */
+
+static Verbosity IncrementVerbosity(void)
+{
+ PRIntn verboge = (PRIntn)verbosity + 1;
+ return (Verbosity)verboge;
+} /* IncrementVerbosity */
+
+PRIntn main(PRIntn argc, char** argv)
+{
+ PRUintn index;
+ PRBool boolean;
+ CSClient_t *client;
+ PRStatus rv, joinStatus;
+ CSServer_t *server = NULL;
+ char *thread_type;
+
+ PRUintn backlog = DEFAULT_BACKLOG;
+ PRUintn clients = DEFAULT_CLIENTS;
+ const char *serverName = DEFAULT_SERVER;
+ PRBool serverIsLocal = PR_TRUE;
+ PRUintn accepting = ALLOWED_IN_ACCEPT;
+ PRUintn workersMin = DEFAULT_WORKERS_MIN;
+ PRUintn workersMax = DEFAULT_WORKERS_MAX;
+ PRIntn execution = DEFAULT_EXECUTION_TIME;
+
+ /*
+ * -G use global threads
+ * -a <n> threads allowed in accept
+ * -b <n> backlock for listen
+ * -c <threads> number of clients to create
+ * -w <threads> minimal number of server threads
+ * -W <threads> maximum number of server threads
+ * -e <seconds> duration of the test in seconds
+ * -s <string> dsn name of server (implies no server here)
+ * -v verbosity
+ */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "GX6b:a:c:w:W:e:s:T:vdhp");
+
+#if defined(WIN32)
+ thread_provider = thread_win32;
+#elif defined(_PR_PTHREADS)
+ thread_provider = thread_pthread;
+#elif defined(IRIX)
+ thread_provider = thread_sproc;
+#else
+ thread_provider = thread_nspr;
+#endif
+
+ debug_out = PR_GetSpecialFD(PR_StandardError);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'G': /* use global threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'X': /* use XTP as transport */
+ protocol = 36;
+ break;
+#ifdef _PR_INET6
+ case '6': /* Use IPv6 */
+ domain = AF_INET6;
+ PR_SetIPv6Enable(PR_TRUE);
+ break;
+#endif /* _PR_INET6 */
+ case 'a': /* the value for accepting */
+ accepting = atoi(opt->value);
+ break;
+ case 'b': /* the value for backlock */
+ backlog = atoi(opt->value);
+ break;
+ case 'T': /* the thread provider */
+ if ('n' == *opt->value) thread_provider = thread_nspr;
+ else if ('p' == *opt->value) thread_provider = thread_pthread;
+ else if ('w' == *opt->value) thread_provider = thread_win32;
+ else {Help(); return 2; }
+ break;
+ case 'c': /* number of client threads */
+ clients = atoi(opt->value);
+ break;
+ case 'w': /* minimum server worker threads */
+ workersMin = atoi(opt->value);
+ break;
+ case 'W': /* maximum server worker threads */
+ workersMax = atoi(opt->value);
+ break;
+ case 'e': /* program execution time in seconds */
+ execution = atoi(opt->value);
+ break;
+ case 's': /* server's address */
+ serverName = opt->value;
+ break;
+ case 'v': /* verbosity */
+ verbosity = IncrementVerbosity();
+ break;
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'p': /* pthread mode */
+ pthread_stats = PR_TRUE;
+ break;
+ case 'h':
+ default:
+ Help();
+ return 2;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (0 != PL_strcmp(serverName, DEFAULT_SERVER)) serverIsLocal = PR_FALSE;
+ if (0 == execution) execution = DEFAULT_EXECUTION_TIME;
+ if (0 == workersMax) workersMax = DEFAULT_WORKERS_MAX;
+ if (0 == workersMin) workersMin = DEFAULT_WORKERS_MIN;
+ if (0 == accepting) accepting = ALLOWED_IN_ACCEPT;
+ if (0 == backlog) backlog = DEFAULT_BACKLOG;
+
+ if (workersMin > accepting) accepting = workersMin;
+
+ PR_STDIO_INIT();
+ TimeOfDayMessage("Client/Server started at", PR_CurrentThread());
+
+ cltsrv_log_file = PR_NewLogModule("cltsrv_log");
+ MY_ASSERT(NULL != cltsrv_log_file);
+ boolean = PR_SetLogFile("cltsrv.log");
+ MY_ASSERT(boolean);
+
+#ifdef XP_MAC
+ debug_mode = PR_TRUE;
+#endif
+
+ if (serverIsLocal)
+ {
+ /* Establish the server */
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_INFO,
+ ("main(0x%p): starting server\n", PR_CurrentThread()));
+
+ server = PR_NEWZAP(CSServer_t);
+ PR_INIT_CLIST(&server->list);
+ server->state = cs_init;
+ server->ml = PR_NewLock();
+ server->backlog = backlog;
+ server->port = DEFAULT_PORT;
+ server->workers.minimum = workersMin;
+ server->workers.maximum = workersMax;
+ server->workers.accepting = accepting;
+ server->stateChange = PR_NewCondVar(server->ml);
+ server->pool.exiting = PR_NewCondVar(server->ml);
+ server->pool.acceptComplete = PR_NewCondVar(server->ml);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("main(0x%p): creating server thread\n", PR_CurrentThread()));
+
+ rv = NewThread(
+ Server, server, PR_PRIORITY_HIGH, PR_JOINABLE_THREAD);
+ TEST_ASSERT(PR_SUCCESS == rv);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("main(0x%p): waiting for server init\n", PR_CurrentThread()));
+
+ PR_Lock(server->ml);
+ while (server->state == cs_init)
+ PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
+ PR_Unlock(server->ml);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("main(0x%p): server init complete (port #%d)\n",
+ PR_CurrentThread(), server->port));
+ }
+
+ if (clients != 0)
+ {
+ /* Create all of the clients */
+ PRHostEnt host;
+ char buffer[BUFFER_SIZE];
+ client = (CSClient_t*)PR_CALLOC(clients * sizeof(CSClient_t));
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("main(0x%p): creating %d client threads\n",
+ PR_CurrentThread(), clients));
+
+ if (!serverIsLocal)
+ {
+ rv = PR_GetHostByName(serverName, buffer, BUFFER_SIZE, &host);
+ if (PR_SUCCESS != rv)
+ {
+ PL_FPrintError(PR_STDERR, "PR_GetHostByName");
+ return 2;
+ }
+ }
+
+ for (index = 0; index < clients; ++index)
+ {
+ client[index].state = cs_init;
+ client[index].ml = PR_NewLock();
+ if (serverIsLocal)
+ {
+ (void)PR_InitializeNetAddr(
+ PR_IpAddrLoopback, DEFAULT_PORT,
+ &client[index].serverAddress);
+ }
+ else
+ {
+ (void)PR_EnumerateHostEnt(
+ 0, &host, DEFAULT_PORT, &client[index].serverAddress);
+ }
+ client[index].stateChange = PR_NewCondVar(client[index].ml);
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_INFO,
+ ("main(0x%p): creating client threads\n", PR_CurrentThread()));
+ rv = NewThread(
+ Client, &client[index], PR_PRIORITY_NORMAL, PR_JOINABLE_THREAD);
+ TEST_ASSERT(PR_SUCCESS == rv);
+ PR_Lock(client[index].ml);
+ while (cs_init == client[index].state)
+ PR_WaitCondVar(client[index].stateChange, PR_INTERVAL_NO_TIMEOUT);
+ PR_Unlock(client[index].ml);
+ }
+ }
+
+ /* Then just let them go at it for a bit */
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("main(0x%p): waiting for execution interval (%d seconds)\n",
+ PR_CurrentThread(), execution));
+
+ WaitForCompletion(execution);
+
+ TimeOfDayMessage("Shutting down", PR_CurrentThread());
+
+ if (clients != 0)
+ {
+ for (index = 0; index < clients; ++index)
+ {
+ TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS,
+ ("main(0x%p): notifying client(0x%p) to stop\n",
+ PR_CurrentThread(), client[index].thread));
+
+ PR_Lock(client[index].ml);
+ if (cs_run == client[index].state)
+ {
+ client[index].state = cs_stop;
+ PR_Interrupt(client[index].thread);
+ while (cs_stop == client[index].state)
+ PR_WaitCondVar(
+ client[index].stateChange, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(client[index].ml);
+
+ TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE,
+ ("main(0x%p): joining client(0x%p)\n",
+ PR_CurrentThread(), client[index].thread));
+
+ joinStatus = JoinThread(client[index].thread);
+ TEST_ASSERT(PR_SUCCESS == joinStatus);
+ PR_DestroyCondVar(client[index].stateChange);
+ PR_DestroyLock(client[index].ml);
+ }
+ PR_DELETE(client);
+ }
+
+ if (NULL != server)
+ {
+ /* All clients joined - retrieve the server */
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("main(0x%p): notifying server(0x%p) to stop\n",
+ PR_CurrentThread(), server->thread));
+
+ PR_Lock(server->ml);
+ server->state = cs_stop;
+ PR_Interrupt(server->thread);
+ while (cs_exit != server->state)
+ PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT);
+ PR_Unlock(server->ml);
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_NOTICE,
+ ("main(0x%p): joining server(0x%p)\n",
+ PR_CurrentThread(), server->thread));
+ joinStatus = JoinThread(server->thread);
+ TEST_ASSERT(PR_SUCCESS == joinStatus);
+
+ PR_DestroyCondVar(server->stateChange);
+ PR_DestroyCondVar(server->pool.exiting);
+ PR_DestroyCondVar(server->pool.acceptComplete);
+ PR_DestroyLock(server->ml);
+ PR_DELETE(server);
+ }
+
+ TEST_LOG(
+ cltsrv_log_file, TEST_LOG_ALWAYS,
+ ("main(0x%p): test complete\n", PR_CurrentThread()));
+
+ if (thread_provider == thread_win32)
+ thread_type = "\nWin32 Thread Statistics\n";
+ else if (thread_provider == thread_pthread)
+ thread_type = "\npthread Statistics\n";
+ else if (thread_provider == thread_sproc)
+ thread_type = "\nsproc Statistics\n";
+ else {
+ PR_ASSERT(thread_provider == thread_nspr);
+ thread_type = "\nPRThread Statistics\nn";
+ }
+
+ PT_FPrintStats(debug_out, thread_type);
+
+ TimeOfDayMessage("Test exiting at", PR_CurrentThread());
+ return 0;
+} /* main */
+
+/* cltsrv.c */
diff --git a/pr/tests/prpoll.c b/pr/tests/prpoll.c
new file mode 100644
index 00000000..e59effa4
--- /dev/null
+++ b/pr/tests/prpoll.c
@@ -0,0 +1,273 @@
+/* -*- 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 "prinit.h"
+#include "prio.h"
+#include "prlog.h"
+#include "prprf.h"
+#include "prnetdb.h"
+
+#ifndef XP_MAC
+#include "private/pprio.h"
+#else
+#include "pprio.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+static void
+clientThreadFunc(void *arg)
+{
+ PRUint16 port = (PRUint16) arg;
+ PRFileDesc *sock;
+ PRNetAddr addr;
+ char buf[128];
+ int i;
+
+ addr.inet.family = AF_INET;
+ addr.inet.port = PR_htons(port);
+ addr.inet.ip = PR_htonl(INADDR_LOOPBACK);
+ PR_snprintf(buf, sizeof(buf), "%hu", port);
+
+ for (i = 0; i < 5; i++) {
+ sock = PR_NewTCPSocket();
+ PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT);
+
+ PR_Write(sock, buf, sizeof(buf));
+ PR_Close(sock);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *listenSock1, *listenSock2;
+ PRFileDesc *badFD;
+ PRUint16 listenPort1, listenPort2;
+ PRNetAddr addr;
+ char buf[128];
+ PRThread *clientThread;
+ PRPollDesc pds0[10], pds1[10], *pds, *other_pds;
+ PRIntn npds;
+ PRInt32 retVal;
+ PRIntn i, j;
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ printf("This program tests PR_Poll with sockets.\n");
+ printf("Timeout, error reporting, and normal operation are tested.\n\n");
+
+ /* Create two listening sockets */
+ if ((listenSock1 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ exit(1);
+ }
+ addr.inet.family = AF_INET;
+ addr.inet.ip = PR_htonl(INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ exit(1);
+ }
+ if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ exit(1);
+ }
+ listenPort1 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock1, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ exit(1);
+ }
+
+ if ((listenSock2 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ exit(1);
+ }
+ addr.inet.family = AF_INET;
+ addr.inet.ip = PR_htonl(INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ exit(1);
+ }
+ if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ exit(1);
+ }
+ listenPort2 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock2, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ exit(1);
+ }
+ PR_snprintf(buf, sizeof(buf),
+ "The server thread is listening on ports %hu and %hu\n\n",
+ listenPort1, listenPort2);
+ printf("%s", buf);
+
+ /* Set up the poll descriptor array */
+ pds = pds0;
+ other_pds = pds1;
+ memset(pds, 0, sizeof(pds));
+ pds[0].fd = listenSock1;
+ pds[0].in_flags = PR_POLL_READ;
+ pds[1].fd = listenSock2;
+ pds[1].in_flags = PR_POLL_READ;
+ npds = 2;
+
+ /* Testing timeout */
+ printf("PR_Poll should time out in 5 seconds\n");
+ retVal = PR_Poll(pds, npds, PR_SecondsToInterval(5));
+ if (retVal != 0) {
+ PR_snprintf(buf, sizeof(buf),
+ "PR_Poll should time out and return 0, but it returns %ld\n",
+ retVal);
+ fprintf(stderr, "%s", buf);
+ exit(1);
+ }
+ printf("PR_Poll timed out. Test passed.\n\n");
+
+ /* Testing bad fd */
+ printf("PR_Poll should detect a bad file descriptor\n");
+ if ((badFD = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a TCP socket\n");
+ exit(1);
+ }
+
+ pds[2].fd = badFD;
+ pds[2].in_flags = PR_POLL_READ;
+ npds = 3;
+ PR_Close(badFD); /* make the fd bad */
+ retVal = PR_Poll(pds, npds, PR_INTERVAL_NO_TIMEOUT);
+ if (retVal != 1 || (unsigned short) pds[2].out_flags != PR_POLL_NVAL) {
+ fprintf(stderr, "Failed to detect the bad fd: "
+ "PR_Poll returns %d, out_flags is 0x%hx\n",
+ retVal, pds[2].out_flags);
+ exit(1);
+ }
+ printf("PR_Poll detected the bad fd. Test passed.\n\n");
+ npds = 2;
+
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ clientThreadFunc, (void *) listenPort1,
+ PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+ if (clientThread == NULL) {
+ fprintf(stderr, "can't create thread\n");
+ exit(1);
+ }
+
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ clientThreadFunc, (void *) listenPort2,
+ PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+ if (clientThread == NULL) {
+ fprintf(stderr, "can't create thread\n");
+ exit(1);
+ }
+
+ printf("Two client threads are created. Each of them will\n");
+ printf("send data to one of the two ports the server is listening on.\n");
+ printf("The data they send is the port number. Each of them send\n");
+ printf("the data five times, so you should see ten lines below,\n");
+ printf("interleaved in an arbitrary order.\n");
+
+ /* 20 events total */
+ i = 0;
+ while (i < 20) {
+ PRPollDesc *tmp;
+ int nextIndex;
+ int nEvents = 0;
+
+ retVal = PR_Poll(pds, npds, PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(retVal != 0); /* no timeout */
+ if (retVal == -1) {
+ fprintf(stderr, "PR_Poll failed\n");
+ exit(1);
+ }
+
+ nextIndex = 2;
+ /* the two listening sockets */
+ for (j = 0; j < 2; j++) {
+ other_pds[j] = pds[j];
+ PR_ASSERT((pds[j].out_flags & PR_POLL_WRITE) == 0
+ && (pds[j].out_flags & PR_POLL_EXCEPT) == 0);
+ if (pds[j].out_flags & PR_POLL_READ) {
+ PRFileDesc *sock;
+
+ nEvents++;
+ sock = PR_Accept(pds[j].fd, NULL, PR_INTERVAL_NO_TIMEOUT);
+ if (sock == NULL) {
+ fprintf(stderr, "PR_Accept() failed\n");
+ exit(1);
+ }
+ other_pds[nextIndex].fd = sock;
+ other_pds[nextIndex].in_flags = PR_POLL_READ;
+ nextIndex++;
+ } else if (pds[j].out_flags & PR_POLL_ERR) {
+ fprintf(stderr, "PR_Poll() indicates that an fd has error\n");
+ exit(1);
+ } else if (pds[j].out_flags & PR_POLL_NVAL) {
+ fprintf(stderr, "PR_Poll() indicates that fd %d is invalid\n",
+ PR_FileDesc2NativeHandle(pds[j].fd));
+ exit(1);
+ }
+ }
+
+ for (j = 2; j < npds; j++) {
+ PR_ASSERT((pds[j].out_flags & PR_POLL_WRITE) == 0
+ && (pds[j].out_flags & PR_POLL_EXCEPT) == 0);
+ if (pds[j].out_flags & PR_POLL_READ) {
+ PRInt32 nBytes;
+
+ nEvents++;
+ nBytes = PR_Read(pds[j].fd, buf, sizeof(buf));
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Read() failed\n");
+ exit(1);
+ }
+ /* Just to be safe */
+ buf[127] = '\0';
+ PR_Close(pds[j].fd);
+ printf("The server received \"%s\" from a client\n", buf);
+ } else if (pds[j].out_flags & PR_POLL_ERR) {
+ fprintf(stderr, "PR_Poll() indicates that an fd has error\n");
+ exit(1);
+ } else if (pds[j].out_flags & PR_POLL_NVAL) {
+ fprintf(stderr, "PR_Poll() indicates that an fd is invalid\n");
+ exit(1);
+ } else {
+ other_pds[nextIndex] = pds[j];
+ nextIndex++;
+ }
+ }
+
+ PR_ASSERT(retVal == nEvents);
+ /* swap */
+ tmp = pds;
+ pds = other_pds;
+ other_pds = tmp;
+ npds = nextIndex;
+ i += nEvents;
+ }
+
+ printf("All tests finished\n");
+ PR_Cleanup();
+ return 0;
+}
diff --git a/pr/tests/prselect.c b/pr/tests/prselect.c
new file mode 100644
index 00000000..6e19fa60
--- /dev/null
+++ b/pr/tests/prselect.c
@@ -0,0 +1,353 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+** 1997 - Netscape Communications Corporation
+**
+** Name: prselect_err.c
+**
+** Description: tests PR_Select with sockets Error condition functions.
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+#include "prttools.h"
+
+
+#include "prinit.h"
+#include "prio.h"
+#include "prlog.h"
+#include "prprf.h"
+#include "prerror.h"
+#include "prnetdb.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/***********************************************************************
+** PRIVATE FUNCTION: Test_Result
+** DESCRIPTION: Used in conjunction with the regress tool, prints out the
+** status of the test case.
+** INPUTS: PASS/FAIL
+** OUTPUTS: None
+** RETURN: None
+** SIDE EFFECTS:
+**
+** RESTRICTIONS:
+** None
+** MEMORY: NA
+** ALGORITHM: Determine what the status is and print accordingly.
+**
+***********************************************************************/
+
+
+static Test_Result (int result)
+{
+ if (result == PASS)
+ printf ("PASS\n");
+ else
+ printf ("FAIL\n");
+}
+
+static void
+clientThreadFunc(void *arg)
+{
+ PRUint16 port = (PRUint16) arg;
+ PRFileDesc *sock;
+ PRNetAddr addr;
+ char buf[128];
+ int i;
+
+ addr.inet.family = AF_INET;
+ addr.inet.port = PR_htons(port);
+ addr.inet.ip = PR_htonl(INADDR_LOOPBACK);
+ PR_snprintf(buf, sizeof(buf), "%hu", port);
+
+ for (i = 0; i < 5; i++) {
+ sock = PR_NewTCPSocket();
+ PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT);
+ PR_Write(sock, buf, sizeof(buf));
+ PR_Close(sock);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *listenSock1, *listenSock2;
+ PRFileDesc *badFD;
+ PRFileDesc *fds0[10], *fds1[10], **fds, **other_fds;
+ PRIntn nfds;
+ PRUint16 listenPort1, listenPort2;
+ PRNetAddr addr;
+ PR_fd_set readFdSet;
+ char buf[128];
+ PRThread *clientThread;
+ PRInt32 retVal;
+ PRIntn i, j;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ if (debug_mode) {
+ printf("This program tests PR_Select with sockets. Timeout, error\n");
+ printf("reporting, and normal operation are tested.\n\n");
+ }
+
+ /* Create two listening sockets */
+ if ((listenSock1 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ if (!debug_mode) Test_Result(FAIL);
+ exit(1);
+ }
+ addr.inet.family = AF_INET;
+ addr.inet.ip = PR_htonl(INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ if (!debug_mode) Test_Result(FAIL);
+ exit(1);
+ }
+ if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ if (!debug_mode) Test_Result(FAIL);
+ exit(1);
+ }
+ listenPort1 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock1, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ if (!debug_mode) Test_Result(FAIL);
+ exit(1);
+ }
+
+ if ((listenSock2 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ if (!debug_mode) Test_Result(FAIL);
+ exit(1);
+ }
+ addr.inet.family = AF_INET;
+ addr.inet.ip = PR_htonl(INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ if (!debug_mode) Test_Result(FAIL);
+ exit(1);
+ }
+ if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ if (!debug_mode) Test_Result(FAIL);
+ exit(1);
+ }
+ listenPort2 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock2, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ if (!debug_mode) Test_Result(FAIL);
+ exit(1);
+ }
+ PR_snprintf(buf, sizeof(buf),
+ "The server thread is listening on ports %hu and %hu\n\n",
+ listenPort1, listenPort2);
+ printf("%s", buf);
+
+ /* Set up the fd set */
+ PR_FD_ZERO(&readFdSet);
+ PR_FD_SET(listenSock1, &readFdSet);
+ PR_FD_SET(listenSock2, &readFdSet);
+
+ /* Testing timeout */
+ if (debug_mode) printf("PR_Select should time out in 5 seconds\n");
+ retVal = PR_Select(0 /* unused */, &readFdSet, NULL, NULL,
+ PR_SecondsToInterval(5));
+ if (retVal != 0) {
+ PR_snprintf(buf, sizeof(buf),
+ "PR_Select should time out and return 0, but it returns %ld\n",
+ retVal);
+ fprintf(stderr, "%s", buf);
+ if (retVal == -1) {
+ fprintf(stderr, "Error %d, oserror %d\n", PR_GetError(),
+ PR_GetOSError());
+ if (!debug_mode) Test_Result(FAIL);
+ }
+ exit(1);
+ }
+ if (debug_mode) printf("PR_Select timed out. Test passed.\n\n");
+ else Test_Result(PASS);
+
+ /* Testing bad fd */
+ printf("PR_Select should detect a bad file descriptor\n");
+ if ((badFD = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a TCP socket\n");
+ exit(1);
+ }
+
+ PR_FD_SET(listenSock1, &readFdSet);
+ PR_FD_SET(listenSock2, &readFdSet);
+ PR_FD_SET(badFD, &readFdSet);
+ PR_Close(badFD); /* make the fd bad */
+ retVal = PR_Select(0 /* unused */, &readFdSet, NULL, NULL,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (retVal != -1 || PR_GetError() != PR_BAD_DESCRIPTOR_ERROR) {
+ fprintf(stderr, "Failed to detect the bad fd: "
+ "PR_Select returns %d\n", retVal);
+ if (retVal == -1) {
+ fprintf(stderr, "Error %d, oserror %d\n", PR_GetError(),
+ PR_GetOSError());
+ }
+ exit(1);
+ }
+ printf("PR_Select detected a bad fd. Test passed.\n\n");
+ PR_FD_CLR(badFD, &readFdSet);
+
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ clientThreadFunc, (void *) listenPort1,
+ PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+ if (clientThread == NULL) {
+ fprintf(stderr, "can't create thread\n");
+ exit(1);
+ }
+
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ clientThreadFunc, (void *) listenPort2,
+ PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+ if (clientThread == NULL) {
+ fprintf(stderr, "can't create thread\n");
+ exit(1);
+ }
+
+ printf("Two client threads are created. Each of them will\n");
+ printf("send data to one of the two ports the server is listening on.\n");
+ printf("The data they send is the port number. Each of them send\n");
+ printf("the data five times, so you should see ten lines below,\n");
+ printf("interleaved in an arbitrary order.\n");
+
+ /* set up the fd array */
+ fds = fds0;
+ other_fds = fds1;
+ fds[0] = listenSock1;
+ fds[1] = listenSock2;
+ nfds = 2;
+ PR_FD_SET(listenSock1, &readFdSet);
+ PR_FD_SET(listenSock2, &readFdSet);
+
+ /* 20 events total */
+ i = 0;
+ while (i < 20) {
+ PRFileDesc **tmp;
+ int nextIndex;
+ int nEvents = 0;
+
+ retVal = PR_Select(0 /* unused */, &readFdSet, NULL, NULL,
+ PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(retVal != 0); /* no timeout */
+ if (retVal == -1) {
+ fprintf(stderr, "PR_Select failed (%d, %d)\n", PR_GetError(),
+ PR_GetOSError());
+ exit(1);
+ }
+
+ nextIndex = 2;
+ /* the two listening sockets */
+ for (j = 0; j < 2; j++) {
+ other_fds[j] = fds[j];
+ if (PR_FD_ISSET(fds[j], &readFdSet)) {
+ PRFileDesc *sock;
+
+ nEvents++;
+ sock = PR_Accept(fds[j], NULL, PR_INTERVAL_NO_TIMEOUT);
+ if (sock == NULL) {
+ fprintf(stderr, "PR_Accept() failed\n");
+ exit(1);
+ }
+ other_fds[nextIndex] = sock;
+ PR_FD_SET(sock, &readFdSet);
+ nextIndex++;
+ }
+ PR_FD_SET(fds[j], &readFdSet);
+ }
+
+ for (j = 2; j < nfds; j++) {
+ if (PR_FD_ISSET(fds[j], &readFdSet)) {
+ PRInt32 nBytes;
+
+ PR_FD_CLR(fds[j], &readFdSet);
+ nEvents++;
+ nBytes = PR_Read(fds[j], buf, sizeof(buf));
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Read() failed\n");
+ exit(1);
+ }
+ /* Just to be safe */
+ buf[127] = '\0';
+ PR_Close(fds[j]);
+ printf("The server received \"%s\" from a client\n", buf);
+ } else {
+ PR_FD_SET(fds[j], &readFdSet);
+ other_fds[nextIndex] = fds[j];
+ nextIndex++;
+ }
+ }
+
+ PR_ASSERT(retVal == nEvents);
+ /* swap */
+ tmp = fds;
+ fds = other_fds;
+ other_fds = tmp;
+ nfds = nextIndex;
+ i += nEvents;
+ }
+
+ printf("All tests finished\n");
+ PR_Cleanup();
+ return 0;
+}
diff --git a/pr/tests/prttools.h b/pr/tests/prttools.h
new file mode 100644
index 00000000..236a8b39
--- /dev/null
+++ b/pr/tests/prttools.h
@@ -0,0 +1,24 @@
+/* -*- 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.
+ */
+
+/* Used in Regress Tool */
+#define NOSTATUS 2
+#define PASS 1
+#define FAIL 0
+
+PRIntn debug_mode=0;
diff --git a/pr/tests/ranfile.c b/pr/tests/ranfile.c
new file mode 100644
index 00000000..1ee6ca20
--- /dev/null
+++ b/pr/tests/ranfile.c
@@ -0,0 +1,410 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+**
+** Contact: AOF<freier@netscape.com>
+**
+** Name: ranfile.c
+**
+** Description: Test to hammer on various components of NSPR
+** Modification History:
+** 20-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "prinit.h"
+#include "prthread.h"
+#include "prlock.h"
+#include "prcvar.h"
+#include "prmem.h"
+#include "prinrval.h"
+#include "prio.h"
+
+#include <string.h>
+#include <stdio.h>
+
+static PRIntn debug_mode = 0;
+static PRIntn failed_already=0;
+static PRThreadScope thread_scope = PR_LOCAL_THREAD;
+
+typedef enum {sg_go, sg_stop, sg_done} Action;
+typedef enum {sg_okay, sg_open, sg_close, sg_delete, sg_write, sg_seek} Problem;
+
+typedef struct Hammer_s {
+ PRLock *ml;
+ PRCondVar *cv;
+ PRUint32 id;
+ PRUint32 limit;
+ PRUint32 writes;
+ PRThread *thread;
+ PRIntervalTime timein;
+ Action action;
+ Problem problem;
+} Hammer_t;
+
+static PRInt32 pageSize = 1024;
+static const char* baseName = "./";
+static const char *programName = "Random File";
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+#endif
+
+/***********************************************************************
+** PRIVATE FUNCTION: Random
+** DESCRIPTION:
+** Generate a pseudo-random number
+** INPUTS: None
+** OUTPUTS: None
+** RETURN: A pseudo-random unsigned number, 32-bits wide
+** SIDE EFFECTS:
+** Updates random seed (a static)
+** RESTRICTIONS:
+** None
+** MEMORY: NA
+** ALGORITHM:
+** Uses the current interval timer value, promoted to a 64 bit
+** float as a multiplier for a static residue (which begins
+** as an uninitialized variable). The result is bits [16..48)
+** of the product. Seed is then updated with the return value
+** promoted to a float-64.
+***********************************************************************/
+static PRUint32 Random(void)
+{
+ PRUint32 rv;
+ PRUint64 shift;
+ static PRFloat64 seed = 0x58a9382; /* Just make sure it isn't 0! */
+ PRFloat64 random = seed * (PRFloat64)PR_IntervalNow();
+ LL_USHR(shift, *((PRUint64*)&random), 16);
+ LL_L2UI(rv, shift);
+ seed = (PRFloat64)rv;
+ return rv;
+} /* Random */
+
+/***********************************************************************
+** PRIVATE FUNCTION: Thread
+** DESCRIPTION:
+** Hammer on the file I/O system
+** INPUTS: A pointer to the thread's private data
+** OUTPUTS: None
+** RETURN: None
+** SIDE EFFECTS:
+** Creates, accesses and deletes a file
+** RESTRICTIONS:
+** (Currently) must have file create permission in "/usr/tmp".
+** MEMORY: NA
+** ALGORITHM:
+** This function is a root of a thread
+** 1) Creates a (hopefully) unique file in /usr/tmp/
+** 2) Writes a zero to a random number of sequential pages
+** 3) Closes the file
+** 4) Reopens the file
+** 5) Seeks to a random page within the file
+** 6) Writes a one byte on that page
+** 7) Repeat steps [5..6] for each page in the file
+** 8) Close and delete the file
+** 9) Repeat steps [1..8] until told to stop
+** 10) Notify complete and return
+***********************************************************************/
+static void PR_CALLBACK Thread(void *arg)
+{
+ PRUint32 index;
+ char filename[30];
+ const char zero = 0;
+ PRFileDesc *file = NULL;
+ PRStatus rv = PR_SUCCESS;
+ Hammer_t *cd = (Hammer_t*)arg;
+
+ (void)sprintf(filename, "%ssg%04ld.dat", baseName, cd->id);
+
+ if (debug_mode) printf("Starting work on %s\n", filename);
+
+ while (PR_TRUE)
+ {
+ PRUint32 bytes;
+ PRUint32 minor = (Random() % cd->limit) + 1;
+ PRUint32 random = (Random() % cd->limit) + 1;
+ PRUint32 pages = (Random() % cd->limit) + 10;
+ while (minor-- > 0)
+ {
+ cd->problem = sg_okay;
+ if (cd->action != sg_go) goto finished;
+ cd->problem = sg_open;
+ file = PR_Open(filename, PR_RDWR|PR_CREATE_FILE, 0666);
+ if (file == NULL) goto finished;
+ for (index = 0; index < pages; index++)
+ {
+ cd->problem = sg_okay;
+ if (cd->action != sg_go) goto close;
+ cd->problem = sg_seek;
+ bytes = PR_Seek(file, pageSize * index, PR_SEEK_SET);
+ if (bytes != pageSize * index) goto close;
+ cd->problem = sg_write;
+ bytes = PR_Write(file, &zero, sizeof(zero));
+ if (bytes <= 0) goto close;
+ cd->writes += 1;
+ }
+ cd->problem = sg_close;
+ rv = PR_Close(file);
+ if (rv != PR_SUCCESS) goto purge;
+
+ cd->problem = sg_okay;
+ if (cd->action != sg_go) goto purge;
+
+ cd->problem = sg_open;
+ file = PR_Open(filename, PR_RDWR, 0666);
+ for (index = 0; index < pages; index++)
+ {
+ cd->problem = sg_okay;
+ if (cd->action != sg_go) goto close;
+ cd->problem = sg_seek;
+ bytes = PR_Seek(file, pageSize * index, PR_SEEK_SET);
+ if (bytes != pageSize * index) goto close;
+ cd->problem = sg_write;
+ bytes = PR_Write(file, &zero, sizeof(zero));
+ if (bytes <= 0) goto close;
+ cd->writes += 1;
+ random = (random + 511) % pages;
+ }
+ cd->problem = sg_close;
+ rv = PR_Close(file);
+ if (rv != PR_SUCCESS) goto purge;
+ cd->problem = sg_delete;
+ rv = PR_Delete(filename);
+ if (rv != PR_SUCCESS) goto finished;
+ }
+ }
+
+close:
+ (void)PR_Close(file);
+purge:
+ (void)PR_Delete(filename);
+finished:
+ PR_Lock(cd->ml);
+ cd->action = sg_done;
+ PR_NotifyCondVar(cd->cv);
+ PR_Unlock(cd->ml);
+
+ if (debug_mode) printf("Ending work on %s\n", filename);
+
+ return;
+} /* Thread */
+
+static Hammer_t hammer[100];
+static PRCondVar *cv;
+/***********************************************************************
+** PRIVATE FUNCTION: main
+** DESCRIPTION:
+** Hammer on the file I/O system
+** INPUTS: The usual argc and argv
+** argv[0] - program name (not used)
+** argv[1] - the number of times to execute the major loop
+** argv[2] - the number of threads to toss into the batch
+** argv[3] - the clipping number applied to randoms
+** default values: loops = 2, threads = 10, limit = 57
+** OUTPUTS: None
+** RETURN: None
+** SIDE EFFECTS:
+** Creates, accesses and deletes lots of files
+** RESTRICTIONS:
+** (Currently) must have file create permission in "/usr/tmp".
+** MEMORY: NA
+** ALGORITHM:
+** 1) Fork a "Thread()"
+** 2) Wait for 'interleave' seconds
+** 3) For [0..'threads') repeat [1..2]
+** 4) Mark all objects to stop
+** 5) Collect the threads, accumulating the results
+** 6) For [0..'loops') repeat [1..5]
+** 7) Print accumulated results and exit
+**
+** Characteristic output (from IRIX)
+** Random File: Using loops = 2, threads = 10, limit = 57
+** Random File: [min [avg] max] writes/sec average
+***********************************************************************/
+int main (int argc, char *argv[])
+{
+ PRLock *ml;
+ PRUint32 id = 0;
+ int active, poll;
+ PRIntervalTime interleave;
+ PRIntervalTime duration = 0;
+ int limit = 0, loops = 0, threads = 0, times;
+ PRUint32 writes, writesMin = 0x7fffffff, writesTot = 0, durationTot = 0, writesMax = 0;
+
+ const char *where[] = {"okay", "open", "close", "delete", "write", "seek"};
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "Gdl:t:i:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'G': /* global threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ case 'l': /* limiting number */
+ limit = atoi(opt->value);
+ break;
+ case 't': /* number of threads */
+ threads = atoi(opt->value);
+ break;
+ case 'i': /* iteration counter */
+ loops = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ interleave = PR_SecondsToInterval(10);
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("ranfile.log");
+ debug_mode = 1;
+#endif
+
+ ml = PR_NewLock();
+ cv = PR_NewCondVar(ml);
+
+ if (loops == 0) loops = 2;
+ if (limit == 0) limit = 57;
+ if (threads == 0) threads = 10;
+
+ if (debug_mode) printf(
+ "%s: Using loops = %d, threads = %d, limit = %d and %s threads\n",
+ programName, loops, threads, limit,
+ (thread_scope == PR_LOCAL_THREAD) ? "LOCAL" : "GLOBAL");
+
+ for (times = 0; times < loops; ++times)
+ {
+ if (debug_mode) printf("%s: Setting concurrency level to %d\n", programName, times + 1);
+ PR_SetConcurrency(times + 1);
+ for (active = 0; active < threads; active++)
+ {
+ hammer[active].ml = ml;
+ hammer[active].cv = cv;
+ hammer[active].id = id++;
+ hammer[active].writes = 0;
+ hammer[active].action = sg_go;
+ hammer[active].problem = sg_okay;
+ hammer[active].limit = (Random() % limit) + 1;
+ hammer[active].timein = PR_IntervalNow();
+ hammer[active].thread = PR_CreateThread(
+ PR_USER_THREAD, Thread, &hammer[active],
+ PR_GetThreadPriority(PR_GetCurrentThread()),
+ thread_scope, PR_JOINABLE_THREAD, 0);
+
+ PR_Lock(ml);
+ PR_WaitCondVar(cv, interleave); /* start new ones slowly */
+ PR_Unlock(ml);
+ }
+
+ /*
+ * The last thread started has had the opportunity to run for
+ * 'interleave' seconds. Now gather them all back in.
+ */
+ PR_Lock(ml);
+ for (poll = 0; poll < threads; poll++)
+ {
+ if (hammer[poll].action == sg_go) /* don't overwrite done */
+ hammer[poll].action = sg_stop; /* ask him to stop */
+ }
+ PR_Unlock(ml);
+
+ while (active > 0)
+ {
+ for (poll = 0; poll < threads; poll++)
+ {
+ PR_Lock(ml);
+ while (hammer[poll].action < sg_done)
+ PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT);
+ PR_Unlock(ml);
+
+ active -= 1; /* this is another one down */
+ (void)PR_JoinThread(hammer[poll].thread);
+ hammer[poll].thread = NULL;
+ if (hammer[poll].problem == sg_okay)
+ {
+ duration = PR_IntervalToMilliseconds(
+ PR_IntervalNow() - hammer[poll].timein);
+ writes = hammer[poll].writes * 1000 / duration;
+ if (writes < writesMin)
+ writesMin = writes;
+ if (writes > writesMax)
+ writesMax = writes;
+ writesTot += hammer[poll].writes;
+ durationTot += duration;
+ }
+ else
+ if (debug_mode) printf(
+ "%s: test failed %s after %ld seconds\n",
+ programName, where[hammer[poll].problem], duration);
+ else failed_already=1;
+ }
+ }
+ }
+ if (debug_mode) printf(
+ "%s: [%ld [%ld] %ld] writes/sec average\n",
+ programName, writesMin, writesTot * 1000 / durationTot, writesMax);
+
+ PR_DestroyCondVar(cv);
+ PR_DestroyLock(ml);
+
+ if (failed_already)
+ {
+ printf("FAIL\n");
+ return 1;
+ }
+ else
+ {
+ printf("PASS\n");
+ return 0;
+ }
+} /* main */
diff --git a/pr/tests/sel_spd.c b/pr/tests/sel_spd.c
new file mode 100644
index 00000000..b0033567
--- /dev/null
+++ b/pr/tests/sel_spd.c
@@ -0,0 +1,546 @@
+/* -*- 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.
+ */
+
+/*
+ * Test the speed of select within NSPR
+ *
+ */
+
+#include "nspr.h"
+#include "prpriv.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#ifdef XP_MAC
+#include "prlog.h"
+int fprintf(FILE *stream, const char *fmt, ...)
+{
+PR_LogPrint(fmt);
+return 0;
+}
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+#endif
+
+#define PORT_BASE 19000
+
+typedef struct timer_slot_t {
+ unsigned long d_connect;
+ unsigned long d_cl_data;
+ unsigned long d_sv_data;
+ unsigned long d_close;
+ unsigned long d_total;
+ unsigned long requests;
+} timer_slot_t;
+
+static long _iterations = 5;
+static long _client_data = 8192;
+
+#if defined(XP_MAC)
+/*
+ * Mac does not scale well specially the requirement for thread stack
+ * space and buffer allocation space. It is easy to get into a fragmented
+ * memory and not be able to allocate thread stack or client/server data
+ * buffer.
+*/
+static long _server_data = (8*1024);
+static long _threads_max = 10, _threads = 10;
+#else
+static long _server_data = (128*1024);
+static long _threads_max = 100, _threads = 100;
+#endif
+
+static int verbose=0;
+static PRMonitor *exit_cv;
+static long _thread_exit_count;
+static timer_slot_t *timer_data;
+static PRThreadScope scope1, scope2;
+
+void tally_results(int);
+
+/* return the diff in microseconds */
+unsigned long _delta(PRIntervalTime *start, PRIntervalTime *stop)
+{
+ /*
+ * Will C do the right thing with unsigned arithemtic?
+ */
+ return PR_IntervalToMicroseconds(*stop - *start);
+}
+
+int _readn(PRFileDesc *sock, char *buf, int len)
+{
+ int rem;
+ int bytes;
+
+ for (rem=len; rem; rem -= bytes) {
+ bytes = PR_Recv(sock, buf, rem, 0, PR_INTERVAL_NO_TIMEOUT);
+ if (bytes <= 0)
+ return -1;
+ }
+ return len;
+}
+
+void
+_thread_exit(int id)
+{
+ PR_EnterMonitor(exit_cv);
+#ifdef DEBUG
+ fprintf(stdout, "Thread %d EXIT\n", id);
+#endif
+
+ _thread_exit_count--;
+ if (_thread_exit_count == 0) {
+#ifdef DEBUG
+ fprintf(stdout, "Thread %d EXIT triggered notify\n", id);
+#endif
+ PR_Notify(exit_cv);
+ }
+ PR_ExitMonitor(exit_cv);
+}
+
+void
+_server_thread(void *arg_id)
+{
+ void _client_thread(void *);
+ PRThread *thread;
+ int *id = (int *)arg_id;
+ PRFileDesc *sock;
+ PRNetAddr sa;
+ PRFileDesc * newsock;
+ char *data_buffer = NULL;
+ int data_buffer_size;
+ int index;
+ PRIntervalTime start,
+ connect_done,
+ read_done,
+ write_done,
+ close_done;
+
+
+#ifdef DEBUG
+ fprintf(stdout, "server thread %d alive\n", *id);
+#endif
+
+ data_buffer_size = (_client_data>_server_data?_client_data:_server_data);
+
+ if ( (data_buffer = (char *)PR_Malloc(data_buffer_size * sizeof(char))) == NULL ) {
+ fprintf(stderr, "Error creating buffer in server thread %d\n", *id);
+ goto done;
+ }
+
+
+ if ( (sock = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Error creating socket in server thread %d\n", *id);
+ goto done;
+ }
+
+ memset(&sa, 0 , sizeof(sa));
+ sa.inet.family = PR_AF_INET;
+ sa.inet.port = PORT_BASE + *id;
+ sa.inet.ip = PR_htonl(PR_INADDR_ANY);
+
+ if ( PR_Bind(sock, &sa) < 0) {
+ fprintf(stderr, "Error binding socket in server thread %d errno = %d\n", *id, errno);
+ goto done;
+ }
+
+#if 0
+ {
+ int one = 1;
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&one, sizeof(one));
+ }
+#endif
+
+ if ( PR_Listen(sock, 32) < 0 ) {
+ fprintf(stderr, "Error listening to socket in server thread %d\n", *id);
+ goto done;
+ }
+
+ /* Tell the client to start */
+ if ( (thread = PR_CreateThread(PR_USER_THREAD,
+ _client_thread,
+ id,
+ PR_PRIORITY_NORMAL,
+ scope2,
+ PR_UNJOINABLE_THREAD,
+ 0)) == NULL)
+ fprintf(stderr, "Error creating client thread %d\n", *id);
+
+ for (index = 0; index< _iterations; index++) {
+
+#ifdef DEBUG
+ fprintf(stdout, "server thread %d loop %d\n", *id, index);
+#endif
+
+ start = PR_IntervalNow();
+
+ if ( (newsock = PR_Accept(sock, &sa,
+ PR_INTERVAL_NO_TIMEOUT)) == NULL) {
+ fprintf(stderr, "Error accepting connection %d in server thread %d\n",
+ index, *id);
+ goto done;
+ }
+#ifdef DEBUG
+ fprintf(stdout, "server thread %d got connection\n", *id, newsock);
+#endif
+
+
+ connect_done = PR_IntervalNow();
+
+ if ( _readn(newsock, data_buffer, _client_data) < _client_data) {
+ fprintf(stderr, "Error reading client data for iteration %d in server thread %d\n", index, *id );
+ goto done;
+ }
+
+#ifdef DEBUG
+ fprintf(stdout, "server thread %d read %d bytes\n", *id, _client_data);
+#endif
+ read_done = PR_IntervalNow();
+
+ if ( PR_Send(newsock, data_buffer, _server_data, 0,
+ PR_INTERVAL_NO_TIMEOUT) < _server_data) {
+ fprintf(stderr, "Error sending client data for iteration %d in server thread %d\n", index, *id );
+ goto done;
+ }
+
+#ifdef DEBUG
+ fprintf(stdout, "server thread %d write %d bytes\n", *id, _server_data);
+#endif
+
+ write_done = PR_IntervalNow();
+
+ PR_Close(newsock);
+
+ close_done = PR_IntervalNow();
+
+ timer_data[2*(*id)].d_connect += _delta(&start, &connect_done);
+ timer_data[2*(*id)].d_cl_data += _delta(&connect_done, &read_done);
+ timer_data[2*(*id)].d_sv_data += _delta(&read_done, &write_done);
+ timer_data[2*(*id)].d_close += _delta(&write_done, &close_done);
+ timer_data[2*(*id)].d_total += _delta(&start, &close_done);
+ timer_data[2*(*id)].requests++;
+
+
+#ifdef DEBUG
+ fprintf(stdout, "server: %d %d %d %d %d\n",
+ _delta(&start, &connect_done), _delta(&connect_done, &read_done),
+ _delta(&read_done, &write_done), _delta(&write_done, &close_done),
+ _delta(&start, &close_done));
+#endif
+ }
+
+done:
+ if (data_buffer != NULL) PR_Free (data_buffer);
+ if (sock) PR_Close(sock);
+ _thread_exit(*id);
+ return;
+}
+
+void
+_client_thread(void *arg_id)
+{
+ int *id = (int *)arg_id;
+ int index;
+ PRNetAddr sa;
+ PRFileDesc *sock_h;
+ char *data_buffer = NULL;
+ int data_buffer_size;
+ int bytes;
+ PRIntervalTime start,
+ connect_done,
+ read_done,
+ write_done,
+ close_done;
+ PRStatus rv;
+
+#ifdef DEBUG
+ fprintf(stdout, "client thread %d alive\n", *id);
+#endif
+
+ data_buffer_size = (_client_data>_server_data?_client_data:_server_data);
+
+ if ( (data_buffer = (char *)PR_Malloc(data_buffer_size * sizeof(char))) == NULL) {
+ fprintf(stderr, "Error creating buffer in server thread %d\n", *id);
+ goto done;
+ }
+
+ memset(&sa, 0 , sizeof(sa));
+ rv = PR_InitializeNetAddr(PR_IpAddrLoopback, PORT_BASE + *id, &sa);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ for (index = 0; index< _iterations; index++) {
+
+#ifdef DEBUG
+ fprintf(stdout, "client thread %d loop %d\n", *id, index);
+#endif
+
+ start = PR_IntervalNow();
+ if ( (sock_h = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Error creating socket %d in client thread %d\n",
+ index, *id);
+ goto done;
+ }
+
+#ifdef DEBUG
+ fprintf(stdout, "client thread %d socket created %d\n", *id, sock_h);
+#endif
+
+ if ( PR_Connect(sock_h, &sa,
+ PR_INTERVAL_NO_TIMEOUT) < 0) {
+ fprintf(stderr, "Error accepting connection %d in client thread %d\n",
+ index, *id);
+ goto done;
+ }
+
+#ifdef DEBUG
+ fprintf(stdout, "client thread %d socket connected %d\n", *id, sock_h);
+#endif
+
+ connect_done = PR_IntervalNow();
+ if ( PR_Send(sock_h, data_buffer, _client_data, 0,
+ PR_INTERVAL_NO_TIMEOUT) < _client_data) {
+ fprintf(stderr, "Error sending client data for iteration %d in client thread %d\n", index, *id );
+ goto done;
+ }
+
+#ifdef DEBUG
+ fprintf(stdout, "client thread %d socket wrote %d\n", *id, _client_data);
+#endif
+
+ write_done = PR_IntervalNow();
+ if ( (bytes = _readn(sock_h, data_buffer, _server_data)) < _server_data) {
+ fprintf(stderr, "Error reading server data for iteration %d in client thread %d (read %d bytes)\n", index, *id, bytes );
+ goto done;
+ }
+
+#ifdef DEBUG
+ fprintf(stdout, "client thread %d socket read %d\n", *id, _server_data);
+#endif
+
+ read_done = PR_IntervalNow();
+ PR_Close(sock_h);
+ close_done = PR_IntervalNow();
+
+ timer_data[2*(*id)+1].d_connect += _delta(&start, &connect_done);
+ timer_data[2*(*id)+1].d_cl_data += _delta(&connect_done, &write_done);
+ timer_data[2*(*id)+1].d_sv_data += _delta(&write_done, &read_done);
+ timer_data[2*(*id)+1].d_close += _delta(&read_done, &close_done);
+ timer_data[2*(*id)+1].d_total += _delta(&start, &close_done);
+ timer_data[2*(*id)+1].requests++;
+ }
+done:
+ if (data_buffer != NULL) PR_Free (data_buffer);
+ _thread_exit(*id);
+
+ return;
+}
+
+static
+void do_work(void)
+{
+ int index;
+
+ _thread_exit_count = _threads * 2;
+ for (index=0; index<_threads; index++) {
+ PRThread *thread;
+ int *id = (int *)PR_Malloc(sizeof(int));
+
+ *id = index;
+
+ if ( (thread = PR_CreateThread(PR_USER_THREAD,
+ _server_thread,
+ id,
+ PR_PRIORITY_NORMAL,
+ scope1,
+ PR_UNJOINABLE_THREAD,
+ 0)) == NULL)
+ fprintf(stderr, "Error creating server thread %d\n", index);
+ }
+
+ PR_EnterMonitor(exit_cv);
+ PR_Wait(exit_cv, 0x7fffffff);
+ PR_ExitMonitor(exit_cv);
+
+ fprintf(stdout, "TEST COMPLETE!\n");
+
+ tally_results(verbose);
+
+}
+
+static void do_workUU(void)
+{
+ scope1 = PR_LOCAL_THREAD;
+ scope2 = PR_LOCAL_THREAD;
+ do_work();
+}
+
+static void do_workUK(void)
+{
+ scope1 = PR_LOCAL_THREAD;
+ scope2 = PR_GLOBAL_THREAD;
+ do_work();
+}
+
+static void do_workKU(void)
+{
+ scope1 = PR_GLOBAL_THREAD;
+ scope2 = PR_LOCAL_THREAD;
+ do_work();
+}
+
+static void do_workKK(void)
+{
+ scope1 = PR_GLOBAL_THREAD;
+ scope2 = PR_GLOBAL_THREAD;
+ do_work();
+}
+
+
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+
+ printf("%40s: %6.2f usec\n", msg, d / _iterations);
+}
+
+
+main(int argc, char **argv)
+{
+#ifdef XP_UNIX
+ int opt;
+ extern char *optarg;
+#endif
+
+#ifdef XP_UNIX
+ while ( (opt = getopt(argc, argv, "c:s:i:t:v")) != EOF) {
+ switch(opt) {
+ case 'i':
+ _iterations = atoi(optarg);
+ break;
+ case 't':
+ _threads_max = _threads = atoi(optarg);
+ break;
+ case 'c':
+ _client_data = atoi(optarg);
+ break;
+ case 's':
+ _server_data = atoi(optarg);
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ break;
+ }
+ }
+#endif
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("sel_spd.log");
+#endif
+
+ fprintf(stdout, "Running test for %d iterations with %d simultaneous threads.\n",
+ _iterations, _threads);
+ fprintf(stdout, "\tWill send %d bytes of client data and %d bytes of server data\n",
+ _client_data, _server_data);
+
+ if ( (exit_cv = PR_NewMonitor()) == NULL)
+ fprintf(stderr, "Error creating monitor for exit cv\n");
+ if ( (timer_data = (timer_slot_t *)PR_Malloc(2*_threads * sizeof(timer_slot_t))) == NULL)
+ fprintf(stderr, "error allocating thread time results array\n");
+ memset(timer_data, 0 , 2*_threads*sizeof(timer_slot_t));
+
+ Measure(do_workUU, "select loop user/user");
+ Measure(do_workUK, "select loop user/user");
+ Measure(do_workKU, "select loop user/user");
+ Measure(do_workKK, "select loop user/user");
+
+
+ return 0;
+}
+
+void
+tally_results(int verbose)
+{
+ int index;
+ unsigned long tot_connect = 0;
+ unsigned long tot_cl_data = 0;
+ unsigned long tot_sv_data = 0;
+ unsigned long tot_close = 0;
+ unsigned long tot_all = 0;
+ unsigned long tot_requests = 0;
+
+ fprintf(stdout, "Server results:\n\n");
+ for (index=0; index<_threads_max*2; index+=2) {
+
+ if (verbose)
+ fprintf(stdout, "server thread %u\t%u\t%u\t%u\t%u\t%u\t%u\n",
+ index, timer_data[index].requests, timer_data[index].d_connect,
+ timer_data[index].d_cl_data, timer_data[index].d_sv_data,
+ timer_data[index].d_close, timer_data[index].d_total);
+
+ tot_connect += timer_data[index].d_connect / _threads;
+ tot_cl_data += timer_data[index].d_cl_data / _threads;
+ tot_sv_data += timer_data[index].d_sv_data / _threads;
+ tot_close += timer_data[index].d_close / _threads;
+ tot_all += timer_data[index].d_total / _threads;
+ tot_requests += timer_data[index].requests / _threads;
+ }
+ fprintf(stdout, "----------\n");
+ fprintf(stdout, "server per thread totals %u\t%u\t%u\t%u\t%u\n",
+ tot_requests, tot_connect, tot_cl_data, tot_sv_data, tot_close);
+ fprintf(stdout, "server per thread elapsed time %u\n", tot_all);
+ fprintf(stdout, "----------\n");
+
+ tot_connect = tot_cl_data = tot_sv_data = tot_close = tot_all = tot_requests = 0;
+ fprintf(stdout, "Client results:\n\n");
+ for (index=1; index<_threads_max*2; index+=2) {
+
+ if (verbose)
+ fprintf(stdout, "client thread %u\t%u\t%u\t%u\t%u\t%u\t%u\n",
+ index, timer_data[index].requests, timer_data[index].d_connect,
+ timer_data[index].d_cl_data, timer_data[index].d_sv_data,
+ timer_data[index].d_close, timer_data[index].d_total);
+
+ tot_connect += timer_data[index].d_connect / _threads;
+ tot_cl_data += timer_data[index].d_cl_data / _threads;
+ tot_sv_data += timer_data[index].d_sv_data / _threads;
+ tot_close += timer_data[index].d_close / _threads;
+ tot_all += timer_data[index].d_total / _threads;
+ tot_requests += timer_data[index].requests / _threads;
+ }
+ fprintf(stdout, "----------\n");
+ fprintf(stdout, "client per thread totals %u\t%u\t%u\t%u\t%u\n",
+ tot_requests, tot_connect, tot_cl_data, tot_sv_data, tot_close);
+ fprintf(stdout, "client per thread elapsed time %u\n", tot_all);
+}
+
diff --git a/pr/tests/selct_er.c b/pr/tests/selct_er.c
new file mode 100755
index 00000000..ca76426d
--- /dev/null
+++ b/pr/tests/selct_er.c
@@ -0,0 +1,204 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+** 1997 - Netscape Communications Corporation
+**
+** Name: prselect_err.c
+**
+** Description: tests PR_Select with sockets Error condition functions.
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "primpl.h"
+#include "pprio.h"
+#include "prnetdb.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *listenSock1, *listenSock2;
+ PRFileDesc *badFD;
+ PRUint16 listenPort1, listenPort2;
+ PRNetAddr addr;
+ PR_fd_set readFdSet;
+ char buf[128];
+ PRInt32 retVal;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ if (debug_mode) {
+ printf("This program tests PR_Select with sockets. Error\n");
+ printf("reporting operations are tested.\n\n");
+ }
+
+ /* Create two listening sockets */
+ if ((listenSock1 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ addr.inet.family = AF_INET;
+ addr.inet.ip = PR_htonl(INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ listenPort1 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock1, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+
+ if ((listenSock2 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ addr.inet.family = AF_INET;
+ addr.inet.ip = PR_htonl(INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ listenPort2 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock2, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ PR_snprintf(buf, sizeof(buf),
+ "The server thread is listening on ports %hu and %hu\n\n",
+ listenPort1, listenPort2);
+ if (debug_mode) printf("%s", buf);
+
+ /* Set up the fd set */
+ PR_FD_ZERO(&readFdSet);
+ PR_FD_SET(listenSock1, &readFdSet);
+ PR_FD_SET(listenSock2, &readFdSet);
+
+
+ /* Testing bad fd */
+ if (debug_mode) printf("PR_Select should detect a bad file descriptor\n");
+ if ((badFD = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a TCP socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+
+ PR_FD_SET(badFD, &readFdSet);
+ /*
+ * Make the fd invalid
+ */
+#if defined(XP_UNIX)
+ close(PR_FileDesc2NativeHandle(badFD));
+#elif defined(XP_OS2)
+ soclose(PR_FileDesc2NativeHandle(badFD));
+#elif defined(WIN32) || defined(WIN16)
+ closesocket(PR_FileDesc2NativeHandle(badFD));
+#elif defined(XP_MAC)
+ _PR_MD_CLOSE_SOCKET(PR_FileDesc2NativeHandle(badFD));
+#else
+#error "Unknown architecture"
+#endif
+
+ retVal = PR_Select(0 /* unused */, &readFdSet, NULL, NULL,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (retVal != -1 || PR_GetError() != PR_BAD_DESCRIPTOR_ERROR) {
+ fprintf(stderr, "Failed to detect the bad fd: "
+ "PR_Select returns %d\n", retVal);
+ if (retVal == -1) {
+ fprintf(stderr, "Error %d, oserror %d\n", PR_GetError(),
+ PR_GetOSError());
+ failed_already=1;
+ }
+ goto exit_now;
+ }
+ if (debug_mode) printf("PR_Select detected a bad fd. Test passed.\n\n");
+ PR_FD_CLR(badFD, &readFdSet);
+
+ PR_Cleanup();
+ goto exit_now;
+exit_now:
+ if(failed_already)
+ return 1;
+ else
+ return 0;
+
+}
diff --git a/pr/tests/selct_nm.c b/pr/tests/selct_nm.c
new file mode 100644
index 00000000..c8bbe30e
--- /dev/null
+++ b/pr/tests/selct_nm.c
@@ -0,0 +1,297 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+** 1997 - Netscape Communications Corporation
+**
+** Name: prselect_norm.c
+**
+** Description: tests PR_Select with sockets - Normal operations.
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "prinit.h"
+#include "prio.h"
+#include "prlog.h"
+#include "prprf.h"
+#include "prerror.h"
+#include "prnetdb.h"
+
+#include "probslet.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+static void
+clientThreadFunc(void *arg)
+{
+ PRUintn port = (PRUintn) arg;
+ PRFileDesc *sock;
+ PRNetAddr addr;
+ char buf[128];
+ int i;
+
+ addr.inet.family = PR_AF_INET;
+ addr.inet.port = PR_htons((PRUint16)port);
+ addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+ PR_snprintf(buf, sizeof(buf), "%hu", addr.inet.port);
+
+ for (i = 0; i < 5; i++) {
+ sock = PR_NewTCPSocket();
+ PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT);
+ PR_Write(sock, buf, sizeof(buf));
+ PR_Close(sock);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *listenSock1, *listenSock2;
+ PRFileDesc *fds0[10], *fds1[10], **fds, **other_fds;
+ PRIntn nfds;
+ PRUint16 listenPort1, listenPort2;
+ PRNetAddr addr;
+ PR_fd_set readFdSet;
+ char buf[128];
+ PRThread *clientThread;
+ PRInt32 retVal;
+ PRIntn i, j;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ if (debug_mode) {
+ printf("This program tests PR_Select with sockets. \n");
+ printf(" Normal operation are tested.\n\n");
+ }
+
+ /* Create two listening sockets */
+ if ((listenSock1 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ addr.inet.family = PR_AF_INET;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ listenPort1 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock1, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+
+ if ((listenSock2 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ addr.inet.family = PR_AF_INET;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ listenPort2 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock2, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+failed_already=1;
+ goto exit_now;
+ }
+ PR_snprintf(buf, sizeof(buf),
+ "The server thread is listening on ports %hu and %hu\n\n",
+ listenPort1, listenPort2);
+ if (debug_mode) printf("%s", buf);
+
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ clientThreadFunc, (void *) listenPort1,
+ PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+ if (clientThread == NULL) {
+ fprintf(stderr, "can't create thread\n");
+ failed_already=1;
+ goto exit_now;
+ }
+
+ clientThread = PR_CreateThread(PR_USER_THREAD,
+ clientThreadFunc, (void *) listenPort2,
+ PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+ if (clientThread == NULL) {
+ fprintf(stderr, "can't create thread\n");
+ failed_already=1;
+ goto exit_now;
+ }
+
+ if (debug_mode) {
+ printf("Two client threads are created. Each of them will\n");
+ printf("send data to one of the two ports the server is listening on.\n");
+ printf("The data they send is the port number. Each of them send\n");
+ printf("the data five times, so you should see ten lines below,\n");
+ printf("interleaved in an arbitrary order.\n");
+ }
+ /* set up the fd array */
+ fds = fds0;
+ other_fds = fds1;
+ fds[0] = listenSock1;
+ fds[1] = listenSock2;
+ nfds = 2;
+ /* Set up the fd set */
+ PR_FD_ZERO(&readFdSet);
+ PR_FD_SET(listenSock1, &readFdSet);
+ PR_FD_SET(listenSock2, &readFdSet);
+
+ /* 20 events total */
+ i = 0;
+ while (i < 20) {
+ PRFileDesc **tmp;
+ int nextIndex;
+ int nEvents = 0;
+
+ retVal = PR_Select(0 /* unused */, &readFdSet, NULL, NULL,
+ PR_INTERVAL_NO_TIMEOUT);
+ PR_ASSERT(retVal != 0); /* no timeout */
+ if (retVal == -1) {
+ fprintf(stderr, "PR_Select failed (%d, %d)\n", PR_GetError(),
+ PR_GetOSError());
+ failed_already=1;
+ goto exit_now;
+ }
+
+ nextIndex = 2;
+ /* the two listening sockets */
+ for (j = 0; j < 2; j++) {
+ other_fds[j] = fds[j];
+ if (PR_FD_ISSET(fds[j], &readFdSet)) {
+ PRFileDesc *sock;
+
+ nEvents++;
+ sock = PR_Accept(fds[j], NULL, PR_INTERVAL_NO_TIMEOUT);
+ if (sock == NULL) {
+ fprintf(stderr, "PR_Accept() failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ other_fds[nextIndex] = sock;
+ PR_FD_SET(sock, &readFdSet);
+ nextIndex++;
+ }
+ PR_FD_SET(fds[j], &readFdSet);
+ }
+
+ for (j = 2; j < nfds; j++) {
+ if (PR_FD_ISSET(fds[j], &readFdSet)) {
+ PRInt32 nBytes;
+
+ PR_FD_CLR(fds[j], &readFdSet);
+ nEvents++;
+ nBytes = PR_Read(fds[j], buf, sizeof(buf));
+ if (nBytes == -1) {
+ fprintf(stderr, "PR_Read() failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ /* Just to be safe */
+ buf[127] = '\0';
+ PR_Close(fds[j]);
+ if (debug_mode) printf("The server received \"%s\" from a client\n", buf);
+ } else {
+ PR_FD_SET(fds[j], &readFdSet);
+ other_fds[nextIndex] = fds[j];
+ nextIndex++;
+ }
+ }
+
+ PR_ASSERT(retVal == nEvents);
+ /* swap */
+ tmp = fds;
+ fds = other_fds;
+ other_fds = tmp;
+ nfds = nextIndex;
+ i += nEvents;
+ }
+
+ if (debug_mode) printf("Test passed\n");
+
+ PR_Cleanup();
+ goto exit_now;
+exit_now:
+ if(failed_already)
+ return 1;
+ else
+ return 0;
+}
diff --git a/pr/tests/selct_to.c b/pr/tests/selct_to.c
new file mode 100644
index 00000000..0b13a814
--- /dev/null
+++ b/pr/tests/selct_to.c
@@ -0,0 +1,185 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+** 1997 - Netscape Communications Corporation
+**
+** Name: prselect_to.c
+**
+** Description: tests PR_Select with sockets. Time out functions
+**
+** Modification History:
+** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "prinit.h"
+#include "prio.h"
+#include "prlog.h"
+#include "prprf.h"
+#include "prnetdb.h"
+
+#include "probslet.h"
+
+#include "prerror.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+int main(int argc, char **argv)
+{
+ PRFileDesc *listenSock1, *listenSock2;
+ PRUint16 listenPort1, listenPort2;
+ PRNetAddr addr;
+ PR_fd_set readFdSet;
+ char buf[128];
+ PRInt32 retVal;
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ if (debug_mode) {
+ printf("This program tests PR_Select with sockets. Timeout \n");
+ printf("operations are tested.\n\n");
+ }
+
+ /* Create two listening sockets */
+ if ((listenSock1 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ addr.inet.family = PR_AF_INET;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ listenPort1 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock1, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+
+ if ((listenSock2 = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr, "Can't create a new TCP socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ addr.inet.family = PR_AF_INET;
+ addr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ addr.inet.port = PR_htons(0);
+ if (PR_Bind(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "Can't bind socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) {
+ fprintf(stderr, "PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ listenPort2 = PR_ntohs(addr.inet.port);
+ if (PR_Listen(listenSock2, 5) == PR_FAILURE) {
+ fprintf(stderr, "Can't listen on a socket\n");
+ failed_already=1;
+ goto exit_now;
+ }
+ PR_snprintf(buf, sizeof(buf),
+ "The server thread is listening on ports %hu and %hu\n\n",
+ listenPort1, listenPort2);
+ if (debug_mode) printf("%s", buf);
+
+ /* Set up the fd set */
+ PR_FD_ZERO(&readFdSet);
+ PR_FD_SET(listenSock1, &readFdSet);
+ PR_FD_SET(listenSock2, &readFdSet);
+
+ /* Testing timeout */
+ if (debug_mode) printf("PR_Select should time out in 5 seconds\n");
+ retVal = PR_Select(0 /* unused */, &readFdSet, NULL, NULL,
+ PR_SecondsToInterval(5));
+ if (retVal != 0) {
+ PR_snprintf(buf, sizeof(buf),
+ "PR_Select should time out and return 0, but it returns %ld\n",
+ retVal);
+ fprintf(stderr, "%s", buf);
+ if (retVal == -1) {
+ fprintf(stderr, "Error %d, oserror %d\n", PR_GetError(),
+ PR_GetOSError());
+ failed_already=1;
+ }
+ goto exit_now;
+ }
+ if (debug_mode) printf("PR_Select timed out. Test passed.\n\n");
+
+ PR_Cleanup();
+
+exit_now:
+ if(failed_already)
+ return 1;
+ else
+ return 0;
+}
diff --git a/pr/tests/select2.c b/pr/tests/select2.c
new file mode 100644
index 00000000..a4e7bb27
--- /dev/null
+++ b/pr/tests/select2.c
@@ -0,0 +1,330 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+**
+** Name: select2.c
+**
+** Description: Measure PR_Select and Empty_Select performance.
+**
+** Modification History:
+** 20-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+#include "prttools.h"
+#include "primpl.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define PORT 8000
+#define DEFAULT_COUNT 10
+PRInt32 count;
+
+
+/***********************************************************************
+** PRIVATE FUNCTION: Test_Result
+** DESCRIPTION: Used in conjunction with the regress tool, prints out the
+** status of the test case.
+** INPUTS: PASS/FAIL
+** OUTPUTS: None
+** RETURN: None
+** SIDE EFFECTS:
+**
+** RESTRICTIONS:
+** None
+** MEMORY: NA
+** ALGORITHM: Determine what the status is and print accordingly.
+**
+***********************************************************************/
+
+
+static void Test_Result (int result)
+{
+ switch (result)
+ {
+ case PASS:
+ printf ("PASS\n");
+ break;
+ case FAIL:
+ printf ("FAIL\n");
+ break;
+ default:
+ printf ("NOSTATUS\n");
+ break;
+ }
+}
+
+static void EmptyPRSelect(void)
+{
+ PRInt32 index = count;
+ PRInt32 rv;
+
+ for (; index--;)
+ rv = PR_Select(0, NULL, NULL, NULL, PR_INTERVAL_NO_WAIT);
+}
+
+static void EmptyNativeSelect(void)
+{
+ PRInt32 rv;
+ PRInt32 index = count;
+ struct timeval timeout;
+
+ timeout.tv_sec = timeout.tv_usec = 0;
+ for (; index--;)
+ rv = select(0, NULL, NULL, NULL, &timeout);
+}
+
+static void PRSelectTest(void)
+{
+ PRFileDesc *listenSocket;
+ PRNetAddr serverAddr;
+
+ if ( (listenSocket = PR_NewTCPSocket()) == NULL) {
+ if (debug_mode) printf("\tServer error creating listen socket\n");
+ return;
+ }
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(INADDR_ANY);
+
+ if ( PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) {
+ if (debug_mode) printf("\tServer error binding to server address\n");
+ PR_Close(listenSocket);
+ return;
+ }
+
+ if ( PR_Listen(listenSocket, 128) == PR_FAILURE) {
+ if (debug_mode) printf("\tServer error listening to server socket\n");
+ PR_Close(listenSocket);
+ return;
+ }
+ if (debug_mode) printf("Listening on port %d\n", PORT);
+
+ {
+ PRFileDesc *newSock;
+ PRNetAddr rAddr;
+ PRInt32 loops = 0;
+ PR_fd_set rdset;
+ PRInt32 rv;
+ PRInt32 bytesRead;
+ char buf[11];
+
+ loops++;
+
+ if (debug_mode) printf("Going into accept\n");
+
+ newSock = PR_Accept(listenSocket,
+ &rAddr,
+ PR_INTERVAL_NO_TIMEOUT);
+
+ if (newSock) {
+ if (debug_mode) printf("Got connection!\n");
+ } else {
+ if (debug_mode) printf("PR_Accept failed: error code %d\n", PR_GetError());
+ else Test_Result (FAIL);
+ }
+
+ PR_FD_ZERO(&rdset);
+ PR_FD_SET(newSock, &rdset);
+
+ if (debug_mode) printf("Going into select \n");
+
+ rv = PR_Select(0, &rdset, 0, 0, PR_INTERVAL_NO_TIMEOUT);
+
+ if (debug_mode) printf("return from select is %d\n", rv);
+
+ if (PR_FD_ISSET(newSock, &rdset))
+ if (debug_mode) printf("I can't believe it- the socket is ready okay!\n");
+ else
+ if (debug_mode) printf("Damn; the select test failed...\n");
+ else Test_Result (FAIL);
+
+ strcpy(buf, "XXXXXXXXXX");
+ bytesRead = PR_Recv(newSock, buf, 10, 0, PR_INTERVAL_NO_TIMEOUT);
+ buf[10] = '\0';
+
+ if (debug_mode) printf("Recv completed with %d bytes, %s\n", bytesRead, buf);
+
+ PR_Close(newSock);
+ }
+
+}
+
+#if defined(XP_UNIX)
+
+static void NativeSelectTest(void)
+{
+ PRFileDesc *listenSocket;
+ PRNetAddr serverAddr;
+
+ if ( (listenSocket = PR_NewTCPSocket()) == NULL) {
+ if (debug_mode) printf("\tServer error creating listen socket\n");
+ return;
+ }
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(INADDR_ANY);
+
+ if ( PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) {
+ if (debug_mode) printf("\tServer error binding to server address\n");
+ PR_Close(listenSocket);
+ return;
+ }
+
+ if ( PR_Listen(listenSocket, 128) == PR_FAILURE) {
+ if (debug_mode) printf("\tServer error listening to server socket\n");
+ PR_Close(listenSocket);
+ return;
+ }
+ if (debug_mode) printf("Listening on port %d\n", PORT);
+
+ {
+ PRIntn osfd;
+ char buf[11];
+ fd_set rdset;
+ PRNetAddr rAddr;
+ PRFileDesc *newSock;
+ struct timeval timeout;
+ PRInt32 bytesRead, rv, loops = 0;
+
+ loops++;
+
+ if (debug_mode) printf("Going into accept\n");
+
+ newSock = PR_Accept(listenSocket, &rAddr, PR_INTERVAL_NO_TIMEOUT);
+
+ if (newSock) {
+ if (debug_mode) printf("Got connection!\n");
+ } else {
+ if (debug_mode) printf("PR_Accept failed: error code %d\n", PR_GetError());
+ else Test_Result (FAIL);
+ }
+
+ osfd = PR_FileDesc2NativeHandle(newSock);
+ FD_ZERO(&rdset);
+ FD_SET(osfd, &rdset);
+
+ if (debug_mode) printf("Going into select \n");
+
+
+ timeout.tv_sec = 2; timeout.tv_usec = 0;
+ rv = select(osfd + 1, &rdset, NULL, NULL, &timeout);
+
+ if (debug_mode) printf("return from select is %d\n", rv);
+
+ if (FD_ISSET(osfd, &rdset))
+ if (debug_mode)
+ printf("I can't believe it- the socket is ready okay!\n");
+ else
+ if (debug_mode) printf("Damn; the select test failed...\n");
+ else Test_Result (FAIL);
+
+ strcpy(buf, "XXXXXXXXXX");
+ bytesRead = PR_Recv(newSock, buf, 10, 0, PR_INTERVAL_NO_TIMEOUT);
+ buf[10] = '\0';
+
+ if (debug_mode) printf("Recv completed with %d bytes, %s\n", bytesRead, buf);
+
+ PR_Close(newSock);
+ }
+
+} /* NativeSelectTest */
+
+#endif /* defined(XP_UNIX) */
+
+/************************************************************************/
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+ PRInt32 tot;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+ tot = PR_IntervalToMilliseconds(stop-start);
+
+ if (debug_mode) printf("%40s: %6.2f usec avg, %d msec total\n", msg, d / count, tot);
+}
+
+void main(int argc, char **argv)
+{
+
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ if (argc > 2) {
+ count = atoi(argv[2]);
+ } else {
+ count = DEFAULT_COUNT;
+ }
+
+#if defined(XP_UNIX)
+ Measure(NativeSelectTest, "time to call 1 element select()");
+#endif
+ Measure(EmptyPRSelect, "time to call Empty PR_select()");
+ Measure(EmptyNativeSelect, "time to call Empty select()");
+ Measure(PRSelectTest, "time to call 1 element PR_select()");
+
+ if (!debug_mode) Test_Result (NOSTATUS);
+ PR_Cleanup();
+
+
+}
diff --git a/pr/tests/sem.c b/pr/tests/sem.c
new file mode 100644
index 00000000..48041ac3
--- /dev/null
+++ b/pr/tests/sem.c
@@ -0,0 +1,234 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+**
+** Name: sem.c
+**
+** Description: Tests Semaphonre functions.
+**
+** Modification History:
+** 20-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+#include "prpriv.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+/*
+ Since we don't have stdin, stdout everywhere, we will fake
+ it with our in-memory buffers called stdin and stdout.
+*/
+
+#define SBSIZE 1024
+
+#ifdef XP_MAC
+#include "prlog.h"
+#include "prsem.h"
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+#else
+#include "obsolete/prsem.h"
+#endif
+
+static char stdinBuf[SBSIZE];
+static char stdoutBuf[SBSIZE];
+
+static PRUintn stdinBufIdx = 0;
+static PRUintn stdoutBufIdx = 0;
+static PRStatus finalResult = PR_SUCCESS;
+
+
+static size_t dread (PRUintn device, char *buf, size_t bufSize)
+{
+ PRUintn i;
+
+ /* during first read call, initialize the stdinBuf buffer*/
+ if (stdinBufIdx == 0) {
+ for (i=0; i<SBSIZE; i++)
+ stdinBuf[i] = i;
+ }
+
+ /* now copy data from stdinBuf to the given buffer upto bufSize */
+ for (i=0; i<bufSize; i++) {
+ if (stdinBufIdx == SBSIZE)
+ break;
+ buf[i] = stdinBuf[stdinBufIdx++];
+ }
+
+ return i;
+}
+
+static size_t dwrite (PRUintn device, char *buf, size_t bufSize)
+{
+ PRUintn i, j;
+
+ /* copy data from the given buffer upto bufSize to stdoutBuf */
+ for (i=0; i<bufSize; i++) {
+ if (stdoutBufIdx == SBSIZE)
+ break;
+ stdoutBuf[stdoutBufIdx++] = buf[i];
+ }
+
+ /* during last write call, compare the two buffers */
+ if (stdoutBufIdx == SBSIZE)
+ for (j=0; j<SBSIZE; j++)
+ if (stdinBuf[j] != stdoutBuf[j]) {
+ if (debug_mode) printf("data mismatch for index= %d \n", j);
+ finalResult = PR_FAILURE;
+ }
+
+ return i;
+}
+
+/*------------------ Following is the real test program ---------*/
+/*
+ Program to copy standard input to standard output. The program
+ uses two threads. One reads the input and puts the data in a
+ double buffer. The other reads the buffer contents and writes
+ it to standard output.
+*/
+
+PRSemaphore *emptyBufs; /* number of empty buffers */
+PRSemaphore *fullBufs; /* number of buffers that are full */
+
+#define BSIZE 100
+
+struct {
+ char data[BSIZE];
+ PRUintn nbytes; /* number of bytes in this buffer */
+} buf[2];
+
+static void PR_CALLBACK reader(void *arg)
+{
+ PRUintn i = 0;
+ size_t nbytes;
+
+ do {
+ (void) PR_WaitSem(emptyBufs);
+ nbytes = dread(0, buf[i].data, BSIZE);
+ buf[i].nbytes = nbytes;
+ PR_PostSem(fullBufs);
+ i = (i + 1) % 2;
+ } while (nbytes > 0);
+}
+
+static void writer(void)
+{
+ PRUintn i = 0;
+ size_t nbytes;
+
+ do {
+ (void) PR_WaitSem(fullBufs);
+ nbytes = buf[i].nbytes;
+ if (nbytes > 0) {
+ nbytes = dwrite(1, buf[i].data, nbytes);
+ PR_PostSem(emptyBufs);
+ i = (i + 1) % 2;
+ }
+ } while (nbytes > 0);
+}
+
+int main(int argc, char **argv)
+{
+ PRThread *r;
+
+ PR_STDIO_INIT();
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+
+ {
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ }
+
+ /* main test */
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("sem.log");
+ debug_mode = 1;
+#endif
+
+ emptyBufs = PR_NewSem(2); /* two empty buffers */
+
+ fullBufs = PR_NewSem(0); /* zero full buffers */
+
+ /* create the reader thread */
+
+ r = PR_CreateThread(PR_USER_THREAD,
+ reader, 0,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+
+ /* Do the writer operation in this thread */
+ writer();
+
+ PR_DestroySem(emptyBufs);
+ PR_DestroySem(fullBufs);
+
+ if (finalResult == PR_SUCCESS) {
+ if (debug_mode) printf("sem Test Passed.\n");
+ }
+ else{
+ if (debug_mode) printf("sem Test Failed.\n");
+ failed_already=1;
+ }
+ PR_Cleanup();
+ if(failed_already)
+ return 1;
+ else
+ return 0;
+}
diff --git a/pr/tests/server_test.c b/pr/tests/server_test.c
new file mode 100644
index 00000000..57d935d7
--- /dev/null
+++ b/pr/tests/server_test.c
@@ -0,0 +1,617 @@
+/* -*- 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 server simulates a server running in loopback mode.
+**
+** The idea is that a single server is created. The server initially creates
+** a number of worker threads. Then, with the server running, a number of
+** clients are created which start requesting service from the server.
+**
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+#include "prttools.h"
+
+#include "nspr.h"
+#include "pprthred.h"
+
+#include <string.h>
+
+#define PORT 15004
+#define STACKSIZE 0
+
+static int _iterations = 1000;
+static int _clients = 1;
+static int _client_data = 250;
+static int _server_data = (8*1024);
+
+static PRThreadScope ServerScope, ClientScope;
+
+#define SERVER "Server"
+#define MAIN "Main"
+
+#define SERVER_STATE_STARTUP 0
+#define SERVER_STATE_READY 1
+#define SERVER_STATE_DYING 2
+#define SERVER_STATE_DEAD 4
+int ServerState;
+PRLock *ServerStateCVLock;
+PRCondVar *ServerStateCV;
+
+#undef DEBUGPRINTS
+#ifdef DEBUGPRINTS
+#define DPRINTF printf
+#else
+#define DPRINTF
+#endif
+
+
+/***********************************************************************
+** PRIVATE FUNCTION: Test_Result
+** DESCRIPTION: Used in conjunction with the regress tool, prints out the
+** status of the test case.
+** INPUTS: PASS/FAIL
+** OUTPUTS: None
+** RETURN: None
+** SIDE EFFECTS:
+**
+** RESTRICTIONS:
+** None
+** MEMORY: NA
+** ALGORITHM: Determine what the status is and print accordingly.
+**
+***********************************************************************/
+
+
+static Test_Result (int result)
+{
+ switch (result)
+ {
+ case PASS:
+ printf ("PASS\n");
+ break;
+ case FAIL:
+ printf ("FAIL\n");
+ break;
+ case NOSTATUS:
+ printf ("NOSTATUS\n");
+ break;
+ default:
+ break;
+ }
+}
+
+static void do_work(void);
+
+/* --- Server state functions --------------------------------------------- */
+void
+SetServerState(char *waiter, PRInt32 state)
+{
+ PR_Lock(ServerStateCVLock);
+ ServerState = state;
+ PR_NotifyCondVar(ServerStateCV);
+
+ if (debug_mode) DPRINTF("\t%s changed state to %d\n", waiter, state);
+
+ PR_Unlock(ServerStateCVLock);
+}
+
+int
+WaitServerState(char *waiter, PRInt32 state)
+{
+ PRInt32 rv;
+
+ PR_Lock(ServerStateCVLock);
+
+ if (debug_mode) DPRINTF("\t%s waiting for state %d\n", waiter, state);
+
+ while(!(ServerState & state))
+ PR_WaitCondVar(ServerStateCV, PR_INTERVAL_NO_TIMEOUT);
+ rv = ServerState;
+
+ if (debug_mode) DPRINTF("\t%s resuming from wait for state %d; state now %d\n",
+ waiter, state, ServerState);
+ PR_Unlock(ServerStateCVLock);
+
+ return rv;
+}
+
+/* --- Server Functions ------------------------------------------- */
+
+PRLock *workerThreadsLock;
+PRInt32 workerThreads;
+PRInt32 workerThreadsBusy;
+
+void
+WorkerThreadFunc(void *_listenSock)
+{
+ PRFileDesc *listenSock = (PRFileDesc *)_listenSock;
+ PRInt32 bytesRead;
+ PRInt32 bytesWritten;
+ char *dataBuf;
+ char *sendBuf;
+
+ if (debug_mode) DPRINTF("\tServer buffer is %d bytes; %d data, %d netaddrs\n",
+ _client_data+(4*sizeof(PRNetAddr)), _client_data, (4*sizeof(PRNetAddr)));
+ dataBuf = (char *)PR_MALLOC(_client_data *sizeof(char) + 4*sizeof(PRNetAddr));
+ if (!dataBuf)
+ if (debug_mode) printf("\tServer could not malloc space!?\n");
+ sendBuf = (char *)PR_MALLOC(_server_data *sizeof(char));
+ if (!sendBuf)
+ if (debug_mode) printf("\tServer could not malloc space!?\n");
+
+ if (debug_mode) DPRINTF("\tServer worker thread running\n");
+
+ while(1) {
+ PRInt32 bytesToRead = _client_data;
+ PRInt32 bytesToWrite = _server_data;
+ PRFileDesc *newSock;
+ PRNetAddr *rAddr;
+ PRInt32 loops = 0;
+
+ loops++;
+
+ if (debug_mode) DPRINTF("\tServer thread going into accept\n");
+
+ bytesRead = PR_AcceptRead(listenSock,
+ &newSock,
+ &rAddr,
+ dataBuf,
+ bytesToRead,
+ PR_INTERVAL_NO_TIMEOUT);
+
+ if (bytesRead < 0) {
+ if (debug_mode) printf("\tServer error in accept (%d)\n", bytesRead);
+ continue;
+ }
+
+ if (debug_mode) DPRINTF("\tServer accepted connection (%d bytes)\n", bytesRead);
+
+ PR_AtomicIncrement(&workerThreadsBusy);
+ if (workerThreadsBusy == workerThreads) {
+
+ PR_Lock(workerThreadsLock);
+ if (workerThreadsBusy == workerThreads) {
+ PRThread *WorkerThread;
+
+ WorkerThread = PR_CreateThread(
+ PR_SYSTEM_THREAD,
+ WorkerThreadFunc,
+ listenSock,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_UNJOINABLE_THREAD,
+ STACKSIZE);
+
+ if (!WorkerThread)
+ if (debug_mode) printf("Error creating client thread %d\n", workerThreads);
+ else {
+ PR_AtomicIncrement(&workerThreads);
+ if (debug_mode) DPRINTF("\tServer creates worker (%d)\n", workerThreads);
+ }
+ }
+ PR_Unlock(workerThreadsLock);
+ }
+
+ bytesToRead -= bytesRead;
+ while (bytesToRead) {
+ bytesRead = PR_Recv(newSock,
+ dataBuf,
+ bytesToRead,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (bytesRead < 0) {
+ if (debug_mode) printf("\tServer error receiving data (%d)\n", bytesRead);
+ continue;
+ }
+ if (debug_mode) DPRINTF("\tServer received %d bytes\n", bytesRead);
+ }
+
+ bytesWritten = PR_Send(newSock,
+ sendBuf,
+ bytesToWrite,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (bytesWritten != _server_data)
+ if (debug_mode) printf("\tError sending data to client (%d, %d)\n",
+ bytesWritten, PR_GetOSError());
+ else
+ if (debug_mode) DPRINTF("\tServer sent %d bytes\n", bytesWritten);
+
+ PR_Close(newSock);
+ PR_AtomicDecrement(&workerThreadsBusy);
+ }
+}
+
+PRFileDesc *
+ServerSetup(void)
+{
+ PRFileDesc *listenSocket;
+ PRNetAddr serverAddr;
+ PRThread *WorkerThread;
+
+ if ( (listenSocket = PR_NewTCPSocket()) == NULL) {
+ if (debug_mode) printf("\tServer error creating listen socket\n");
+ else Test_Result(FAIL);
+ return NULL;
+ }
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(INADDR_ANY);
+
+ if ( PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) {
+ if (debug_mode) printf("\tServer error binding to server address: OS error %d\n",
+ PR_GetOSError());
+ else Test_Result(FAIL);
+ PR_Close(listenSocket);
+ return NULL;
+ }
+
+ if ( PR_Listen(listenSocket, 128) == PR_FAILURE) {
+ if (debug_mode) printf("\tServer error listening to server socket\n");
+ else Test_Result(FAIL);
+ PR_Close(listenSocket);
+
+ return NULL;
+ }
+
+ /* Create Clients */
+ workerThreads = 0;
+ workerThreadsBusy = 0;
+
+ workerThreadsLock = PR_NewLock();
+
+ WorkerThread = PR_CreateThread(
+ PR_SYSTEM_THREAD,
+ WorkerThreadFunc,
+ listenSocket,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_UNJOINABLE_THREAD,
+ STACKSIZE);
+
+ if (!WorkerThread) {
+ if (debug_mode) printf("error creating working thread\n");
+ PR_Close(listenSocket);
+ return NULL;
+ }
+ PR_AtomicIncrement(&workerThreads);
+ if (debug_mode) DPRINTF("\tServer created primordial worker thread\n");
+
+ return listenSocket;
+}
+
+/* The main server loop */
+void
+ServerThreadFunc(void *unused)
+{
+ PRFileDesc *listenSocket;
+
+ /* Do setup */
+ listenSocket = ServerSetup();
+
+ if (!listenSocket) {
+ SetServerState(SERVER, SERVER_STATE_DEAD);
+ } else {
+
+ if (debug_mode) DPRINTF("\tServer up\n");
+
+ /* Tell clients they can start now. */
+ SetServerState(SERVER, SERVER_STATE_READY);
+
+ /* Now wait for server death signal */
+ WaitServerState(SERVER, SERVER_STATE_DYING);
+
+ /* Cleanup */
+ SetServerState(SERVER, SERVER_STATE_DEAD);
+ }
+}
+
+/* --- Client Functions ------------------------------------------- */
+
+PRInt32 numRequests;
+PRInt32 numClients;
+PRMonitor *clientMonitor;
+
+void
+ClientThreadFunc(void *unused)
+{
+ PRNetAddr serverAddr;
+ PRFileDesc *clientSocket;
+ char *sendBuf;
+ char *recvBuf;
+ PRInt32 rv;
+ PRInt32 bytesNeeded;
+
+ sendBuf = (char *)PR_MALLOC(_client_data * sizeof(char));
+ if (!sendBuf)
+ if (debug_mode) printf("\tClient could not malloc space!?\n");
+ recvBuf = (char *)PR_MALLOC(_server_data * sizeof(char));
+ if (!recvBuf)
+ if (debug_mode) printf("\tClient could not malloc space!?\n");
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(INADDR_LOOPBACK);
+
+ while(numRequests > 0) {
+
+ if ( (numRequests % 10) == 0 )
+ if (debug_mode) printf(".");
+ if (debug_mode) DPRINTF("\tClient starting request %d\n", numRequests);
+
+ clientSocket = PR_NewTCPSocket();
+ if (!clientSocket) {
+ if (debug_mode) printf("Client error creating socket: OS error %d\n",
+ PR_GetOSError());
+ continue;
+ }
+
+ if (debug_mode) DPRINTF("\tClient connecting\n");
+
+ rv = PR_Connect(clientSocket,
+ &serverAddr,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (!clientSocket) {
+ if (debug_mode) printf("\tClient error connecting\n");
+ continue;
+ }
+
+ if (debug_mode) DPRINTF("\tClient connected\n");
+
+ rv = PR_Send(clientSocket,
+ sendBuf,
+ _client_data,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv != _client_data) {
+ if (debug_mode) printf("Client error sending data (%d)\n", rv);
+ PR_Close(clientSocket);
+ continue;
+ }
+
+ if (debug_mode) DPRINTF("\tClient sent %d bytes\n", rv);
+
+ bytesNeeded = _server_data;
+ while(bytesNeeded) {
+ rv = PR_Recv(clientSocket,
+ recvBuf,
+ bytesNeeded,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv <= 0) {
+ if (debug_mode) printf("Client error receiving data (%d) (%d/%d)\n",
+ rv, (_server_data - bytesNeeded), _server_data);
+ break;
+ }
+ if (debug_mode) DPRINTF("\tClient received %d bytes; need %d more\n", rv, bytesNeeded - rv);
+ bytesNeeded -= rv;
+ }
+
+ PR_Close(clientSocket);
+
+ PR_AtomicDecrement(&numRequests);
+ }
+
+ PR_EnterMonitor(clientMonitor);
+ --numClients;
+ PR_Notify(clientMonitor);
+ PR_ExitMonitor(clientMonitor);
+
+ PR_DELETE(sendBuf);
+ PR_DELETE(recvBuf);
+}
+
+void
+RunClients(void)
+{
+ PRInt32 index;
+
+ numRequests = _iterations;
+ numClients = _clients;
+ clientMonitor = PR_NewMonitor();
+
+ for (index=0; index<_clients; index++) {
+ PRThread *clientThread;
+
+
+ clientThread = PR_CreateThread(
+ PR_USER_THREAD,
+ ClientThreadFunc,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ ClientScope,
+ PR_UNJOINABLE_THREAD,
+ STACKSIZE);
+
+ if (!clientThread) {
+ if (debug_mode) printf("\terror creating client thread %d\n", index);
+ } else
+ if (debug_mode) DPRINTF("\tMain created client %d/%d\n", index+1, _clients);
+
+ }
+
+ PR_EnterMonitor(clientMonitor);
+ while(numClients)
+ PR_Wait(clientMonitor, PR_INTERVAL_NO_TIMEOUT);
+ PR_ExitMonitor(clientMonitor);
+}
+
+/* --- Main Function ---------------------------------------------- */
+
+static
+void do_work()
+{
+ PRThread *ServerThread;
+ PRInt32 state;
+
+ SetServerState(MAIN, SERVER_STATE_STARTUP);
+ ServerThread = PR_CreateThread(
+ PR_USER_THREAD,
+ ServerThreadFunc,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_JOINABLE_THREAD,
+ STACKSIZE);
+ if (!ServerThread) {
+ if (debug_mode) printf("error creating main server thread\n");
+ return;
+ }
+
+ /* Wait for server to be ready */
+ state = WaitServerState(MAIN, SERVER_STATE_READY|SERVER_STATE_DEAD);
+
+ if (!(state & SERVER_STATE_DEAD)) {
+ /* Run Test Clients */
+ RunClients();
+
+ /* Send death signal to server */
+ SetServerState(MAIN, SERVER_STATE_DYING);
+ }
+
+ PR_JoinThread(ServerThread);
+}
+
+static void do_workUU(void)
+{
+ ServerScope = PR_LOCAL_THREAD;
+ ClientScope = PR_LOCAL_THREAD;
+ do_work();
+}
+
+static void do_workUK(void)
+{
+ ServerScope = PR_LOCAL_THREAD;
+ ClientScope = PR_GLOBAL_THREAD;
+ do_work();
+}
+
+static void do_workKU(void)
+{
+ ServerScope = PR_GLOBAL_THREAD;
+ ClientScope = PR_LOCAL_THREAD;
+ do_work();
+}
+
+static void do_workKK(void)
+{
+ ServerScope = PR_GLOBAL_THREAD;
+ ClientScope = PR_GLOBAL_THREAD;
+ do_work();
+}
+
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+
+ if (debug_mode) printf("\n%40s: %6.2f usec\n", msg, d / _iterations);
+}
+
+
+main(int argc, char **argv)
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+ if (debug_mode) {
+ printf("Enter number of iterations: \n");
+ scanf("%d", &_iterations);
+ printf("Enter number of clients : \n");
+ scanf("%d", &_clients);
+ printf("Enter size of client data : \n");
+ scanf("%d", &_client_data);
+ printf("Enter size of server data : \n");
+ scanf("%d", &_server_data);
+ }
+ else {
+ _iterations = 10;
+ _clients = 1;
+ _client_data = 10;
+ _server_data = 10;
+ }
+
+ if (debug_mode) {
+ printf("\n\n%d iterations with %d client threads.\n",
+ _iterations, _clients);
+ printf("Sending %d bytes of client data and %d bytes of server data\n",
+ _client_data, _server_data);
+ }
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ PR_SetThreadRecycleMode(64);
+
+ ServerStateCVLock = PR_NewLock();
+ ServerStateCV = PR_NewCondVar(ServerStateCVLock);
+
+ Measure(do_workUU, "server loop user/user");
+ #if 0
+ Measure(do_workUK, "server loop user/kernel");
+ Measure(do_workKU, "server loop kernel/user");
+ Measure(do_workKK, "server loop kernel/kernel");
+ #endif
+
+ PR_Cleanup();
+
+ return 0;
+}
diff --git a/pr/tests/servr_kk.c b/pr/tests/servr_kk.c
new file mode 100644
index 00000000..1efc7c61
--- /dev/null
+++ b/pr/tests/servr_kk.c
@@ -0,0 +1,584 @@
+/* -*- 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 server simulates a server running in loopback mode.
+**
+** The idea is that a single server is created. The server initially creates
+** a number of worker threads. Then, with the server running, a number of
+** clients are created which start requesting service from the server.
+**
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+#include "pprthred.h"
+
+#include <string.h>
+
+#define PORT 15004
+#define STACKSIZE 0
+
+static int _iterations = 1000;
+static int _clients = 1;
+static int _client_data = 250;
+static int _server_data = (8*1024);
+
+static PRThreadScope ServerScope, ClientScope;
+
+#define SERVER "Server"
+#define MAIN "Main"
+
+#define SERVER_STATE_STARTUP 0
+#define SERVER_STATE_READY 1
+#define SERVER_STATE_DYING 2
+#define SERVER_STATE_DEAD 4
+int ServerState;
+PRLock *ServerStateCVLock;
+PRCondVar *ServerStateCV;
+
+#undef DEBUGPRINTS
+#ifdef DEBUGPRINTS
+#define DPRINTF printf
+#else
+#define DPRINTF
+#endif
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+static void do_work(void);
+
+/* --- Server state functions --------------------------------------------- */
+void
+SetServerState(char *waiter, PRInt32 state)
+{
+ PR_Lock(ServerStateCVLock);
+ ServerState = state;
+ PR_NotifyCondVar(ServerStateCV);
+
+ if (debug_mode) DPRINTF("\t%s changed state to %d\n", waiter, state);
+
+ PR_Unlock(ServerStateCVLock);
+}
+
+int
+WaitServerState(char *waiter, PRInt32 state)
+{
+ PRInt32 rv;
+
+ PR_Lock(ServerStateCVLock);
+
+ if (debug_mode) DPRINTF("\t%s waiting for state %d\n", waiter, state);
+
+ while(!(ServerState & state))
+ PR_WaitCondVar(ServerStateCV, PR_INTERVAL_NO_TIMEOUT);
+ rv = ServerState;
+
+ if (debug_mode) DPRINTF("\t%s resuming from wait for state %d; state now %d\n",
+ waiter, state, ServerState);
+ PR_Unlock(ServerStateCVLock);
+
+ return rv;
+}
+
+/* --- Server Functions ------------------------------------------- */
+
+PRLock *workerThreadsLock;
+PRInt32 workerThreads;
+PRInt32 workerThreadsBusy;
+
+void
+WorkerThreadFunc(void *_listenSock)
+{
+ PRFileDesc *listenSock = (PRFileDesc *)_listenSock;
+ PRInt32 bytesRead;
+ PRInt32 bytesWritten;
+ char *dataBuf;
+ char *sendBuf;
+
+ if (debug_mode) DPRINTF("\tServer buffer is %d bytes; %d data, %d netaddrs\n",
+ _client_data+(4*sizeof(PRNetAddr)), _client_data, (4*sizeof(PRNetAddr)));
+ dataBuf = (char *)PR_MALLOC(_client_data *sizeof(char) + 4*sizeof(PRNetAddr));
+ if (!dataBuf)
+ if (debug_mode) printf("\tServer could not malloc space!?\n");
+ sendBuf = (char *)PR_MALLOC(_server_data *sizeof(char));
+ if (!sendBuf)
+ if (debug_mode) printf("\tServer could not malloc space!?\n");
+
+ if (debug_mode) DPRINTF("\tServer worker thread running\n");
+
+ while(1) {
+ PRInt32 bytesToRead = _client_data;
+ PRInt32 bytesToWrite = _server_data;
+ PRFileDesc *newSock;
+ PRNetAddr *rAddr;
+ PRInt32 loops = 0;
+
+ loops++;
+
+ if (debug_mode) DPRINTF("\tServer thread going into accept\n");
+
+ bytesRead = PR_AcceptRead(listenSock,
+ &newSock,
+ &rAddr,
+ dataBuf,
+ bytesToRead,
+ PR_INTERVAL_NO_TIMEOUT);
+
+ if (bytesRead < 0) {
+ if (debug_mode) printf("\tServer error in accept (%d)\n", bytesRead);
+ continue;
+ }
+
+ if (debug_mode) DPRINTF("\tServer accepted connection (%d bytes)\n", bytesRead);
+
+ PR_AtomicIncrement(&workerThreadsBusy);
+ if (workerThreadsBusy == workerThreads) {
+
+ PR_Lock(workerThreadsLock);
+ if (workerThreadsBusy == workerThreads) {
+ PRThread *WorkerThread;
+
+ WorkerThread = PR_CreateThread(
+ PR_SYSTEM_THREAD,
+ WorkerThreadFunc,
+ listenSock,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_UNJOINABLE_THREAD,
+ STACKSIZE);
+
+ if (!WorkerThread)
+ if (debug_mode) printf("Error creating client thread %d\n", workerThreads);
+ else {
+ PR_AtomicIncrement(&workerThreads);
+ if (debug_mode) DPRINTF("\tServer creates worker (%d)\n", workerThreads);
+ }
+ }
+ PR_Unlock(workerThreadsLock);
+ }
+
+ bytesToRead -= bytesRead;
+ while (bytesToRead) {
+ bytesRead = PR_Recv(newSock,
+ dataBuf,
+ bytesToRead,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (bytesRead < 0) {
+ if (debug_mode) printf("\tServer error receiving data (%d)\n", bytesRead);
+ continue;
+ }
+ if (debug_mode) DPRINTF("\tServer received %d bytes\n", bytesRead);
+ }
+
+ bytesWritten = PR_Send(newSock,
+ sendBuf,
+ bytesToWrite,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (bytesWritten != _server_data)
+ if (debug_mode) printf("\tError sending data to client (%d, %d)\n",
+ bytesWritten, PR_GetOSError());
+ else
+ if (debug_mode) DPRINTF("\tServer sent %d bytes\n", bytesWritten);
+
+ PR_Close(newSock);
+ PR_AtomicDecrement(&workerThreadsBusy);
+ }
+}
+
+PRFileDesc *
+ServerSetup(void)
+{
+ PRFileDesc *listenSocket;
+ PRNetAddr serverAddr;
+ PRThread *WorkerThread;
+
+ if ( (listenSocket = PR_NewTCPSocket()) == NULL) {
+ if (debug_mode) printf("\tServer error creating listen socket\n");
+ else failed_already=1;
+ return NULL;
+ }
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = PR_AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+
+ if ( PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) {
+ if (debug_mode) printf("\tServer error binding to server address: OS error %d\n",
+ PR_GetOSError());
+ else failed_already=1;
+ PR_Close(listenSocket);
+ return NULL;
+ }
+
+ if ( PR_Listen(listenSocket, 128) == PR_FAILURE) {
+ if (debug_mode) printf("\tServer error listening to server socket\n");
+ else failed_already=1;
+ PR_Close(listenSocket);
+
+ return NULL;
+ }
+
+ /* Create Clients */
+ workerThreads = 0;
+ workerThreadsBusy = 0;
+
+ workerThreadsLock = PR_NewLock();
+
+ WorkerThread = PR_CreateThread(
+ PR_SYSTEM_THREAD,
+ WorkerThreadFunc,
+ listenSocket,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_UNJOINABLE_THREAD,
+ STACKSIZE);
+
+ if (!WorkerThread) {
+ if (debug_mode) printf("error creating working thread\n");
+ PR_Close(listenSocket);
+ return NULL;
+ }
+ PR_AtomicIncrement(&workerThreads);
+ if (debug_mode) DPRINTF("\tServer created primordial worker thread\n");
+
+ return listenSocket;
+}
+
+/* The main server loop */
+void
+ServerThreadFunc(void *unused)
+{
+ PRFileDesc *listenSocket;
+
+ /* Do setup */
+ listenSocket = ServerSetup();
+
+ if (!listenSocket) {
+ SetServerState(SERVER, SERVER_STATE_DEAD);
+ } else {
+
+ if (debug_mode) DPRINTF("\tServer up\n");
+
+ /* Tell clients they can start now. */
+ SetServerState(SERVER, SERVER_STATE_READY);
+
+ /* Now wait for server death signal */
+ WaitServerState(SERVER, SERVER_STATE_DYING);
+
+ /* Cleanup */
+ SetServerState(SERVER, SERVER_STATE_DEAD);
+ }
+}
+
+/* --- Client Functions ------------------------------------------- */
+
+PRInt32 numRequests;
+PRInt32 numClients;
+PRMonitor *clientMonitor;
+
+void
+ClientThreadFunc(void *unused)
+{
+ PRNetAddr serverAddr;
+ PRFileDesc *clientSocket;
+ char *sendBuf;
+ char *recvBuf;
+ PRInt32 rv;
+ PRInt32 bytesNeeded;
+
+ sendBuf = (char *)PR_MALLOC(_client_data * sizeof(char));
+ if (!sendBuf)
+ if (debug_mode) printf("\tClient could not malloc space!?\n");
+ recvBuf = (char *)PR_MALLOC(_server_data * sizeof(char));
+ if (!recvBuf)
+ if (debug_mode) printf("\tClient could not malloc space!?\n");
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = PR_AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+
+ while(numRequests > 0) {
+
+ if ( (numRequests % 10) == 0 )
+ if (debug_mode) printf(".");
+ if (debug_mode) DPRINTF("\tClient starting request %d\n", numRequests);
+
+ clientSocket = PR_NewTCPSocket();
+ if (!clientSocket) {
+ if (debug_mode) printf("Client error creating socket: OS error %d\n",
+ PR_GetOSError());
+ continue;
+ }
+
+ if (debug_mode) DPRINTF("\tClient connecting\n");
+
+ rv = PR_Connect(clientSocket,
+ &serverAddr,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (!clientSocket) {
+ if (debug_mode) printf("\tClient error connecting\n");
+ continue;
+ }
+
+ if (debug_mode) DPRINTF("\tClient connected\n");
+
+ rv = PR_Send(clientSocket,
+ sendBuf,
+ _client_data,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv != _client_data) {
+ if (debug_mode) printf("Client error sending data (%d)\n", rv);
+ PR_Close(clientSocket);
+ continue;
+ }
+
+ if (debug_mode) DPRINTF("\tClient sent %d bytes\n", rv);
+
+ bytesNeeded = _server_data;
+ while(bytesNeeded) {
+ rv = PR_Recv(clientSocket,
+ recvBuf,
+ bytesNeeded,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv <= 0) {
+ if (debug_mode) printf("Client error receiving data (%d) (%d/%d)\n",
+ rv, (_server_data - bytesNeeded), _server_data);
+ break;
+ }
+ if (debug_mode) DPRINTF("\tClient received %d bytes; need %d more\n", rv, bytesNeeded - rv);
+ bytesNeeded -= rv;
+ }
+
+ PR_Close(clientSocket);
+
+ PR_AtomicDecrement(&numRequests);
+ }
+
+ PR_EnterMonitor(clientMonitor);
+ --numClients;
+ PR_Notify(clientMonitor);
+ PR_ExitMonitor(clientMonitor);
+
+ PR_DELETE(sendBuf);
+ PR_DELETE(recvBuf);
+}
+
+void
+RunClients(void)
+{
+ PRInt32 index;
+
+ numRequests = _iterations;
+ numClients = _clients;
+ clientMonitor = PR_NewMonitor();
+
+ for (index=0; index<_clients; index++) {
+ PRThread *clientThread;
+
+
+ clientThread = PR_CreateThread(
+ PR_USER_THREAD,
+ ClientThreadFunc,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ ClientScope,
+ PR_UNJOINABLE_THREAD,
+ STACKSIZE);
+
+ if (!clientThread) {
+ if (debug_mode) printf("\terror creating client thread %d\n", index);
+ } else
+ if (debug_mode) DPRINTF("\tMain created client %d/%d\n", index+1, _clients);
+
+ }
+
+ PR_EnterMonitor(clientMonitor);
+ while(numClients)
+ PR_Wait(clientMonitor, PR_INTERVAL_NO_TIMEOUT);
+ PR_ExitMonitor(clientMonitor);
+}
+
+/* --- Main Function ---------------------------------------------- */
+
+static
+void do_work()
+{
+ PRThread *ServerThread;
+ PRInt32 state;
+
+ SetServerState(MAIN, SERVER_STATE_STARTUP);
+ ServerThread = PR_CreateThread(
+ PR_USER_THREAD,
+ ServerThreadFunc,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_JOINABLE_THREAD,
+ STACKSIZE);
+ if (!ServerThread) {
+ if (debug_mode) printf("error creating main server thread\n");
+ return;
+ }
+
+ /* Wait for server to be ready */
+ state = WaitServerState(MAIN, SERVER_STATE_READY|SERVER_STATE_DEAD);
+
+ if (!(state & SERVER_STATE_DEAD)) {
+ /* Run Test Clients */
+ RunClients();
+
+ /* Send death signal to server */
+ SetServerState(MAIN, SERVER_STATE_DYING);
+ }
+
+ PR_JoinThread(ServerThread);
+}
+
+static void do_workUU(void)
+{
+ ServerScope = PR_LOCAL_THREAD;
+ ClientScope = PR_LOCAL_THREAD;
+ do_work();
+}
+
+static void do_workUK(void)
+{
+ ServerScope = PR_LOCAL_THREAD;
+ ClientScope = PR_GLOBAL_THREAD;
+ do_work();
+}
+
+static void do_workKU(void)
+{
+ ServerScope = PR_GLOBAL_THREAD;
+ ClientScope = PR_LOCAL_THREAD;
+ do_work();
+}
+
+static void do_workKK(void)
+{
+ ServerScope = PR_GLOBAL_THREAD;
+ ClientScope = PR_GLOBAL_THREAD;
+ do_work();
+}
+
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+
+ if (debug_mode) printf("\n%40s: %6.2f usec\n", msg, d / _iterations);
+}
+
+
+main(int argc, char **argv)
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+ if (debug_mode) {
+ printf("Enter number of iterations: \n");
+ scanf("%d", &_iterations);
+ printf("Enter number of clients : \n");
+ scanf("%d", &_clients);
+ printf("Enter size of client data : \n");
+ scanf("%d", &_client_data);
+ printf("Enter size of server data : \n");
+ scanf("%d", &_server_data);
+ }
+ else {
+ _iterations = 7;
+ _clients = 7;
+ _client_data = 100;
+ _server_data = 100;
+ }
+
+ if (debug_mode) {
+ printf("\n\n%d iterations with %d client threads.\n",
+ _iterations, _clients);
+ printf("Sending %d bytes of client data and %d bytes of server data\n",
+ _client_data, _server_data);
+ }
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ PR_SetThreadRecycleMode(64);
+
+ ServerStateCVLock = PR_NewLock();
+ ServerStateCV = PR_NewCondVar(ServerStateCVLock);
+
+
+ Measure(do_workKK, "server loop kernel/kernel");
+
+ PR_Cleanup();
+
+ if(failed_already)
+ return 1;
+ else
+ return 0;
+
+}
diff --git a/pr/tests/servr_ku.c b/pr/tests/servr_ku.c
new file mode 100644
index 00000000..e394ec4f
--- /dev/null
+++ b/pr/tests/servr_ku.c
@@ -0,0 +1,564 @@
+/* -*- 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 server simulates a server running in loopback mode.
+**
+** The idea is that a single server is created. The server initially creates
+** a number of worker threads. Then, with the server running, a number of
+** clients are created which start requesting service from the server.
+**
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+#include "pprthred.h"
+
+#include <string.h>
+
+#define PORT 15004
+#define STACKSIZE 0
+
+static int _iterations = 1000;
+static int _clients = 1;
+static int _client_data = 250;
+static int _server_data = (8*1024);
+
+static PRThreadScope ServerScope, ClientScope;
+
+#define SERVER "Server"
+#define MAIN "Main"
+
+#define SERVER_STATE_STARTUP 0
+#define SERVER_STATE_READY 1
+#define SERVER_STATE_DYING 2
+#define SERVER_STATE_DEAD 4
+int ServerState;
+PRLock *ServerStateCVLock;
+PRCondVar *ServerStateCV;
+
+#undef DEBUGPRINTS
+#ifdef DEBUGPRINTS
+#define DPRINTF printf
+#else
+#define DPRINTF
+#endif
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+static void do_work(void);
+
+/* --- Server state functions --------------------------------------------- */
+void
+SetServerState(char *waiter, PRInt32 state)
+{
+ PR_Lock(ServerStateCVLock);
+ ServerState = state;
+ PR_NotifyCondVar(ServerStateCV);
+
+ if (debug_mode) DPRINTF("\t%s changed state to %d\n", waiter, state);
+
+ PR_Unlock(ServerStateCVLock);
+}
+
+int
+WaitServerState(char *waiter, PRInt32 state)
+{
+ PRInt32 rv;
+
+ PR_Lock(ServerStateCVLock);
+
+ if (debug_mode) DPRINTF("\t%s waiting for state %d\n", waiter, state);
+
+ while(!(ServerState & state))
+ PR_WaitCondVar(ServerStateCV, PR_INTERVAL_NO_TIMEOUT);
+ rv = ServerState;
+
+ if (debug_mode) DPRINTF("\t%s resuming from wait for state %d; state now %d\n",
+ waiter, state, ServerState);
+ PR_Unlock(ServerStateCVLock);
+
+ return rv;
+}
+
+/* --- Server Functions ------------------------------------------- */
+
+PRLock *workerThreadsLock;
+PRInt32 workerThreads;
+PRInt32 workerThreadsBusy;
+
+void
+WorkerThreadFunc(void *_listenSock)
+{
+ PRFileDesc *listenSock = (PRFileDesc *)_listenSock;
+ PRInt32 bytesRead;
+ PRInt32 bytesWritten;
+ char *dataBuf;
+ char *sendBuf;
+
+ if (debug_mode) DPRINTF("\tServer buffer is %d bytes; %d data, %d netaddrs\n",
+ _client_data+(4*sizeof(PRNetAddr)), _client_data, (4*sizeof(PRNetAddr)));
+ dataBuf = (char *)PR_MALLOC(_client_data *sizeof(char) + 4*sizeof(PRNetAddr));
+ if (!dataBuf)
+ if (debug_mode) printf("\tServer could not malloc space!?\n");
+ sendBuf = (char *)PR_MALLOC(_server_data *sizeof(char));
+ if (!sendBuf)
+ if (debug_mode) printf("\tServer could not malloc space!?\n");
+
+ if (debug_mode) DPRINTF("\tServer worker thread running\n");
+
+ while(1) {
+ PRInt32 bytesToRead = _client_data;
+ PRInt32 bytesToWrite = _server_data;
+ PRFileDesc *newSock;
+ PRNetAddr *rAddr;
+ PRInt32 loops = 0;
+
+ loops++;
+
+ if (debug_mode) DPRINTF("\tServer thread going into accept\n");
+
+ bytesRead = PR_AcceptRead(listenSock,
+ &newSock,
+ &rAddr,
+ dataBuf,
+ bytesToRead,
+ PR_INTERVAL_NO_TIMEOUT);
+
+ if (bytesRead < 0) {
+ if (debug_mode) printf("\tServer error in accept (%d)\n", bytesRead);
+ continue;
+ }
+
+ if (debug_mode) DPRINTF("\tServer accepted connection (%d bytes)\n", bytesRead);
+
+ PR_AtomicIncrement(&workerThreadsBusy);
+ if (workerThreadsBusy == workerThreads) {
+
+ PR_Lock(workerThreadsLock);
+ if (workerThreadsBusy == workerThreads) {
+ PRThread *WorkerThread;
+
+ WorkerThread = PR_CreateThread(
+ PR_SYSTEM_THREAD,
+ WorkerThreadFunc,
+ listenSock,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_UNJOINABLE_THREAD,
+ STACKSIZE);
+
+ if (!WorkerThread)
+ if (debug_mode) printf("Error creating client thread %d\n", workerThreads);
+ else {
+ PR_AtomicIncrement(&workerThreads);
+ if (debug_mode) DPRINTF("\tServer creates worker (%d)\n", workerThreads);
+ }
+ }
+ PR_Unlock(workerThreadsLock);
+ }
+
+ bytesToRead -= bytesRead;
+ while (bytesToRead) {
+ bytesRead = PR_Recv(newSock,
+ dataBuf,
+ bytesToRead,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (bytesRead < 0) {
+ if (debug_mode) printf("\tServer error receiving data (%d)\n", bytesRead);
+ continue;
+ }
+ if (debug_mode) DPRINTF("\tServer received %d bytes\n", bytesRead);
+ }
+
+ bytesWritten = PR_Send(newSock,
+ sendBuf,
+ bytesToWrite,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (bytesWritten != _server_data)
+ if (debug_mode) printf("\tError sending data to client (%d, %d)\n",
+ bytesWritten, PR_GetOSError());
+ else
+ if (debug_mode) DPRINTF("\tServer sent %d bytes\n", bytesWritten);
+
+ PR_Close(newSock);
+ PR_AtomicDecrement(&workerThreadsBusy);
+ }
+}
+
+PRFileDesc *
+ServerSetup(void)
+{
+ PRFileDesc *listenSocket;
+ PRNetAddr serverAddr;
+ PRThread *WorkerThread;
+
+ if ( (listenSocket = PR_NewTCPSocket()) == NULL) {
+ if (debug_mode) printf("\tServer error creating listen socket\n");
+ else failed_already=1;
+ return NULL;
+ }
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = PR_AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+
+ if ( PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) {
+ if (debug_mode) printf("\tServer error binding to server address: OS error %d\n",
+ PR_GetOSError());
+ else failed_already=1;
+ PR_Close(listenSocket);
+ return NULL;
+ }
+
+ if ( PR_Listen(listenSocket, 128) == PR_FAILURE) {
+ if (debug_mode) printf("\tServer error listening to server socket\n");
+ else failed_already=1;
+ PR_Close(listenSocket);
+
+ return NULL;
+ }
+
+ /* Create Clients */
+ workerThreads = 0;
+ workerThreadsBusy = 0;
+
+ workerThreadsLock = PR_NewLock();
+
+ WorkerThread = PR_CreateThread(
+ PR_SYSTEM_THREAD,
+ WorkerThreadFunc,
+ listenSocket,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_UNJOINABLE_THREAD,
+ STACKSIZE);
+
+ if (!WorkerThread) {
+ if (debug_mode) printf("error creating working thread\n");
+ PR_Close(listenSocket);
+ return NULL;
+ }
+ PR_AtomicIncrement(&workerThreads);
+ if (debug_mode) DPRINTF("\tServer created primordial worker thread\n");
+
+ return listenSocket;
+}
+
+/* The main server loop */
+void
+ServerThreadFunc(void *unused)
+{
+ PRFileDesc *listenSocket;
+
+ /* Do setup */
+ listenSocket = ServerSetup();
+
+ if (!listenSocket) {
+ SetServerState(SERVER, SERVER_STATE_DEAD);
+ } else {
+
+ if (debug_mode) DPRINTF("\tServer up\n");
+
+ /* Tell clients they can start now. */
+ SetServerState(SERVER, SERVER_STATE_READY);
+
+ /* Now wait for server death signal */
+ WaitServerState(SERVER, SERVER_STATE_DYING);
+
+ /* Cleanup */
+ SetServerState(SERVER, SERVER_STATE_DEAD);
+ }
+}
+
+/* --- Client Functions ------------------------------------------- */
+
+PRInt32 numRequests;
+PRInt32 numClients;
+PRMonitor *clientMonitor;
+
+void
+ClientThreadFunc(void *unused)
+{
+ PRNetAddr serverAddr;
+ PRFileDesc *clientSocket;
+ char *sendBuf;
+ char *recvBuf;
+ PRInt32 rv;
+ PRInt32 bytesNeeded;
+
+ sendBuf = (char *)PR_MALLOC(_client_data * sizeof(char));
+ if (!sendBuf)
+ if (debug_mode) printf("\tClient could not malloc space!?\n");
+ recvBuf = (char *)PR_MALLOC(_server_data * sizeof(char));
+ if (!recvBuf)
+ if (debug_mode) printf("\tClient could not malloc space!?\n");
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = PR_AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+
+ while(numRequests > 0) {
+
+ if ( (numRequests % 10) == 0 )
+ if (debug_mode) printf(".");
+ if (debug_mode) DPRINTF("\tClient starting request %d\n", numRequests);
+
+ clientSocket = PR_NewTCPSocket();
+ if (!clientSocket) {
+ if (debug_mode) printf("Client error creating socket: OS error %d\n",
+ PR_GetOSError());
+ continue;
+ }
+
+ if (debug_mode) DPRINTF("\tClient connecting\n");
+
+ rv = PR_Connect(clientSocket,
+ &serverAddr,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (!clientSocket) {
+ if (debug_mode) printf("\tClient error connecting\n");
+ continue;
+ }
+
+ if (debug_mode) DPRINTF("\tClient connected\n");
+
+ rv = PR_Send(clientSocket,
+ sendBuf,
+ _client_data,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv != _client_data) {
+ if (debug_mode) printf("Client error sending data (%d)\n", rv);
+ PR_Close(clientSocket);
+ continue;
+ }
+
+ if (debug_mode) DPRINTF("\tClient sent %d bytes\n", rv);
+
+ bytesNeeded = _server_data;
+ while(bytesNeeded) {
+ rv = PR_Recv(clientSocket,
+ recvBuf,
+ bytesNeeded,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv <= 0) {
+ if (debug_mode) printf("Client error receiving data (%d) (%d/%d)\n",
+ rv, (_server_data - bytesNeeded), _server_data);
+ break;
+ }
+ if (debug_mode) DPRINTF("\tClient received %d bytes; need %d more\n", rv, bytesNeeded - rv);
+ bytesNeeded -= rv;
+ }
+
+ PR_Close(clientSocket);
+
+ PR_AtomicDecrement(&numRequests);
+ }
+
+ PR_EnterMonitor(clientMonitor);
+ --numClients;
+ PR_Notify(clientMonitor);
+ PR_ExitMonitor(clientMonitor);
+
+ PR_DELETE(sendBuf);
+ PR_DELETE(recvBuf);
+}
+
+void
+RunClients(void)
+{
+ PRInt32 index;
+
+ numRequests = _iterations;
+ numClients = _clients;
+ clientMonitor = PR_NewMonitor();
+
+ for (index=0; index<_clients; index++) {
+ PRThread *clientThread;
+
+
+ clientThread = PR_CreateThread(
+ PR_USER_THREAD,
+ ClientThreadFunc,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ ClientScope,
+ PR_UNJOINABLE_THREAD,
+ STACKSIZE);
+
+ if (!clientThread) {
+ if (debug_mode) printf("\terror creating client thread %d\n", index);
+ } else
+ if (debug_mode) DPRINTF("\tMain created client %d/%d\n", index+1, _clients);
+
+ }
+
+ PR_EnterMonitor(clientMonitor);
+ while(numClients)
+ PR_Wait(clientMonitor, PR_INTERVAL_NO_TIMEOUT);
+ PR_ExitMonitor(clientMonitor);
+}
+
+/* --- Main Function ---------------------------------------------- */
+
+static
+void do_work()
+{
+ PRThread *ServerThread;
+ PRInt32 state;
+
+ SetServerState(MAIN, SERVER_STATE_STARTUP);
+ ServerThread = PR_CreateThread(
+ PR_USER_THREAD,
+ ServerThreadFunc,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_JOINABLE_THREAD,
+ STACKSIZE);
+ if (!ServerThread) {
+ if (debug_mode) printf("error creating main server thread\n");
+ return;
+ }
+
+ /* Wait for server to be ready */
+ state = WaitServerState(MAIN, SERVER_STATE_READY|SERVER_STATE_DEAD);
+
+ if (!(state & SERVER_STATE_DEAD)) {
+ /* Run Test Clients */
+ RunClients();
+
+ /* Send death signal to server */
+ SetServerState(MAIN, SERVER_STATE_DYING);
+ }
+
+ PR_JoinThread(ServerThread);
+}
+
+
+static void do_workKU(void)
+{
+ ServerScope = PR_GLOBAL_THREAD;
+ ClientScope = PR_LOCAL_THREAD;
+ do_work();
+}
+
+
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+
+ if (debug_mode) printf("\n%40s: %6.2f usec\n", msg, d / _iterations);
+}
+
+
+main(int argc, char **argv)
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+ if (debug_mode) {
+ printf("Enter number of iterations: \n");
+ scanf("%d", &_iterations);
+ printf("Enter number of clients : \n");
+ scanf("%d", &_clients);
+ printf("Enter size of client data : \n");
+ scanf("%d", &_client_data);
+ printf("Enter size of server data : \n");
+ scanf("%d", &_server_data);
+ }
+ else {
+ _iterations = 7;
+ _clients = 7;
+ _client_data = 100;
+ _server_data = 100;
+ }
+
+ if (debug_mode) {
+ printf("\n\n%d iterations with %d client threads.\n",
+ _iterations, _clients);
+ printf("Sending %d bytes of client data and %d bytes of server data\n",
+ _client_data, _server_data);
+ }
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ PR_SetThreadRecycleMode(64);
+
+ ServerStateCVLock = PR_NewLock();
+ ServerStateCV = PR_NewCondVar(ServerStateCVLock);
+
+ Measure(do_workKU, "server loop kernel/user");
+
+ PR_Cleanup();
+ if(failed_already)
+ return 1;
+ else
+ return 0;
+
+}
diff --git a/pr/tests/servr_uk.c b/pr/tests/servr_uk.c
new file mode 100644
index 00000000..c462a56f
--- /dev/null
+++ b/pr/tests/servr_uk.c
@@ -0,0 +1,566 @@
+/* -*- 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 server simulates a server running in loopback mode.
+**
+** The idea is that a single server is created. The server initially creates
+** a number of worker threads. Then, with the server running, a number of
+** clients are created which start requesting service from the server.
+**
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+#include "pprthred.h"
+
+#include <string.h>
+
+#define PORT 15004
+#define STACKSIZE 0
+
+static int _iterations = 1000;
+static int _clients = 1;
+static int _client_data = 250;
+static int _server_data = (8*1024);
+
+static PRThreadScope ServerScope, ClientScope;
+
+#define SERVER "Server"
+#define MAIN "Main"
+
+#define SERVER_STATE_STARTUP 0
+#define SERVER_STATE_READY 1
+#define SERVER_STATE_DYING 2
+#define SERVER_STATE_DEAD 4
+int ServerState;
+PRLock *ServerStateCVLock;
+PRCondVar *ServerStateCV;
+
+#undef DEBUGPRINTS
+#ifdef DEBUGPRINTS
+#define DPRINTF printf
+#else
+#define DPRINTF
+#endif
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+
+
+static void do_work(void);
+
+/* --- Server state functions --------------------------------------------- */
+void
+SetServerState(char *waiter, PRInt32 state)
+{
+ PR_Lock(ServerStateCVLock);
+ ServerState = state;
+ PR_NotifyCondVar(ServerStateCV);
+
+ if (debug_mode) DPRINTF("\t%s changed state to %d\n", waiter, state);
+
+ PR_Unlock(ServerStateCVLock);
+}
+
+int
+WaitServerState(char *waiter, PRInt32 state)
+{
+ PRInt32 rv;
+
+ PR_Lock(ServerStateCVLock);
+
+ if (debug_mode) DPRINTF("\t%s waiting for state %d\n", waiter, state);
+
+ while(!(ServerState & state))
+ PR_WaitCondVar(ServerStateCV, PR_INTERVAL_NO_TIMEOUT);
+ rv = ServerState;
+
+ if (debug_mode) DPRINTF("\t%s resuming from wait for state %d; state now %d\n",
+ waiter, state, ServerState);
+ PR_Unlock(ServerStateCVLock);
+
+ return rv;
+}
+
+/* --- Server Functions ------------------------------------------- */
+
+PRLock *workerThreadsLock;
+PRInt32 workerThreads;
+PRInt32 workerThreadsBusy;
+
+void
+WorkerThreadFunc(void *_listenSock)
+{
+ PRFileDesc *listenSock = (PRFileDesc *)_listenSock;
+ PRInt32 bytesRead;
+ PRInt32 bytesWritten;
+ char *dataBuf;
+ char *sendBuf;
+
+ if (debug_mode) DPRINTF("\tServer buffer is %d bytes; %d data, %d netaddrs\n",
+ _client_data+(4*sizeof(PRNetAddr)), _client_data, (4*sizeof(PRNetAddr)));
+ dataBuf = (char *)PR_MALLOC(_client_data *sizeof(char) + 4*sizeof(PRNetAddr));
+ if (!dataBuf)
+ if (debug_mode) printf("\tServer could not malloc space!?\n");
+ sendBuf = (char *)PR_MALLOC(_server_data *sizeof(char));
+ if (!sendBuf)
+ if (debug_mode) printf("\tServer could not malloc space!?\n");
+
+ if (debug_mode) DPRINTF("\tServer worker thread running\n");
+
+ while(1) {
+ PRInt32 bytesToRead = _client_data;
+ PRInt32 bytesToWrite = _server_data;
+ PRFileDesc *newSock;
+ PRNetAddr *rAddr;
+ PRInt32 loops = 0;
+
+ loops++;
+
+ if (debug_mode) DPRINTF("\tServer thread going into accept\n");
+
+ bytesRead = PR_AcceptRead(listenSock,
+ &newSock,
+ &rAddr,
+ dataBuf,
+ bytesToRead,
+ PR_INTERVAL_NO_TIMEOUT);
+
+ if (bytesRead < 0) {
+ if (debug_mode) printf("\tServer error in accept (%d)\n", bytesRead);
+ continue;
+ }
+
+ if (debug_mode) DPRINTF("\tServer accepted connection (%d bytes)\n", bytesRead);
+
+ PR_AtomicIncrement(&workerThreadsBusy);
+ if (workerThreadsBusy == workerThreads) {
+
+ PR_Lock(workerThreadsLock);
+ if (workerThreadsBusy == workerThreads) {
+ PRThread *WorkerThread;
+
+ WorkerThread = PR_CreateThread(
+ PR_SYSTEM_THREAD,
+ WorkerThreadFunc,
+ listenSock,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_UNJOINABLE_THREAD,
+ STACKSIZE);
+
+ if (!WorkerThread)
+ if (debug_mode) printf("Error creating client thread %d\n", workerThreads);
+ else {
+ PR_AtomicIncrement(&workerThreads);
+ if (debug_mode) DPRINTF("\tServer creates worker (%d)\n", workerThreads);
+ }
+ }
+ PR_Unlock(workerThreadsLock);
+ }
+
+ bytesToRead -= bytesRead;
+ while (bytesToRead) {
+ bytesRead = PR_Recv(newSock,
+ dataBuf,
+ bytesToRead,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (bytesRead < 0) {
+ if (debug_mode) printf("\tServer error receiving data (%d)\n", bytesRead);
+ continue;
+ }
+ if (debug_mode) DPRINTF("\tServer received %d bytes\n", bytesRead);
+ }
+
+ bytesWritten = PR_Send(newSock,
+ sendBuf,
+ bytesToWrite,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (bytesWritten != _server_data)
+ if (debug_mode) printf("\tError sending data to client (%d, %d)\n",
+ bytesWritten, PR_GetOSError());
+ else
+ if (debug_mode) DPRINTF("\tServer sent %d bytes\n", bytesWritten);
+
+ PR_Close(newSock);
+ PR_AtomicDecrement(&workerThreadsBusy);
+ }
+}
+
+PRFileDesc *
+ServerSetup(void)
+{
+ PRFileDesc *listenSocket;
+ PRNetAddr serverAddr;
+ PRThread *WorkerThread;
+
+ if ( (listenSocket = PR_NewTCPSocket()) == NULL) {
+ if (debug_mode) printf("\tServer error creating listen socket\n");
+ else
+ return NULL;
+ }
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = PR_AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+
+ if ( PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) {
+ if (debug_mode) printf("\tServer error binding to server address: OS error %d\n",
+ PR_GetOSError());
+ else failed_already=1;
+ PR_Close(listenSocket);
+ return NULL;
+ }
+
+ if ( PR_Listen(listenSocket, 128) == PR_FAILURE) {
+ if (debug_mode) printf("\tServer error listening to server socket\n");
+ else failed_already=1;
+ PR_Close(listenSocket);
+
+ return NULL;
+ }
+
+ /* Create Clients */
+ workerThreads = 0;
+ workerThreadsBusy = 0;
+
+ workerThreadsLock = PR_NewLock();
+
+ WorkerThread = PR_CreateThread(
+ PR_SYSTEM_THREAD,
+ WorkerThreadFunc,
+ listenSocket,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_UNJOINABLE_THREAD,
+ STACKSIZE);
+
+ if (!WorkerThread) {
+ if (debug_mode) printf("error creating working thread\n");
+ PR_Close(listenSocket);
+ return NULL;
+ }
+ PR_AtomicIncrement(&workerThreads);
+ if (debug_mode) DPRINTF("\tServer created primordial worker thread\n");
+
+ return listenSocket;
+}
+
+/* The main server loop */
+void
+ServerThreadFunc(void *unused)
+{
+ PRFileDesc *listenSocket;
+
+ /* Do setup */
+ listenSocket = ServerSetup();
+
+ if (!listenSocket) {
+ SetServerState(SERVER, SERVER_STATE_DEAD);
+ } else {
+
+ if (debug_mode) DPRINTF("\tServer up\n");
+
+ /* Tell clients they can start now. */
+ SetServerState(SERVER, SERVER_STATE_READY);
+
+ /* Now wait for server death signal */
+ WaitServerState(SERVER, SERVER_STATE_DYING);
+
+ /* Cleanup */
+ SetServerState(SERVER, SERVER_STATE_DEAD);
+ }
+}
+
+/* --- Client Functions ------------------------------------------- */
+
+PRInt32 numRequests;
+PRInt32 numClients;
+PRMonitor *clientMonitor;
+
+void
+ClientThreadFunc(void *unused)
+{
+ PRNetAddr serverAddr;
+ PRFileDesc *clientSocket;
+ char *sendBuf;
+ char *recvBuf;
+ PRInt32 rv;
+ PRInt32 bytesNeeded;
+
+ sendBuf = (char *)PR_MALLOC(_client_data * sizeof(char));
+ if (!sendBuf)
+ if (debug_mode) printf("\tClient could not malloc space!?\n");
+ recvBuf = (char *)PR_MALLOC(_server_data * sizeof(char));
+ if (!recvBuf)
+ if (debug_mode) printf("\tClient could not malloc space!?\n");
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = PR_AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+
+ while(numRequests > 0) {
+
+ if ( (numRequests % 10) == 0 )
+ if (debug_mode) printf(".");
+ if (debug_mode) DPRINTF("\tClient starting request %d\n", numRequests);
+
+ clientSocket = PR_NewTCPSocket();
+ if (!clientSocket) {
+ if (debug_mode) printf("Client error creating socket: OS error %d\n",
+ PR_GetOSError());
+ continue;
+ }
+
+ if (debug_mode) DPRINTF("\tClient connecting\n");
+
+ rv = PR_Connect(clientSocket,
+ &serverAddr,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (!clientSocket) {
+ if (debug_mode) printf("\tClient error connecting\n");
+ continue;
+ }
+
+ if (debug_mode) DPRINTF("\tClient connected\n");
+
+ rv = PR_Send(clientSocket,
+ sendBuf,
+ _client_data,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv != _client_data) {
+ if (debug_mode) printf("Client error sending data (%d)\n", rv);
+ PR_Close(clientSocket);
+ continue;
+ }
+
+ if (debug_mode) DPRINTF("\tClient sent %d bytes\n", rv);
+
+ bytesNeeded = _server_data;
+ while(bytesNeeded) {
+ rv = PR_Recv(clientSocket,
+ recvBuf,
+ bytesNeeded,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv <= 0) {
+ if (debug_mode) printf("Client error receiving data (%d) (%d/%d)\n",
+ rv, (_server_data - bytesNeeded), _server_data);
+ break;
+ }
+ if (debug_mode) DPRINTF("\tClient received %d bytes; need %d more\n", rv, bytesNeeded - rv);
+ bytesNeeded -= rv;
+ }
+
+ PR_Close(clientSocket);
+
+ PR_AtomicDecrement(&numRequests);
+ }
+
+ PR_EnterMonitor(clientMonitor);
+ --numClients;
+ PR_Notify(clientMonitor);
+ PR_ExitMonitor(clientMonitor);
+
+ PR_DELETE(sendBuf);
+ PR_DELETE(recvBuf);
+}
+
+void
+RunClients(void)
+{
+ PRInt32 index;
+
+ numRequests = _iterations;
+ numClients = _clients;
+ clientMonitor = PR_NewMonitor();
+
+ for (index=0; index<_clients; index++) {
+ PRThread *clientThread;
+
+
+ clientThread = PR_CreateThread(
+ PR_USER_THREAD,
+ ClientThreadFunc,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ ClientScope,
+ PR_UNJOINABLE_THREAD,
+ STACKSIZE);
+
+ if (!clientThread) {
+ if (debug_mode) printf("\terror creating client thread %d\n", index);
+ } else
+ if (debug_mode) DPRINTF("\tMain created client %d/%d\n", index+1, _clients);
+
+ }
+
+ PR_EnterMonitor(clientMonitor);
+ while(numClients)
+ PR_Wait(clientMonitor, PR_INTERVAL_NO_TIMEOUT);
+ PR_ExitMonitor(clientMonitor);
+}
+
+/* --- Main Function ---------------------------------------------- */
+
+static
+void do_work()
+{
+ PRThread *ServerThread;
+ PRInt32 state;
+
+ SetServerState(MAIN, SERVER_STATE_STARTUP);
+ ServerThread = PR_CreateThread(
+ PR_USER_THREAD,
+ ServerThreadFunc,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_JOINABLE_THREAD,
+ STACKSIZE);
+ if (!ServerThread) {
+ if (debug_mode) printf("error creating main server thread\n");
+ return;
+ }
+
+ /* Wait for server to be ready */
+ state = WaitServerState(MAIN, SERVER_STATE_READY|SERVER_STATE_DEAD);
+
+ if (!(state & SERVER_STATE_DEAD)) {
+ /* Run Test Clients */
+ RunClients();
+
+ /* Send death signal to server */
+ SetServerState(MAIN, SERVER_STATE_DYING);
+ }
+
+ PR_JoinThread(ServerThread);
+}
+
+
+static void do_workUK(void)
+{
+ ServerScope = PR_LOCAL_THREAD;
+ ClientScope = PR_GLOBAL_THREAD;
+ do_work();
+}
+
+
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+
+ if (debug_mode) printf("\n%40s: %6.2f usec\n", msg, d / _iterations);
+}
+
+
+main(int argc, char **argv)
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+ if (debug_mode) {
+ printf("Enter number of iterations: \n");
+ scanf("%d", &_iterations);
+ printf("Enter number of clients : \n");
+ scanf("%d", &_clients);
+ printf("Enter size of client data : \n");
+ scanf("%d", &_client_data);
+ printf("Enter size of server data : \n");
+ scanf("%d", &_server_data);
+ }
+ else {
+ _iterations = 7;
+ _clients = 7;
+ _client_data = 100;
+ _server_data = 100;
+ }
+
+ if (debug_mode) {
+ printf("\n\n%d iterations with %d client threads.\n",
+ _iterations, _clients);
+ printf("Sending %d bytes of client data and %d bytes of server data\n",
+ _client_data, _server_data);
+ }
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ PR_SetThreadRecycleMode(64);
+
+ ServerStateCVLock = PR_NewLock();
+ ServerStateCV = PR_NewCondVar(ServerStateCVLock);
+
+ Measure(do_workUK, "server loop user/kernel");
+
+ PR_Cleanup();
+
+ if(failed_already)
+ return 1;
+ else
+ return 0;
+}
diff --git a/pr/tests/servr_uu.c b/pr/tests/servr_uu.c
new file mode 100644
index 00000000..ffa36a23
--- /dev/null
+++ b/pr/tests/servr_uu.c
@@ -0,0 +1,564 @@
+/* -*- 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 server simulates a server running in loopback mode.
+**
+** The idea is that a single server is created. The server initially creates
+** a number of worker threads. Then, with the server running, a number of
+** clients are created which start requesting service from the server.
+**
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+#include "pprthred.h"
+
+#include <string.h>
+
+#define PORT 15004
+#define STACKSIZE 0
+
+static int _iterations = 1000;
+static int _clients = 1;
+static int _client_data = 250;
+static int _server_data = (8*1024);
+
+static PRThreadScope ServerScope, ClientScope;
+
+#define SERVER "Server"
+#define MAIN "Main"
+
+#define SERVER_STATE_STARTUP 0
+#define SERVER_STATE_READY 1
+#define SERVER_STATE_DYING 2
+#define SERVER_STATE_DEAD 4
+int ServerState;
+PRLock *ServerStateCVLock;
+PRCondVar *ServerStateCV;
+
+#undef DEBUGPRINTS
+#ifdef DEBUGPRINTS
+#define DPRINTF printf
+#else
+#define DPRINTF
+#endif
+
+PRIntn failed_already=0;
+PRIntn debug_mode;
+
+static void do_work(void);
+
+/* --- Server state functions --------------------------------------------- */
+void
+SetServerState(char *waiter, PRInt32 state)
+{
+ PR_Lock(ServerStateCVLock);
+ ServerState = state;
+ PR_NotifyCondVar(ServerStateCV);
+
+ if (debug_mode) DPRINTF("\t%s changed state to %d\n", waiter, state);
+
+ PR_Unlock(ServerStateCVLock);
+}
+
+int
+WaitServerState(char *waiter, PRInt32 state)
+{
+ PRInt32 rv;
+
+ PR_Lock(ServerStateCVLock);
+
+ if (debug_mode) DPRINTF("\t%s waiting for state %d\n", waiter, state);
+
+ while(!(ServerState & state))
+ PR_WaitCondVar(ServerStateCV, PR_INTERVAL_NO_TIMEOUT);
+ rv = ServerState;
+
+ if (debug_mode) DPRINTF("\t%s resuming from wait for state %d; state now %d\n",
+ waiter, state, ServerState);
+ PR_Unlock(ServerStateCVLock);
+
+ return rv;
+}
+
+/* --- Server Functions ------------------------------------------- */
+
+PRLock *workerThreadsLock;
+PRInt32 workerThreads;
+PRInt32 workerThreadsBusy;
+
+void
+WorkerThreadFunc(void *_listenSock)
+{
+ PRFileDesc *listenSock = (PRFileDesc *)_listenSock;
+ PRInt32 bytesRead;
+ PRInt32 bytesWritten;
+ char *dataBuf;
+ char *sendBuf;
+
+ if (debug_mode) DPRINTF("\tServer buffer is %d bytes; %d data, %d netaddrs\n",
+ _client_data+(4*sizeof(PRNetAddr)), _client_data, (4*sizeof(PRNetAddr)));
+ dataBuf = (char *)PR_MALLOC(_client_data *sizeof(char) + 4*sizeof(PRNetAddr));
+ if (!dataBuf)
+ if (debug_mode) printf("\tServer could not malloc space!?\n");
+ sendBuf = (char *)PR_MALLOC(_server_data *sizeof(char));
+ if (!sendBuf)
+ if (debug_mode) printf("\tServer could not malloc space!?\n");
+
+ if (debug_mode) DPRINTF("\tServer worker thread running\n");
+
+ while(1) {
+ PRInt32 bytesToRead = _client_data;
+ PRInt32 bytesToWrite = _server_data;
+ PRFileDesc *newSock;
+ PRNetAddr *rAddr;
+ PRInt32 loops = 0;
+
+ loops++;
+
+ if (debug_mode) DPRINTF("\tServer thread going into accept\n");
+
+ bytesRead = PR_AcceptRead(listenSock,
+ &newSock,
+ &rAddr,
+ dataBuf,
+ bytesToRead,
+ PR_INTERVAL_NO_TIMEOUT);
+
+ if (bytesRead < 0) {
+ if (debug_mode) printf("\tServer error in accept (%d)\n", bytesRead);
+ continue;
+ }
+
+ if (debug_mode) DPRINTF("\tServer accepted connection (%d bytes)\n", bytesRead);
+
+ PR_AtomicIncrement(&workerThreadsBusy);
+ if (workerThreadsBusy == workerThreads) {
+
+ PR_Lock(workerThreadsLock);
+ if (workerThreadsBusy == workerThreads) {
+ PRThread *WorkerThread;
+
+ WorkerThread = PR_CreateThread(
+ PR_SYSTEM_THREAD,
+ WorkerThreadFunc,
+ listenSock,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_UNJOINABLE_THREAD,
+ STACKSIZE);
+
+ if (!WorkerThread)
+ if (debug_mode) printf("Error creating client thread %d\n", workerThreads);
+ else {
+ PR_AtomicIncrement(&workerThreads);
+ if (debug_mode) DPRINTF("\tServer creates worker (%d)\n", workerThreads);
+ }
+ }
+ PR_Unlock(workerThreadsLock);
+ }
+
+ bytesToRead -= bytesRead;
+ while (bytesToRead) {
+ bytesRead = PR_Recv(newSock,
+ dataBuf,
+ bytesToRead,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (bytesRead < 0) {
+ if (debug_mode) printf("\tServer error receiving data (%d)\n", bytesRead);
+ continue;
+ }
+ if (debug_mode) DPRINTF("\tServer received %d bytes\n", bytesRead);
+ }
+
+ bytesWritten = PR_Send(newSock,
+ sendBuf,
+ bytesToWrite,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (bytesWritten != _server_data)
+ if (debug_mode) printf("\tError sending data to client (%d, %d)\n",
+ bytesWritten, PR_GetOSError());
+ else
+ if (debug_mode) DPRINTF("\tServer sent %d bytes\n", bytesWritten);
+
+ PR_Close(newSock);
+ PR_AtomicDecrement(&workerThreadsBusy);
+ }
+}
+
+PRFileDesc *
+ServerSetup(void)
+{
+ PRFileDesc *listenSocket;
+ PRNetAddr serverAddr;
+ PRThread *WorkerThread;
+
+ if ( (listenSocket = PR_NewTCPSocket()) == NULL) {
+ if (debug_mode) printf("\tServer error creating listen socket\n");
+ else failed_already=1;
+ return NULL;
+ }
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = PR_AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+
+ if ( PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) {
+ if (debug_mode) printf("\tServer error binding to server address: OS error %d\n",
+ PR_GetOSError());
+ else failed_already=1;
+ PR_Close(listenSocket);
+ return NULL;
+ }
+
+ if ( PR_Listen(listenSocket, 128) == PR_FAILURE) {
+ if (debug_mode) printf("\tServer error listening to server socket\n");
+ else failed_already=1;
+ PR_Close(listenSocket);
+
+ return NULL;
+ }
+
+ /* Create Clients */
+ workerThreads = 0;
+ workerThreadsBusy = 0;
+
+ workerThreadsLock = PR_NewLock();
+
+ WorkerThread = PR_CreateThread(
+ PR_SYSTEM_THREAD,
+ WorkerThreadFunc,
+ listenSocket,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_UNJOINABLE_THREAD,
+ STACKSIZE);
+
+ if (!WorkerThread) {
+ if (debug_mode) printf("error creating working thread\n");
+ PR_Close(listenSocket);
+ return NULL;
+ }
+ PR_AtomicIncrement(&workerThreads);
+ if (debug_mode) DPRINTF("\tServer created primordial worker thread\n");
+
+ return listenSocket;
+}
+
+/* The main server loop */
+void
+ServerThreadFunc(void *unused)
+{
+ PRFileDesc *listenSocket;
+
+ /* Do setup */
+ listenSocket = ServerSetup();
+
+ if (!listenSocket) {
+ SetServerState(SERVER, SERVER_STATE_DEAD);
+ } else {
+
+ if (debug_mode) DPRINTF("\tServer up\n");
+
+ /* Tell clients they can start now. */
+ SetServerState(SERVER, SERVER_STATE_READY);
+
+ /* Now wait for server death signal */
+ WaitServerState(SERVER, SERVER_STATE_DYING);
+
+ /* Cleanup */
+ SetServerState(SERVER, SERVER_STATE_DEAD);
+ }
+}
+
+/* --- Client Functions ------------------------------------------- */
+
+PRInt32 numRequests;
+PRInt32 numClients;
+PRMonitor *clientMonitor;
+
+void
+ClientThreadFunc(void *unused)
+{
+ PRNetAddr serverAddr;
+ PRFileDesc *clientSocket;
+ char *sendBuf;
+ char *recvBuf;
+ PRInt32 rv;
+ PRInt32 bytesNeeded;
+
+ sendBuf = (char *)PR_MALLOC(_client_data * sizeof(char));
+ if (!sendBuf)
+ if (debug_mode) printf("\tClient could not malloc space!?\n");
+ recvBuf = (char *)PR_MALLOC(_server_data * sizeof(char));
+ if (!recvBuf)
+ if (debug_mode) printf("\tClient could not malloc space!?\n");
+
+ memset(&serverAddr, 0, sizeof(PRNetAddr));
+ serverAddr.inet.family = PR_AF_INET;
+ serverAddr.inet.port = PR_htons(PORT);
+ serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+
+ while(numRequests > 0) {
+
+ if ( (numRequests % 10) == 0 )
+ if (debug_mode) printf(".");
+ if (debug_mode) DPRINTF("\tClient starting request %d\n", numRequests);
+
+ clientSocket = PR_NewTCPSocket();
+ if (!clientSocket) {
+ if (debug_mode) printf("Client error creating socket: OS error %d\n",
+ PR_GetOSError());
+ continue;
+ }
+
+ if (debug_mode) DPRINTF("\tClient connecting\n");
+
+ rv = PR_Connect(clientSocket,
+ &serverAddr,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (!clientSocket) {
+ if (debug_mode) printf("\tClient error connecting\n");
+ continue;
+ }
+
+ if (debug_mode) DPRINTF("\tClient connected\n");
+
+ rv = PR_Send(clientSocket,
+ sendBuf,
+ _client_data,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv != _client_data) {
+ if (debug_mode) printf("Client error sending data (%d)\n", rv);
+ PR_Close(clientSocket);
+ continue;
+ }
+
+ if (debug_mode) DPRINTF("\tClient sent %d bytes\n", rv);
+
+ bytesNeeded = _server_data;
+ while(bytesNeeded) {
+ rv = PR_Recv(clientSocket,
+ recvBuf,
+ bytesNeeded,
+ 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv <= 0) {
+ if (debug_mode) printf("Client error receiving data (%d) (%d/%d)\n",
+ rv, (_server_data - bytesNeeded), _server_data);
+ break;
+ }
+ if (debug_mode) DPRINTF("\tClient received %d bytes; need %d more\n", rv, bytesNeeded - rv);
+ bytesNeeded -= rv;
+ }
+
+ PR_Close(clientSocket);
+
+ PR_AtomicDecrement(&numRequests);
+ }
+
+ PR_EnterMonitor(clientMonitor);
+ --numClients;
+ PR_Notify(clientMonitor);
+ PR_ExitMonitor(clientMonitor);
+
+ PR_DELETE(sendBuf);
+ PR_DELETE(recvBuf);
+}
+
+void
+RunClients(void)
+{
+ PRInt32 index;
+
+ numRequests = _iterations;
+ numClients = _clients;
+ clientMonitor = PR_NewMonitor();
+
+ for (index=0; index<_clients; index++) {
+ PRThread *clientThread;
+
+
+ clientThread = PR_CreateThread(
+ PR_USER_THREAD,
+ ClientThreadFunc,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ ClientScope,
+ PR_UNJOINABLE_THREAD,
+ STACKSIZE);
+
+ if (!clientThread) {
+ if (debug_mode) printf("\terror creating client thread %d\n", index);
+ } else
+ if (debug_mode) DPRINTF("\tMain created client %d/%d\n", index+1, _clients);
+
+ }
+
+ PR_EnterMonitor(clientMonitor);
+ while(numClients)
+ PR_Wait(clientMonitor, PR_INTERVAL_NO_TIMEOUT);
+ PR_ExitMonitor(clientMonitor);
+}
+
+/* --- Main Function ---------------------------------------------- */
+
+static
+void do_work()
+{
+ PRThread *ServerThread;
+ PRInt32 state;
+
+ SetServerState(MAIN, SERVER_STATE_STARTUP);
+ ServerThread = PR_CreateThread(
+ PR_USER_THREAD,
+ ServerThreadFunc,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ ServerScope,
+ PR_JOINABLE_THREAD,
+ STACKSIZE);
+ if (!ServerThread) {
+ if (debug_mode) printf("error creating main server thread\n");
+ return;
+ }
+
+ /* Wait for server to be ready */
+ state = WaitServerState(MAIN, SERVER_STATE_READY|SERVER_STATE_DEAD);
+
+ if (!(state & SERVER_STATE_DEAD)) {
+ /* Run Test Clients */
+ RunClients();
+
+ /* Send death signal to server */
+ SetServerState(MAIN, SERVER_STATE_DYING);
+ }
+
+ PR_JoinThread(ServerThread);
+}
+
+static void do_workUU(void)
+{
+ ServerScope = PR_LOCAL_THREAD;
+ ClientScope = PR_LOCAL_THREAD;
+ do_work();
+}
+
+
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+
+ if (debug_mode) printf("\n%40s: %6.2f usec\n", msg, d / _iterations);
+}
+
+
+main(int argc, char **argv)
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+ if (debug_mode) {
+ printf("Enter number of iterations: \n");
+ scanf("%d", &_iterations);
+ printf("Enter number of clients : \n");
+ scanf("%d", &_clients);
+ printf("Enter size of client data : \n");
+ scanf("%d", &_client_data);
+ printf("Enter size of server data : \n");
+ scanf("%d", &_server_data);
+ }
+ else {
+ _iterations = 7;
+ _clients = 7;
+ _client_data = 100;
+ _server_data = 100;
+ }
+
+ if (debug_mode) {
+ printf("\n\n%d iterations with %d client threads.\n",
+ _iterations, _clients);
+ printf("Sending %d bytes of client data and %d bytes of server data\n",
+ _client_data, _server_data);
+ }
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ PR_SetThreadRecycleMode(64);
+
+ ServerStateCVLock = PR_NewLock();
+ ServerStateCV = PR_NewCondVar(ServerStateCVLock);
+
+ Measure(do_workUU, "server loop user/user");
+
+ PR_Cleanup();
+
+ if(failed_already)
+ return 1;
+ else
+ return 0;
+
+}
diff --git a/pr/tests/short_thread.c b/pr/tests/short_thread.c
new file mode 100644
index 00000000..9f765b6b
--- /dev/null
+++ b/pr/tests/short_thread.c
@@ -0,0 +1,71 @@
+/* -*- 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 <stdio.h>
+#include "nspr.h"
+#include "plgetopt.h"
+
+/*
+ * Create a thread that exits right away; useful for testing race conditions in thread
+ * creation
+ */
+
+int _debug_on = 0;
+#define DPRINTF(arg) if (_debug_on) printf arg
+
+static void housecleaning(void *cur_time);
+
+int main (int argc, char **argv)
+{
+ static PRIntervalTime thread_start_time;
+ static PRThread *housekeeping_tid = NULL;
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ _debug_on = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (( housekeeping_tid =
+ PR_CreateThread (PR_USER_THREAD, housecleaning, (void*)&thread_start_time,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0))
+ == NULL ) {
+ fprintf(stderr,
+ "simple_test: Error - PR_CreateThread failed: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ exit( 1 );
+ }
+ PR_Cleanup();
+ return(0);
+}
+
+static void
+housecleaning (void *cur_time)
+{
+ DPRINTF(("Child Thread exiting\n"));
+}
diff --git a/pr/tests/sigpipe.c b/pr/tests/sigpipe.c
new file mode 100644
index 00000000..ffc0d40a
--- /dev/null
+++ b/pr/tests/sigpipe.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.
+ */
+
+/*
+ *************************************************************************
+ *
+ * Test: sigpipe.c
+ *
+ * Test the SIGPIPE handler in NSPR. This test applies to Unix only.
+ *
+ *************************************************************************
+ */
+
+#if !defined(XP_UNIX)
+
+int main()
+{
+ /* This test applies to Unix only. */
+ return 0;
+}
+
+#else /* XP_UNIX */
+
+#include "nspr.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+
+int main()
+{
+ int pipefd[2];
+ int rv;
+ char c = '\0';
+
+ /* This initializes NSPR. */
+ PR_SetError(0, 0);
+
+ if (pipe(pipefd) == -1) {
+ fprintf(stderr, "cannot create pipe: %d\n", errno);
+ exit(1);
+ }
+ close(pipefd[0]);
+
+ rv = write(pipefd[1], &c, 1);
+ if (rv != -1) {
+ fprintf(stderr, "write to broken pipe should have failed with EPIPE but returned %d\n", rv);
+ exit(1);
+ }
+ if (errno != EPIPE) {
+ fprintf(stderr, "write to broken pipe failed but with wrong errno: %d\n", errno);
+ exit(1);
+ }
+ printf("write to broken pipe failed with EPIPE, as expected\n");
+ printf("PASSED\n");
+ return 0;
+}
+
+#endif /* XP_UNIX */
diff --git a/pr/tests/sleep.c b/pr/tests/sleep.c
new file mode 100644
index 00000000..97af7074
--- /dev/null
+++ b/pr/tests/sleep.c
@@ -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.
+ */
+
+#include "nspr.h"
+
+#if defined(XP_UNIX)
+
+#include <stdio.h>
+
+#include <unistd.h>
+#include <sys/time.h>
+
+#if defined(HAVE_SVID_GETTOD)
+#define GTOD(_a) gettimeofday(_a)
+#else
+#define GTOD(_a) gettimeofday((_a), NULL)
+#endif
+
+static PRIntn rv = 0;
+
+static void Other(void *unused)
+{
+ PRIntn didit = 0;
+ while (PR_SUCCESS == PR_Sleep(PR_MillisecondsToInterval(250)))
+ {
+ fprintf(stderr, ".");
+ didit += 1;
+ }
+ if (didit < 5) rv = 1;
+}
+
+PRIntn main ()
+{
+ PRUint32 elapsed;
+ PRThread *thread;
+ struct timeval timein, timeout;
+ PRInt32 onePercent = 3000000UL / 100UL;
+
+ fprintf (stderr, "First sleep will sleep 3 seconds.\n");
+ fprintf (stderr, " sleep 1 begin\n");
+ (void)GTOD(&timein);
+ sleep (3);
+ (void)GTOD(&timeout);
+ fprintf (stderr, " sleep 1 end\n");
+ elapsed = 1000000UL * (timeout.tv_sec - timein.tv_sec);
+ elapsed += (timeout.tv_usec - timein.tv_usec);
+ fprintf(stderr, "elapsed %u usecs\n", elapsed);
+ if (labs(elapsed - 3000000UL) > onePercent) rv = 1;
+
+ PR_Init (PR_USER_THREAD, PR_PRIORITY_NORMAL, 100);
+ PR_STDIO_INIT();
+
+ fprintf (stderr, "Second sleep should do the same (does it?).\n");
+ fprintf (stderr, " sleep 2 begin\n");
+ (void)GTOD(&timein);
+ sleep (3);
+ (void)GTOD(&timeout);
+ fprintf (stderr, " sleep 2 end\n");
+ elapsed = 1000000UL * (timeout.tv_sec - timein.tv_sec);
+ elapsed += (timeout.tv_usec - timein.tv_usec);
+ fprintf(stderr, "elapsed %u usecs\n", elapsed);
+ if (labs(elapsed - 3000000UL) > onePercent) rv = 1;
+
+ fprintf (stderr, "What happens to other threads?\n");
+ fprintf (stderr, "You should see dots every quarter second.\n");
+ fprintf (stderr, "If you don't, you're probably running on classic NSPR.\n");
+ thread = PR_CreateThread(
+ PR_USER_THREAD, Other, NULL, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+ fprintf (stderr, " sleep 2 begin\n");
+ (void)GTOD(&timein);
+ sleep (3);
+ (void)GTOD(&timeout);
+ fprintf (stderr, " sleep 2 end\n");
+ PR_Interrupt(thread);
+ PR_JoinThread(thread);
+ elapsed = 1000000UL * (timeout.tv_sec - timein.tv_sec);
+ elapsed += (timeout.tv_usec - timein.tv_usec);
+ fprintf(stderr, "elapsed %u usecs\n", elapsed);
+ if (labs(elapsed - 3000000UL) > onePercent) rv = 1;
+ fprintf(stderr, "%s\n", (0 == rv) ? "PASSED" : "FAILED");
+ return rv;
+}
+
+#else /* defined(XP_UNIX) */
+
+PRIntn main()
+{
+ return 2;
+}
+
+#endif /* defined(XP_UNIX) */
+
diff --git a/pr/tests/socket.c b/pr/tests/socket.c
new file mode 100644
index 00000000..47c134d2
--- /dev/null
+++ b/pr/tests/socket.c
@@ -0,0 +1,1633 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+**
+** Name: socket.c
+**
+** Description: Test socket functionality.
+**
+** Modification History:
+*/
+#include "primpl.h"
+
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#ifdef XP_UNIX
+#include <sys/mman.h>
+#endif
+
+static int _debug_on = 0;
+
+#ifdef XP_MAC
+#include "prlog.h"
+#include "prsem.h"
+int fprintf(FILE *stream, const char *fmt, ...)
+{
+ PR_LogPrint(fmt);
+ return 0;
+}
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+#else
+#include "obsolete/prsem.h"
+#endif
+
+#ifdef XP_PC
+#define mode_t int
+#endif
+
+#define DPRINTF(arg) if (_debug_on) printf arg
+
+#ifdef XP_PC
+char *TEST_DIR = "prdir";
+char *SMALL_FILE_NAME = "prsmallf";
+char *LARGE_FILE_NAME = "prlargef";
+#else
+char *TEST_DIR = "/tmp/prsocket_test_dir";
+char *SMALL_FILE_NAME = "/tmp/prsocket_test_dir/small_file";
+char *LARGE_FILE_NAME = "/tmp/prsocket_test_dir/large_file";
+#endif
+#define SMALL_FILE_SIZE (8 * 1024) /* 8 KB */
+#define SMALL_FILE_HEADER_SIZE (64) /* 64 bytes */
+#define LARGE_FILE_SIZE (3 * 1024 * 1024) /* 3 MB */
+
+#define BUF_DATA_SIZE (2 * 1024)
+#define TCP_MESG_SIZE 1024
+/*
+ * set UDP datagram size small enough that datagrams sent to a port on the
+ * local host will not be lost
+ */
+#define UDP_DGRAM_SIZE 128
+#define NUM_TCP_CLIENTS 10
+#define NUM_UDP_CLIENTS 10
+
+#ifndef XP_MAC
+#define NUM_TRANSMITFILE_CLIENTS 4
+#else
+/* Mac can't handle more than 2* (3Mb) allocations for large file size buffers */
+#define NUM_TRANSMITFILE_CLIENTS 2
+#endif
+
+#define NUM_TCP_CONNECTIONS_PER_CLIENT 5
+#define NUM_TCP_MESGS_PER_CONNECTION 10
+#define NUM_UDP_DATAGRAMS_PER_CLIENT 5
+#define TCP_SERVER_PORT 10000
+#define UDP_SERVER_PORT TCP_SERVER_PORT
+#define SERVER_MAX_BIND_COUNT 100
+
+static PRInt32 num_tcp_clients = NUM_TCP_CLIENTS;
+static PRInt32 num_udp_clients = NUM_UDP_CLIENTS;
+static PRInt32 num_transmitfile_clients = NUM_TRANSMITFILE_CLIENTS;
+static PRInt32 num_tcp_connections_per_client = NUM_TCP_CONNECTIONS_PER_CLIENT;
+static PRInt32 tcp_mesg_size = TCP_MESG_SIZE;
+static PRInt32 num_tcp_mesgs_per_connection = NUM_TCP_MESGS_PER_CONNECTION;
+static PRInt32 num_udp_datagrams_per_client = NUM_UDP_DATAGRAMS_PER_CLIENT;
+static PRInt32 udp_datagram_size = UDP_DGRAM_SIZE;
+
+static PRInt32 thread_count;
+
+int failed_already=0;
+typedef struct buffer {
+ char data[BUF_DATA_SIZE];
+} buffer;
+
+PRNetAddr tcp_server_addr, udp_server_addr;
+
+typedef struct Serve_Client_Param {
+ PRFileDesc *sockfd; /* socket to read from/write to */
+ PRInt32 datalen; /* bytes of data transfered in each read/write */
+} Serve_Client_Param;
+
+typedef struct Server_Param {
+ PRSemaphore *addr_sem; /* sem to post on, after setting up the address */
+ PRMonitor *exit_mon; /* monitor to signal on exit */
+ PRInt32 *exit_counter; /* counter to decrement, before exit */
+ PRInt32 datalen; /* bytes of data transfered in each read/write */
+} Server_Param;
+
+
+typedef struct Client_Param {
+ PRNetAddr server_addr;
+ PRMonitor *exit_mon; /* monitor to signal on exit */
+ PRInt32 *exit_counter; /* counter to decrement, before exit */
+ PRInt32 datalen;
+ PRInt32 udp_connect; /* if set clients connect udp sockets */
+} Client_Param;
+
+/*
+ * readn
+ * read data from sockfd into buf
+ */
+static PRInt32
+readn(PRFileDesc *sockfd, char *buf, int len)
+{
+ int rem;
+ int bytes;
+ int offset = 0;
+
+ for (rem=len; rem; offset += bytes, rem -= bytes) {
+ DPRINTF(("thread = 0x%lx: calling PR_Recv, bytes = %d\n",
+ PR_GetCurrentThread(), rem));
+ bytes = PR_Recv(sockfd, buf + offset, rem, 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ DPRINTF(("thread = 0x%lx: returning from PR_Recv, bytes = %d\n",
+ PR_GetCurrentThread(), bytes));
+ if (bytes <= 0)
+ return -1;
+ }
+ return len;
+}
+
+/*
+ * writen
+ * write data from buf to sockfd
+ */
+static PRInt32
+writen(PRFileDesc *sockfd, char *buf, int len)
+{
+ int rem;
+ int bytes;
+ int offset = 0;
+
+ for (rem=len; rem; offset += bytes, rem -= bytes) {
+ DPRINTF(("thread = 0x%lx: calling PR_Send, bytes = %d\n",
+ PR_GetCurrentThread(), rem));
+ bytes = PR_Send(sockfd, buf + offset, rem, 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ DPRINTF(("thread = 0x%lx: returning from PR_Send, bytes = %d\n",
+ PR_GetCurrentThread(), bytes));
+ if (bytes <= 0)
+ return -1;
+ }
+ return len;
+}
+
+/*
+ * Serve_Client
+ * Thread, started by the server, for serving a client connection.
+ * Reads data from socket and writes it back, unmodified, and
+ * closes the socket
+ */
+static void PR_CALLBACK
+Serve_Client(void *arg)
+{
+ Serve_Client_Param *scp = (Serve_Client_Param *) arg;
+ PRFileDesc *sockfd;
+ buffer *in_buf;
+ PRInt32 bytes, j;
+
+ sockfd = scp->sockfd;
+ bytes = scp->datalen;
+ in_buf = PR_NEW(buffer);
+ if (in_buf == NULL) {
+ fprintf(stderr,"prsocket_test: failed to alloc buffer struct\n");
+ failed_already=1;
+ goto exit;
+ }
+
+
+ for (j = 0; j < num_tcp_mesgs_per_connection; j++) {
+ /*
+ * Read data from client and send it back to the client unmodified
+ */
+ if (readn(sockfd, in_buf->data, bytes) < bytes) {
+ fprintf(stderr,"prsocket_test: ERROR - Serve_Client:readn\n");
+ failed_already=1;
+ goto exit;
+ }
+ /*
+ * shutdown reads, after the last read
+ */
+ if (j == num_tcp_mesgs_per_connection - 1)
+ if (PR_Shutdown(sockfd, PR_SHUTDOWN_RCV) < 0) {
+ fprintf(stderr,"prsocket_test: ERROR - PR_Shutdown\n");
+ }
+ DPRINTF(("Serve_Client [0x%lx]: inbuf[0] = 0x%lx\n",PR_GetCurrentThread(),
+ (*((int *) in_buf->data))));
+ if (writen(sockfd, in_buf->data, bytes) < bytes) {
+ fprintf(stderr,"prsocket_test: ERROR - Serve_Client:writen\n");
+ failed_already=1;
+ goto exit;
+ }
+ }
+ /*
+ * shutdown reads and writes
+ */
+ if (PR_Shutdown(sockfd, PR_SHUTDOWN_BOTH) < 0) {
+ fprintf(stderr,"prsocket_test: ERROR - PR_Shutdown\n");
+ failed_already=1;
+ }
+
+exit:
+ PR_Close(sockfd);
+ if (in_buf) {
+ PR_DELETE(in_buf);
+ }
+}
+
+/*
+ * TCP Server
+ * Server Thread
+ * Bind an address to a socket and listen for incoming connections
+ * Start a Serve_Client thread for each incoming connection.
+ */
+static void PR_CALLBACK
+TCP_Server(void *arg)
+{
+ PRThread *t;
+ Server_Param *sp = (Server_Param *) arg;
+ Serve_Client_Param *scp;
+ PRFileDesc *sockfd, *newsockfd;
+ PRNetAddr netaddr;
+ PRInt32 i;
+ /*
+ * Create a tcp socket
+ */
+ if ((sockfd = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NewTCPSocket failed\n");
+ goto exit;
+ }
+ memset(&netaddr, 0 , sizeof(netaddr));
+ netaddr.inet.family = PR_AF_INET;
+ netaddr.inet.port = PR_htons(TCP_SERVER_PORT);
+ netaddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ /*
+ * try a few times to bind server's address, if addresses are in
+ * use
+ */
+ i = 0;
+ while (PR_Bind(sockfd, &netaddr) < 0) {
+ if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) {
+ netaddr.inet.port += 2;
+ if (i++ < SERVER_MAX_BIND_COUNT)
+ continue;
+ }
+ fprintf(stderr,"prsocket_test: ERROR - PR_Bind failed\n");
+ perror("PR_Bind");
+ failed_already=1;
+ goto exit;
+ }
+
+ if (PR_Listen(sockfd, 32) < 0) {
+ fprintf(stderr,"prsocket_test: ERROR - PR_Listen failed\n");
+ failed_already=1;
+ goto exit;
+ }
+
+ if (PR_GetSockName(sockfd, &netaddr) < 0) {
+ fprintf(stderr,"prsocket_test: ERROR - PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit;
+ }
+
+ DPRINTF(("TCP_Server: PR_BIND netaddr.inet.ip = 0x%lx, netaddr.inet.port = %d\n",
+ netaddr.inet.ip, netaddr.inet.port));
+ tcp_server_addr.inet.family = netaddr.inet.family;
+ tcp_server_addr.inet.port = netaddr.inet.port;
+ tcp_server_addr.inet.ip = netaddr.inet.ip;
+
+ /*
+ * Wake up parent thread because server address is bound and made
+ * available in the global variable 'tcp_server_addr'
+ */
+ PR_PostSem(sp->addr_sem);
+
+ for (i = 0; i < (num_tcp_clients * num_tcp_connections_per_client); i++) {
+
+ if ((newsockfd = PR_Accept(sockfd, &netaddr,
+ PR_INTERVAL_NO_TIMEOUT)) == NULL) {
+ fprintf(stderr,"prsocket_test: ERROR - PR_Accept failed\n");
+ goto exit;
+ }
+ scp = PR_NEW(Serve_Client_Param);
+ if (scp == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NEW failed\n");
+ goto exit;
+ }
+
+ /*
+ * Start a Serve_Client thread for each incoming connection
+ */
+ scp->sockfd = newsockfd;
+ scp->datalen = sp->datalen;
+
+ t = PR_CreateThread(PR_USER_THREAD,
+ Serve_Client, (void *)scp,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (t == NULL) {
+ fprintf(stderr,"prsocket_test: PR_CreateThread failed\n");
+ failed_already=1;
+ goto exit;
+ }
+ DPRINTF(("TCP_Server: Created Serve_Client = 0x%lx\n", t));
+ }
+
+exit:
+ if (sockfd) {
+ PR_Close(sockfd);
+ }
+
+ /*
+ * Decrement exit_counter and notify parent thread
+ */
+
+ PR_EnterMonitor(sp->exit_mon);
+ --(*sp->exit_counter);
+ PR_Notify(sp->exit_mon);
+ PR_ExitMonitor(sp->exit_mon);
+ DPRINTF(("TCP_Server [0x%lx] exiting\n", PR_GetCurrentThread()));
+}
+
+/*
+ * UDP Server
+ * Server Thread
+ * Bind an address to a socket, read data from clients and send data
+ * back to clients
+ */
+static void PR_CALLBACK
+UDP_Server(void *arg)
+{
+ Server_Param *sp = (Server_Param *) arg;
+ PRFileDesc *sockfd;
+ buffer *in_buf;
+ PRNetAddr netaddr;
+ PRInt32 bytes, i, rv = 0;
+
+
+ bytes = sp->datalen;
+ /*
+ * Create a udp socket
+ */
+ if ((sockfd = PR_NewUDPSocket()) == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NewUDPSocket failed\n");
+ failed_already=1;
+ return;
+ }
+ memset(&netaddr, 0 , sizeof(netaddr));
+ netaddr.inet.family = PR_AF_INET;
+ netaddr.inet.port = PR_htons(UDP_SERVER_PORT);
+ netaddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ /*
+ * try a few times to bind server's address, if addresses are in
+ * use
+ */
+ i = 0;
+ while (PR_Bind(sockfd, &netaddr) < 0) {
+ if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) {
+ netaddr.inet.port += 2;
+ if (i++ < SERVER_MAX_BIND_COUNT)
+ continue;
+ }
+ fprintf(stderr,"prsocket_test: ERROR - PR_Bind failed\n");
+ perror("PR_Bind");
+ failed_already=1;
+ return;
+ }
+
+ if (PR_GetSockName(sockfd, &netaddr) < 0) {
+ fprintf(stderr,"prsocket_test: ERROR - PR_GetSockName failed\n");
+ failed_already=1;
+ return;
+ }
+ if (netaddr.inet.port != PR_htons(UDP_SERVER_PORT)) {
+ fprintf(stderr,"prsocket_test: ERROR - tried to bind to UDP "
+ "port %hu, but was bound to port %hu\n",
+ UDP_SERVER_PORT, PR_ntohs(netaddr.inet.port));
+ failed_already=1;
+ return;
+ }
+
+ DPRINTF(("PR_Bind: UDP Server netaddr.inet.ip = 0x%lx, netaddr.inet.port = %d\n",
+ netaddr.inet.ip, netaddr.inet.port));
+ udp_server_addr = netaddr;
+
+ /*
+ * We can't use the IP address returned by PR_GetSockName in
+ * netaddr.inet.ip because netaddr.inet.ip is returned
+ * as 0 (= PR_INADDR_ANY).
+ */
+
+ udp_server_addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+
+ /*
+ * Wake up parent thread because server address is bound and made
+ * available in the global variable 'udp_server_addr'
+ */
+ PR_PostSem(sp->addr_sem);
+
+ bytes = sp->datalen;
+ in_buf = PR_NEW(buffer);
+ if (in_buf == NULL) {
+ fprintf(stderr,"prsocket_test: failed to alloc buffer struct\n");
+ failed_already=1;
+ return;
+ }
+ /*
+ * Receive datagrams from clients and send them back, unmodified, to the
+ * clients
+ */
+ memset(&netaddr, 0 , sizeof(netaddr));
+ for (i = 0; i < (num_udp_clients * num_udp_datagrams_per_client); i++) {
+ DPRINTF(("UDP_Server: calling PR_RecvFrom client - ip = 0x%lx, port = %d bytes = %d inbuf = 0x%lx, inbuf[0] = 0x%lx\n",
+ netaddr.inet.ip, netaddr.inet.port, rv, in_buf->data,
+ in_buf->data[0]));
+
+ rv = PR_RecvFrom(sockfd, in_buf->data, bytes, 0, &netaddr,
+ PR_INTERVAL_NO_TIMEOUT);
+ DPRINTF(("UDP_Server: PR_RecvFrom client - ip = 0x%lx, port = %d bytes = %d inbuf = 0x%lx, inbuf[0] = 0x%lx\n",
+ netaddr.inet.ip, netaddr.inet.port, rv, in_buf->data,
+ in_buf->data[0]));
+ if (rv != bytes) {
+ return;
+ }
+ rv = PR_SendTo(sockfd, in_buf->data, bytes, 0, &netaddr,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv != bytes) {
+ return;
+ }
+ }
+
+ PR_DELETE(in_buf);
+
+ /*
+ * Decrement exit_counter and notify parent thread
+ */
+ PR_EnterMonitor(sp->exit_mon);
+ --(*sp->exit_counter);
+ PR_Notify(sp->exit_mon);
+ PR_ExitMonitor(sp->exit_mon);
+ DPRINTF(("UDP_Server [0x%x] exiting\n", PR_GetCurrentThread()));
+}
+
+/*
+ * TCP_Client
+ * Client Thread
+ * Connect to the server at the address specified in the argument.
+ * Fill in a buffer, write data to server, read it back and check
+ * for data corruption.
+ * Close the socket for server connection
+ */
+static void PR_CALLBACK
+TCP_Client(void *arg)
+{
+ Client_Param *cp = (Client_Param *) arg;
+ PRFileDesc *sockfd;
+ buffer *in_buf, *out_buf;
+ union PRNetAddr netaddr;
+ PRInt32 bytes, i, j;
+
+
+ bytes = cp->datalen;
+ out_buf = PR_NEW(buffer);
+ if (out_buf == NULL) {
+ fprintf(stderr,"prsocket_test: failed to alloc buffer struct\n");
+ failed_already=1;
+ return;
+ }
+ in_buf = PR_NEW(buffer);
+ if (in_buf == NULL) {
+ fprintf(stderr,"prsocket_test: failed to alloc buffer struct\n");
+ failed_already=1;
+ return;
+ }
+ netaddr.inet.family = cp->server_addr.inet.family;
+ netaddr.inet.port = cp->server_addr.inet.port;
+ netaddr.inet.ip = cp->server_addr.inet.ip;
+
+ for (i = 0; i < num_tcp_connections_per_client; i++) {
+ if ((sockfd = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NewTCPSocket failed\n");
+ failed_already=1;
+ return;
+ }
+
+ if (PR_Connect(sockfd, &netaddr,PR_INTERVAL_NO_TIMEOUT) < 0){
+ fprintf(stderr,"prsocket_test: PR_Connect failed\n");
+ failed_already=1;
+ return;
+ }
+ for (j = 0; j < num_tcp_mesgs_per_connection; j++) {
+ /*
+ * fill in random data
+ */
+ memset(out_buf->data, ((PRInt32) (&netaddr)) + i + j, bytes);
+ /*
+ * write to server
+ */
+ if (writen(sockfd, out_buf->data, bytes) < bytes) {
+ fprintf(stderr,"prsocket_test: ERROR - TCP_Client:writen\n");
+ failed_already=1;
+ return;
+ }
+ DPRINTF(("TCP Client [0x%lx]: out_buf = 0x%lx out_buf[0] = 0x%lx\n",
+ PR_GetCurrentThread(), out_buf, (*((int *) out_buf->data))));
+ if (readn(sockfd, in_buf->data, bytes) < bytes) {
+ fprintf(stderr,"prsocket_test: ERROR - TCP_Client:readn\n");
+ failed_already=1;
+ return;
+ }
+ /*
+ * verify the data read
+ */
+ if (memcmp(in_buf->data, out_buf->data, bytes) != 0) {
+ fprintf(stderr,"prsocket_test: ERROR - data corruption\n");
+ failed_already=1;
+ return;
+ }
+ }
+ /*
+ * shutdown reads and writes
+ */
+ if (PR_Shutdown(sockfd, PR_SHUTDOWN_BOTH) < 0) {
+ fprintf(stderr,"prsocket_test: ERROR - PR_Shutdown\n");
+ failed_already=1;
+ }
+ PR_Close(sockfd);
+ }
+
+ PR_DELETE(out_buf);
+ PR_DELETE(in_buf);
+
+ /*
+ * Decrement exit_counter and notify parent thread
+ */
+
+ PR_EnterMonitor(cp->exit_mon);
+ --(*cp->exit_counter);
+ PR_Notify(cp->exit_mon);
+ PR_ExitMonitor(cp->exit_mon);
+ DPRINTF(("TCP_Client [0x%x] exiting\n", PR_GetCurrentThread()));
+}
+
+/*
+ * UDP_Client
+ * Client Thread
+ * Create a socket and bind an address
+ * Communicate with the server at the address specified in the argument.
+ * Fill in a buffer, write data to server, read it back and check
+ * for data corruption.
+ * Close the socket
+ */
+static void PR_CALLBACK
+UDP_Client(void *arg)
+{
+ Client_Param *cp = (Client_Param *) arg;
+ PRFileDesc *sockfd;
+ buffer *in_buf, *out_buf;
+ union PRNetAddr netaddr;
+ PRInt32 bytes, i, rv;
+
+
+ bytes = cp->datalen;
+ out_buf = PR_NEW(buffer);
+ if (out_buf == NULL) {
+ fprintf(stderr,"prsocket_test: failed to alloc buffer struct\n");
+ failed_already=1;
+ return;
+ }
+ in_buf = PR_NEW(buffer);
+ if (in_buf == NULL) {
+ fprintf(stderr,"prsocket_test: failed to alloc buffer struct\n");
+ failed_already=1;
+ return;
+ }
+ if ((sockfd = PR_NewUDPSocket()) == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NewUDPSocket failed\n");
+ failed_already=1;
+ return;
+ }
+
+ /*
+ * bind an address for the client, let the system chose the port
+ * number
+ */
+ memset(&netaddr, 0 , sizeof(netaddr));
+ netaddr.inet.family = PR_AF_INET;
+ netaddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ netaddr.inet.port = PR_htons(0);
+ if (PR_Bind(sockfd, &netaddr) < 0) {
+ fprintf(stderr,"prsocket_test: ERROR - PR_Bind failed\n");
+ perror("PR_Bind");
+ return;
+ }
+
+ if (PR_GetSockName(sockfd, &netaddr) < 0) {
+ fprintf(stderr,"prsocket_test: ERROR - PR_GetSockName failed\n");
+ failed_already=1;
+ return;
+ }
+
+ DPRINTF(("PR_Bind: UDP Client netaddr.inet.ip = 0x%lx, netaddr.inet.port = %d\n",
+ netaddr.inet.ip, netaddr.inet.port));
+
+ netaddr.inet.family = cp->server_addr.inet.family;
+ netaddr.inet.port = cp->server_addr.inet.port;
+ netaddr.inet.ip = cp->server_addr.inet.ip;
+
+ if (cp->udp_connect) {
+ if (PR_Connect(sockfd, &netaddr,PR_INTERVAL_NO_TIMEOUT) < 0){
+ fprintf(stderr,"prsocket_test: PR_Connect failed\n");
+ failed_already=1;
+ return;
+ }
+ }
+
+ for (i = 0; i < num_udp_datagrams_per_client; i++) {
+ /*
+ * fill in random data
+ */
+ DPRINTF(("UDP_Client [0x%lx]: out_buf = 0x%lx bytes = 0x%lx\n",
+ PR_GetCurrentThread(), out_buf->data, bytes));
+ memset(out_buf->data, ((PRInt32) (&netaddr)) + i, bytes);
+ /*
+ * write to server
+ */
+ if (cp->udp_connect)
+ rv = PR_Send(sockfd, out_buf->data, bytes, 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ else
+ rv = PR_SendTo(sockfd, out_buf->data, bytes, 0, &netaddr,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv != bytes) {
+ return;
+ }
+ DPRINTF(("UDP_Client [0x%lx]: out_buf = 0x%lx out_buf[0] = 0x%lx\n",
+ PR_GetCurrentThread(), out_buf, (*((int *) out_buf->data))));
+ if (cp->udp_connect)
+ rv = PR_Recv(sockfd, in_buf->data, bytes, 0,
+ PR_INTERVAL_NO_TIMEOUT);
+ else
+ rv = PR_RecvFrom(sockfd, in_buf->data, bytes, 0, &netaddr,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (rv != bytes) {
+ return;
+ }
+ DPRINTF(("UDP_Client [0x%lx]: in_buf = 0x%lx in_buf[0] = 0x%lx\n",
+ PR_GetCurrentThread(), in_buf, (*((int *) in_buf->data))));
+ /*
+ * verify the data read
+ */
+ if (memcmp(in_buf->data, out_buf->data, bytes) != 0) {
+ fprintf(stderr,"prsocket_test: ERROR - UDP data corruption\n");
+ failed_already=1;
+ return;
+ }
+ }
+ PR_Close(sockfd);
+
+ PR_DELETE(in_buf);
+ PR_DELETE(out_buf);
+
+ /*
+ * Decrement exit_counter and notify parent thread
+ */
+
+ PR_EnterMonitor(cp->exit_mon);
+ --(*cp->exit_counter);
+ PR_Notify(cp->exit_mon);
+ PR_ExitMonitor(cp->exit_mon);
+ PR_DELETE(cp);
+ DPRINTF(("UDP_Client [0x%x] exiting\n", PR_GetCurrentThread()));
+}
+
+/*
+ * TCP_Socket_Client_Server_Test - concurrent server test
+ *
+ * One server and several clients are started
+ * Each client connects to the server and sends a chunk of data
+ * For each connection, server starts another thread to read the data
+ * from the client and send it back to the client, unmodified.
+ * Each client checks that data received from server is same as the
+ * data it sent to the server.
+ *
+ */
+
+static PRInt32
+TCP_Socket_Client_Server_Test(void)
+{
+ int i;
+ PRThread *t;
+ PRThreadScope scope;
+ PRSemaphore *server_sem;
+ Server_Param *sparamp;
+ Client_Param *cparamp;
+ PRMonitor *mon2;
+ PRInt32 datalen;
+
+
+ datalen = tcp_mesg_size;
+ thread_count = 0;
+ /*
+ * start the server thread
+ */
+ sparamp = PR_NEW(Server_Param);
+ if (sparamp == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NEW failed\n");
+ failed_already=1;
+ return -1;
+ }
+ server_sem = PR_NewSem(0);
+ if (server_sem == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NewSem failed\n");
+ failed_already=1;
+ return -1;
+ }
+ mon2 = PR_NewMonitor();
+ if (mon2 == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NewMonitor failed\n");
+ failed_already=1;
+ return -1;
+ }
+ PR_EnterMonitor(mon2);
+
+ sparamp->addr_sem = server_sem;
+ sparamp->exit_mon = mon2;
+ sparamp->exit_counter = &thread_count;
+ sparamp->datalen = datalen;
+ t = PR_CreateThread(PR_USER_THREAD,
+ TCP_Server, (void *)sparamp,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (t == NULL) {
+ fprintf(stderr,"prsocket_test: PR_CreateThread failed\n");
+ failed_already=1;
+ return -1;
+ }
+ DPRINTF(("Created TCP server = 0x%lx\n", t));
+ thread_count++;
+
+ /*
+ * wait till the server address is setup
+ */
+ PR_WaitSem(server_sem);
+
+ /*
+ * Now start a bunch of client threads
+ */
+
+ cparamp = PR_NEW(Client_Param);
+ if (cparamp == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NEW failed\n");
+ failed_already=1;
+ return -1;
+ }
+ cparamp->server_addr = tcp_server_addr;
+ cparamp->server_addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+ cparamp->exit_mon = mon2;
+ cparamp->exit_counter = &thread_count;
+ cparamp->datalen = datalen;
+ for (i = 0; i < num_tcp_clients; i++) {
+ /*
+ * Every other thread is a LOCAL/GLOBAL thread
+ */
+ if (i & 1)
+ scope = PR_LOCAL_THREAD;
+ else
+ scope = PR_GLOBAL_THREAD;
+ t = PR_CreateThread(PR_USER_THREAD,
+ TCP_Client, (void *) cparamp,
+ PR_PRIORITY_NORMAL,
+ scope,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (t == NULL) {
+ fprintf(stderr,"prsocket_test: PR_CreateThread failed\n");
+ failed_already=1;
+ return -1;
+ }
+ DPRINTF(("Created TCP client = 0x%lx\n", t));
+ thread_count++;
+ }
+ /* Wait for server and client threads to exit */
+ while (thread_count) {
+ PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT);
+ DPRINTF(("TCP Server - thread_count = %d\n", thread_count));
+ }
+ PR_ExitMonitor(mon2);
+ printf("%30s","TCP_Socket_Client_Server_Test:");
+ printf("%2ld Server %2ld Clients %2ld connections_per_client\n",1l,
+ num_tcp_clients, num_tcp_connections_per_client);
+ printf("%30s %2ld messages_per_connection %4ld bytes_per_message\n",":",
+ num_tcp_mesgs_per_connection, tcp_mesg_size);
+
+ return 0;
+}
+
+/*
+ * UDP_Socket_Client_Server_Test - iterative server test
+ *
+ * One server and several clients are started
+ * Each client connects to the server and sends a chunk of data
+ * For each connection, server starts another thread to read the data
+ * from the client and send it back to the client, unmodified.
+ * Each client checks that data received from server is same as the
+ * data it sent to the server.
+ *
+ */
+
+static PRInt32
+UDP_Socket_Client_Server_Test(void)
+{
+ int i;
+ PRThread *t;
+ PRSemaphore *server_sem;
+ Server_Param *sparamp;
+ Client_Param *cparamp;
+ PRMonitor *mon2;
+ PRInt32 datalen;
+ PRInt32 udp_connect = 1;
+
+
+ datalen = udp_datagram_size;
+ thread_count = 0;
+ /*
+ * start the server thread
+ */
+ sparamp = PR_NEW(Server_Param);
+ if (sparamp == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NEW failed\n");
+ failed_already=1;
+ return -1;
+ }
+ server_sem = PR_NewSem(0);
+ if (server_sem == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NewSem failed\n");
+ failed_already=1;
+ return -1;
+ }
+ mon2 = PR_NewMonitor();
+ if (mon2 == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NewMonitor failed\n");
+ failed_already=1;
+ return -1;
+ }
+ PR_EnterMonitor(mon2);
+
+ sparamp->addr_sem = server_sem;
+ sparamp->exit_mon = mon2;
+ sparamp->exit_counter = &thread_count;
+ sparamp->datalen = datalen;
+ DPRINTF(("Creating UDP server"));
+ t = PR_CreateThread(PR_USER_THREAD,
+ UDP_Server, (void *)sparamp,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (t == NULL) {
+ fprintf(stderr,"prsocket_test: PR_CreateThread failed\n");
+ failed_already=1;
+ return -1;
+ }
+ thread_count++;
+
+ /*
+ * wait till the server address is setup
+ */
+ PR_WaitSem(server_sem);
+
+ /*
+ * Now start a bunch of client threads
+ */
+
+ for (i = 0; i < num_udp_clients; i++) {
+ cparamp = PR_NEW(Client_Param);
+ if (cparamp == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NEW failed\n");
+ failed_already=1;
+ return -1;
+ }
+ cparamp->server_addr = udp_server_addr;
+ cparamp->exit_mon = mon2;
+ cparamp->exit_counter = &thread_count;
+ cparamp->datalen = datalen;
+ /*
+ * Cause every other client thread to connect udp sockets
+ */
+#ifndef XP_MAC
+ cparamp->udp_connect = udp_connect;
+#else
+ /* No support for UDP connects on Mac */
+ cparamp->udp_connect = 0;
+#endif
+ if (udp_connect)
+ udp_connect = 0;
+ else
+ udp_connect = 1;
+ DPRINTF(("Creating UDP client %d\n", i));
+ t = PR_CreateThread(PR_USER_THREAD,
+ UDP_Client, (void *) cparamp,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (t == NULL) {
+ fprintf(stderr,"prsocket_test: PR_CreateThread failed\n");
+ failed_already=1;
+ return -1;
+ }
+ thread_count++;
+ }
+ /* Wait for server and client threads to exit */
+ while (thread_count) {
+ PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT);
+ DPRINTF(("UDP Server - thread_count = %d\n", thread_count));
+ }
+ PR_ExitMonitor(mon2);
+ printf("%30s","UDP_Socket_Client_Server_Test: ");
+ printf("%2ld Server %2ld Clients\n",1l, num_udp_clients);
+ printf("%30s %2ld datagrams_per_client %4ld bytes_per_datagram\n",":",
+ num_udp_datagrams_per_client, udp_datagram_size);
+
+ return 0;
+}
+
+static PRFileDesc *small_file_fd, *large_file_fd;
+static void *small_file_addr, *small_file_header, *large_file_addr;
+/*
+ * TransmitFile_Client
+ * Client Thread
+ */
+static void
+TransmitFile_Client(void *arg)
+{
+ PRFileDesc *sockfd;
+ union PRNetAddr netaddr;
+ char *small_buf, *large_buf;
+ Client_Param *cp = (Client_Param *) arg;
+
+ small_buf = (char*)PR_Malloc(SMALL_FILE_SIZE + SMALL_FILE_HEADER_SIZE);
+ if (small_buf == NULL) {
+ fprintf(stderr,"prsocket_test: failed to alloc buffer\n");
+ failed_already=1;
+ return;
+ }
+ large_buf = (char*)PR_Malloc(LARGE_FILE_SIZE);
+ if (large_buf == NULL) {
+ fprintf(stderr,"prsocket_test: failed to alloc buffer\n");
+ failed_already=1;
+ return;
+ }
+ netaddr.inet.family = cp->server_addr.inet.family;
+ netaddr.inet.port = cp->server_addr.inet.port;
+ netaddr.inet.ip = cp->server_addr.inet.ip;
+
+ if ((sockfd = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NewTCPSocket failed\n");
+ failed_already=1;
+ return;
+ }
+
+ if (PR_Connect(sockfd, &netaddr,PR_INTERVAL_NO_TIMEOUT) < 0){
+ fprintf(stderr,"prsocket_test: PR_Connect failed\n");
+ failed_already=1;
+ return;
+ }
+ /*
+ * read the small file and verify the data
+ */
+ if (readn(sockfd, small_buf, SMALL_FILE_SIZE + SMALL_FILE_HEADER_SIZE)
+ != (SMALL_FILE_SIZE + SMALL_FILE_HEADER_SIZE)) {
+ fprintf(stderr,
+ "prsocket_test: TransmitFile_Client failed to receive file\n");
+ failed_already=1;
+ return;
+ }
+#ifdef XP_UNIX
+ if (memcmp(small_file_header, small_buf, SMALL_FILE_HEADER_SIZE) != 0){
+ fprintf(stderr,
+ "prsocket_test: TransmitFile_Client ERROR - small file header data corruption\n");
+ failed_already=1;
+ return;
+ }
+ if (memcmp(small_file_addr, small_buf + SMALL_FILE_HEADER_SIZE,
+ SMALL_FILE_SIZE) != 0) {
+ fprintf(stderr,
+ "prsocket_test: TransmitFile_Client ERROR - small file data corruption\n");
+ failed_already=1;
+ return;
+ }
+#endif
+ /*
+ * read the large file and verify the data
+ */
+ if (readn(sockfd, large_buf, LARGE_FILE_SIZE) != LARGE_FILE_SIZE) {
+ fprintf(stderr,
+ "prsocket_test: TransmitFile_Client failed to receive file\n");
+ failed_already=1;
+ return;
+ }
+#ifdef XP_UNIX
+ if (memcmp(large_file_addr, large_buf, LARGE_FILE_SIZE) != 0) {
+ fprintf(stderr,
+ "prsocket_test: TransmitFile_Client ERROR - large file data corruption\n");
+ failed_already=1;
+ }
+#endif
+ PR_DELETE(small_buf);
+ PR_DELETE(large_buf);
+ PR_Close(sockfd);
+
+ /*
+ * Decrement exit_counter and notify parent thread
+ */
+
+ PR_EnterMonitor(cp->exit_mon);
+ --(*cp->exit_counter);
+ PR_Notify(cp->exit_mon);
+ PR_ExitMonitor(cp->exit_mon);
+ DPRINTF(("TransmitFile_Client [0x%lx] exiting\n", PR_GetCurrentThread()));
+}
+
+/*
+ * Serve_TransmitFile_Client
+ * Thread, started by the server, for serving a client connection.
+ * Trasmits a small file, with a header, and a large file, without
+ * a header
+ */
+static void
+Serve_TransmitFile_Client(void *arg)
+{
+ Serve_Client_Param *scp = (Serve_Client_Param *) arg;
+ PRFileDesc *sockfd;
+ PRInt32 bytes;
+ PRFileDesc *local_small_file_fd=NULL;
+ PRFileDesc *local_large_file_fd=NULL;
+
+ sockfd = scp->sockfd;
+ local_small_file_fd = PR_Open(SMALL_FILE_NAME, PR_RDONLY,0);
+
+ if (local_small_file_fd == NULL) {
+ fprintf(stderr,"prsocket_test failed to open file for transmitting %s\n",
+ SMALL_FILE_NAME);
+ failed_already=1;
+ goto done;
+ }
+ local_large_file_fd = PR_Open(LARGE_FILE_NAME, PR_RDONLY,0);
+
+ if (local_large_file_fd == NULL) {
+ fprintf(stderr,"prsocket_test failed to open file for transmitting %s\n",
+ LARGE_FILE_NAME);
+ failed_already=1;
+ goto done;
+ }
+ bytes = PR_TransmitFile(sockfd, local_small_file_fd, small_file_header,
+ SMALL_FILE_HEADER_SIZE, PR_TRANSMITFILE_KEEP_OPEN,
+ PR_INTERVAL_NO_TIMEOUT);
+ if (bytes != (SMALL_FILE_SIZE+ SMALL_FILE_HEADER_SIZE)) {
+ fprintf(stderr,
+ "prsocet_test: PR_TransmitFile failed: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ }
+ bytes = PR_TransmitFile(sockfd, local_large_file_fd, NULL, 0,
+ PR_TRANSMITFILE_CLOSE_SOCKET, PR_INTERVAL_NO_TIMEOUT);
+ if (bytes != LARGE_FILE_SIZE) {
+ fprintf(stderr,
+ "prsocket_test: PR_TransmitFile failed: (%ld, %ld)\n",
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ }
+done:
+ if (local_small_file_fd != NULL)
+ PR_Close(local_small_file_fd);
+ if (local_large_file_fd != NULL)
+ PR_Close(local_large_file_fd);
+}
+
+/*
+ * TransmitFile Server
+ * Server Thread
+ * Bind an address to a socket and listen for incoming connections
+ * Create worker threads to service clients
+ */
+static void
+TransmitFile_Server(void *arg)
+{
+ PRThread **t = NULL; /* an array of PRThread pointers */
+ Server_Param *sp = (Server_Param *) arg;
+ Serve_Client_Param *scp;
+ PRFileDesc *sockfd = NULL, *newsockfd;
+ PRNetAddr netaddr;
+ PRInt32 i;
+ PRThreadScope scope;
+
+ t = (PRThread**)PR_MALLOC(num_transmitfile_clients * sizeof(PRThread *));
+ if (t == NULL) {
+ fprintf(stderr, "prsocket_test: run out of memory\n");
+ failed_already=1;
+ goto exit;
+ }
+ /*
+ * Create a tcp socket
+ */
+ if ((sockfd = PR_NewTCPSocket()) == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NewTCPSocket failed\n");
+ failed_already=1;
+ goto exit;
+ }
+ memset(&netaddr, 0 , sizeof(netaddr));
+ netaddr.inet.family = PR_AF_INET;
+ netaddr.inet.port = PR_htons(TCP_SERVER_PORT);
+ netaddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+ /*
+ * try a few times to bind server's address, if addresses are in
+ * use
+ */
+ i = 0;
+ while (PR_Bind(sockfd, &netaddr) < 0) {
+ if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) {
+ netaddr.inet.port += 2;
+ if (i++ < SERVER_MAX_BIND_COUNT)
+ continue;
+ }
+ fprintf(stderr,"prsocket_test: ERROR - PR_Bind failed\n");
+ failed_already=1;
+ perror("PR_Bind");
+ goto exit;
+ }
+
+ if (PR_Listen(sockfd, 32) < 0) {
+ fprintf(stderr,"prsocket_test: ERROR - PR_Listen failed\n");
+ failed_already=1;
+ goto exit;
+ }
+
+ if (PR_GetSockName(sockfd, &netaddr) < 0) {
+ fprintf(stderr,
+ "prsocket_test: ERROR - PR_GetSockName failed\n");
+ failed_already=1;
+ goto exit;
+ }
+
+ DPRINTF(("TCP_Server: PR_BIND netaddr.inet.ip = 0x%lx, netaddr.inet.port = %d\n",
+ netaddr.inet.ip, netaddr.inet.port));
+ tcp_server_addr.inet.family = netaddr.inet.family;
+ tcp_server_addr.inet.port = netaddr.inet.port;
+ tcp_server_addr.inet.ip = netaddr.inet.ip;
+
+ /*
+ * Wake up parent thread because server address is bound and made
+ * available in the global variable 'tcp_server_addr'
+ */
+ PR_PostSem(sp->addr_sem);
+
+ for (i = 0; i < num_transmitfile_clients ; i++) {
+
+ if ((newsockfd = PR_Accept(sockfd, &netaddr,
+ PR_INTERVAL_NO_TIMEOUT)) == NULL) {
+ fprintf(stderr,
+ "prsocket_test: ERROR - PR_Accept failed\n");
+ failed_already=1;
+ goto exit;
+ }
+ scp = PR_NEW(Serve_Client_Param);
+ if (scp == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NEW failed\n");
+ failed_already=1;
+ goto exit;
+ }
+
+ /*
+ * Start a Serve_Client thread for each incoming connection
+ */
+ scp->sockfd = newsockfd;
+ scp->datalen = sp->datalen;
+
+ /*
+ * create LOCAL and GLOBAL threads alternately
+ */
+ if (i & 1)
+ scope = PR_LOCAL_THREAD;
+ else
+ scope = PR_GLOBAL_THREAD;
+ t[i] = PR_CreateThread(PR_USER_THREAD,
+ Serve_TransmitFile_Client, (void *)scp,
+ PR_PRIORITY_NORMAL,
+ scope,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (t[i] == NULL) {
+ fprintf(stderr,
+ "prsocket_test: PR_CreateThread failed\n");
+ failed_already=1;
+ goto exit;
+ }
+ DPRINTF(("TransmitFile_Server: Created Serve_TransmitFile_Client = 0x%lx\n", t));
+ }
+
+ /*
+ * Wait for all the worker threads to end, so that we know
+ * they are no longer using the small and large file fd's.
+ */
+
+ for (i = 0; i < num_transmitfile_clients; i++) {
+ PR_JoinThread(t[i]);
+ }
+
+exit:
+ if (t) {
+ PR_DELETE(t);
+ }
+ if (sockfd) {
+ PR_Close(sockfd);
+ }
+
+ /*
+ * Decrement exit_counter and notify parent thread
+ */
+
+ PR_EnterMonitor(sp->exit_mon);
+ --(*sp->exit_counter);
+ PR_Notify(sp->exit_mon);
+ PR_ExitMonitor(sp->exit_mon);
+ DPRINTF(("TransmitFile_Server [0x%lx] exiting\n", PR_GetCurrentThread()));
+}
+
+/*
+ * Socket_Misc_Test - test miscellaneous functions
+ *
+ */
+static PRInt32
+Socket_Misc_Test(void)
+{
+ PRIntn i, rv = 0, bytes, count, len;
+ PRThread *t;
+ PRThreadScope scope;
+ PRSemaphore *server_sem;
+ Server_Param *sparamp;
+ Client_Param *cparamp;
+ PRMonitor *mon2;
+ PRInt32 datalen;
+
+ /*
+ * We deliberately pick a buffer size that is not a nice multiple
+ * of 1024.
+ */
+#define TRANSMITFILE_BUF_SIZE (4 * 1024 - 11)
+
+ typedef struct {
+ char data[TRANSMITFILE_BUF_SIZE];
+ } file_buf;
+ file_buf *buf = NULL;
+
+ /*
+ * create file(s) to be transmitted
+ */
+ if ((PR_MkDir(TEST_DIR, 0777)) < 0) {
+ printf("prsocket_test failed to create dir %s\n",TEST_DIR);
+ failed_already=1;
+ return -1;
+ }
+
+ small_file_fd = PR_Open(SMALL_FILE_NAME, PR_RDWR | PR_CREATE_FILE,0777);
+
+ if (small_file_fd == NULL) {
+ fprintf(stderr,"prsocket_test failed to create/open file %s\n",
+ SMALL_FILE_NAME);
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ buf = PR_NEW(file_buf);
+ if (buf == NULL) {
+ fprintf(stderr,"prsocket_test failed to allocate buffer\n");
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ /*
+ * fill in random data
+ */
+ for (i = 0; i < TRANSMITFILE_BUF_SIZE; i++) {
+ buf->data[i] = i;
+ }
+ count = 0;
+ do {
+ len = (SMALL_FILE_SIZE - count) > TRANSMITFILE_BUF_SIZE ?
+ TRANSMITFILE_BUF_SIZE : (SMALL_FILE_SIZE - count);
+ bytes = PR_Write(small_file_fd, buf->data, len);
+ if (bytes <= 0) {
+ fprintf(stderr,
+ "prsocket_test failed to write to file %s\n",
+ SMALL_FILE_NAME);
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ count += bytes;
+ } while (count < SMALL_FILE_SIZE);
+#ifdef XP_UNIX
+ /*
+ * map the small file; used in checking for data corruption
+ */
+ small_file_addr = mmap(0, SMALL_FILE_SIZE, PROT_READ,
+ MAP_PRIVATE, small_file_fd->secret->md.osfd, 0);
+ if (small_file_addr == (void *) -1) {
+ fprintf(stderr,"prsocket_test failed to mmap file %s\n",
+ SMALL_FILE_NAME);
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+#endif
+ /*
+ * header for small file
+ */
+ small_file_header = PR_MALLOC(SMALL_FILE_HEADER_SIZE);
+ if (small_file_header == NULL) {
+ fprintf(stderr,"prsocket_test failed to malloc header file\n");
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ memset(small_file_header, (int) PR_IntervalNow(),
+ SMALL_FILE_HEADER_SIZE);
+ /*
+ * setup large file
+ */
+ large_file_fd = PR_Open(LARGE_FILE_NAME, PR_RDWR | PR_CREATE_FILE,0777);
+
+ if (large_file_fd == NULL) {
+ fprintf(stderr,"prsocket_test failed to create/open file %s\n",
+ LARGE_FILE_NAME);
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ /*
+ * fill in random data
+ */
+ for (i = 0; i < TRANSMITFILE_BUF_SIZE; i++) {
+ buf->data[i] = i;
+ }
+ count = 0;
+ do {
+ len = (LARGE_FILE_SIZE - count) > TRANSMITFILE_BUF_SIZE ?
+ TRANSMITFILE_BUF_SIZE : (LARGE_FILE_SIZE - count);
+ bytes = PR_Write(large_file_fd, buf->data, len);
+ if (bytes <= 0) {
+ fprintf(stderr,
+ "prsocket_test failed to write to file %s: (%ld, %ld)\n",
+ LARGE_FILE_NAME,
+ PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ count += bytes;
+ } while (count < LARGE_FILE_SIZE);
+#ifdef XP_UNIX
+ /*
+ * map the large file; used in checking for data corruption
+ */
+ large_file_addr = mmap(0, LARGE_FILE_SIZE, PROT_READ,
+ MAP_PRIVATE, large_file_fd->secret->md.osfd, 0);
+ if (large_file_addr == (void *) -1) {
+ fprintf(stderr,"prsocket_test failed to mmap file %s\n",
+ LARGE_FILE_NAME);
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+#endif
+ datalen = tcp_mesg_size;
+ thread_count = 0;
+ /*
+ * start the server thread
+ */
+ sparamp = PR_NEW(Server_Param);
+ if (sparamp == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NEW failed\n");
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ server_sem = PR_NewSem(0);
+ if (server_sem == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NewSem failed\n");
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ mon2 = PR_NewMonitor();
+ if (mon2 == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NewMonitor failed\n");
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ PR_EnterMonitor(mon2);
+
+ sparamp->addr_sem = server_sem;
+ sparamp->exit_mon = mon2;
+ sparamp->exit_counter = &thread_count;
+ sparamp->datalen = datalen;
+ t = PR_CreateThread(PR_USER_THREAD,
+ TransmitFile_Server, (void *)sparamp,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (t == NULL) {
+ fprintf(stderr,"prsocket_test: PR_CreateThread failed\n");
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ DPRINTF(("Created TCP server = 0x%x\n", t));
+ thread_count++;
+
+ /*
+ * wait till the server address is setup
+ */
+ PR_WaitSem(server_sem);
+
+ /*
+ * Now start a bunch of client threads
+ */
+
+ cparamp = PR_NEW(Client_Param);
+ if (cparamp == NULL) {
+ fprintf(stderr,"prsocket_test: PR_NEW failed\n");
+ failed_already=1;
+ rv = -1;
+ goto done;
+ }
+ cparamp->server_addr = tcp_server_addr;
+ cparamp->server_addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
+ cparamp->exit_mon = mon2;
+ cparamp->exit_counter = &thread_count;
+ cparamp->datalen = datalen;
+ for (i = 0; i < num_transmitfile_clients; i++) {
+ /*
+ * Every other thread is a LOCAL/GLOBAL thread
+ */
+ if (i & 1)
+ scope = PR_GLOBAL_THREAD;
+ else
+ scope = PR_LOCAL_THREAD;
+ t = PR_CreateThread(PR_USER_THREAD,
+ TransmitFile_Client, (void *) cparamp,
+ PR_PRIORITY_NORMAL,
+ scope,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (t == NULL) {
+ fprintf(stderr,"prsocket_test: PR_CreateThread failed\n");
+ rv = -1;
+ failed_already=1;
+ goto done;
+ }
+ DPRINTF(("Created TransmitFile client = 0x%lx\n", t));
+ thread_count++;
+ }
+ /* Wait for server and client threads to exit */
+ while (thread_count) {
+ PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT);
+ DPRINTF(("Socket_Misc_Test - thread_count = %d\n", thread_count));
+ }
+ PR_ExitMonitor(mon2);
+done:
+ if (buf) {
+ PR_DELETE(buf);
+ }
+#ifdef XP_UNIX
+ munmap((char*)small_file_addr, SMALL_FILE_SIZE);
+ munmap((char*)large_file_addr, LARGE_FILE_SIZE);
+#endif
+ PR_Close(small_file_fd);
+ PR_Close(large_file_fd);
+ if ((PR_Delete(SMALL_FILE_NAME)) == PR_FAILURE) {
+ fprintf(stderr,"prsocket_test: failed to unlink file %s\n",
+ SMALL_FILE_NAME);
+ failed_already=1;
+ }
+ if ((PR_Delete(LARGE_FILE_NAME)) == PR_FAILURE) {
+ fprintf(stderr,"prsocket_test: failed to unlink file %s\n",
+ LARGE_FILE_NAME);
+ failed_already=1;
+ }
+ if ((PR_RmDir(TEST_DIR)) == PR_FAILURE) {
+ fprintf(stderr,"prsocket_test failed to rmdir %s: (%ld, %ld)\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ failed_already=1;
+ }
+
+ printf("%-29s%s","Socket_Misc_Test",":");
+ printf("%2d Server %2d Clients\n",1, num_transmitfile_clients);
+ printf("%30s Sizes of Transmitted Files - %4d KB, %2d MB \n",":",
+ SMALL_FILE_SIZE/1024, LARGE_FILE_SIZE/(1024 * 1024));
+
+
+ return rv;
+}
+/************************************************************************/
+
+/*
+ * Test Socket NSPR APIs
+ */
+
+int
+main(int argc, char **argv)
+{
+ /*
+ * -d debug mode
+ */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ _debug_on = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("socket.log");
+#endif
+ PR_SetConcurrency(4);
+ /*
+ * run client-server test with TCP
+ */
+ if (TCP_Socket_Client_Server_Test() < 0) {
+ printf("TCP_Socket_Client_Server_Test failed\n");
+ goto done;
+ } else
+ printf("TCP_Socket_Client_Server_Test Passed\n");
+ /*
+ * run client-server test with UDP
+ */
+ if (UDP_Socket_Client_Server_Test() < 0) {
+ printf("UDP_Socket_Client_Server_Test failed\n");
+ goto done;
+ } else
+ printf("UDP_Socket_Client_Server_Test Passed\n");
+ /*
+ * Misc socket tests - including transmitfile, etc.
+ */
+
+#if !defined(WIN16)
+ /*
+** The 'transmit file' test does not run because
+** transmit file is not implemented in NSPR yet.
+**
+*/
+ if (Socket_Misc_Test() < 0) {
+ printf("Socket_Misc_Test failed\n");
+ failed_already=1;
+ goto done;
+ } else
+ printf("Socket_Misc_Test passed\n");
+
+ /*
+ * run client-server test with TCP again to test
+ * recycling used sockets from PR_TransmitFile().
+ */
+ if (TCP_Socket_Client_Server_Test() < 0) {
+ printf("TCP_Socket_Client_Server_Test failed\n");
+ goto done;
+ } else
+ printf("TCP_Socket_Client_Server_Test Passed\n");
+#endif
+
+done:
+ PR_Cleanup();
+ if (failed_already) return 1;
+ else return 0;
+}
diff --git a/pr/tests/sockopt.c b/pr/tests/sockopt.c
new file mode 100644
index 00000000..e3eb0eab
--- /dev/null
+++ b/pr/tests/sockopt.c
@@ -0,0 +1,252 @@
+/* -*- 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 "nspr.h"
+#include "prio.h"
+#include "prinit.h"
+#include "prprf.h"
+#ifdef XP_MAC
+#include "probslet.h"
+#else
+#include "obsolete/probslet.h"
+#endif
+
+#include "plerror.h"
+
+static PRFileDesc *err = NULL;
+static PRBool failed = PR_FALSE;
+
+#ifndef XP_MAC
+static void Failed(const char *msg1, const char *msg2)
+{
+ if (NULL != msg1) PR_fprintf(err, "%s ", msg1);
+ PL_FPrintError(err, msg2);
+ failed = PR_TRUE;
+} /* Failed */
+
+#else
+#include "prlog.h"
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+static void Failed(const char *msg1, const char *msg2)
+{
+ if (NULL != msg1) printf("%s ", msg1);
+ printf (msg2);
+ failed |= PR_TRUE;
+} /* Failed */
+
+#endif
+
+static PRSockOption Incr(PRSockOption *option)
+{
+ PRIntn val = ((PRIntn)*option) + 1;
+ *option = (PRSockOption)val;
+ return (PRSockOption)val;
+} /* Incr */
+
+PRIntn main(PRIntn argc, char *argv)
+{
+ PRStatus rv;
+ PRFileDesc *udp = PR_NewUDPSocket();
+ PRFileDesc *tcp = PR_NewTCPSocket();
+ const char *tag[] =
+ {
+ "PR_SockOpt_Nonblocking", /* nonblocking io */
+ "PR_SockOpt_Linger", /* linger on close if data present */
+ "PR_SockOpt_Reuseaddr", /* allow local address reuse */
+ "PR_SockOpt_Keepalive", /* keep connections alive */
+ "PR_SockOpt_RecvBufferSize", /* send buffer size */
+ "PR_SockOpt_SendBufferSize", /* receive buffer size */
+
+ "PR_SockOpt_IpTimeToLive", /* time to live */
+ "PR_SockOpt_IpTypeOfService", /* type of service and precedence */
+
+ "PR_SockOpt_AddMember", /* add an IP group membership */
+ "PR_SockOpt_DropMember", /* drop an IP group membership */
+ "PR_SockOpt_McastInterface", /* multicast interface address */
+ "PR_SockOpt_McastTimeToLive", /* multicast timetolive */
+ "PR_SockOpt_McastLoopback", /* multicast loopback */
+
+ "PR_SockOpt_NoDelay", /* don't delay send to coalesce packets */
+ "PR_SockOpt_MaxSegment", /* maximum segment size */
+ "PR_SockOpt_Last"
+ };
+
+ err = PR_GetSpecialFD(PR_StandardError);
+ PR_STDIO_INIT();
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("sockopt.log");
+#endif
+
+ if (NULL == udp) Failed("PR_NewUDPSocket()", NULL);
+ else if (NULL == tcp) Failed("PR_NewTCPSocket()", NULL);
+ else
+ {
+ PRSockOption option;
+ PRLinger linger;
+ PRUint32 ttl = 20;
+ PRBool boolean = PR_TRUE;
+ PRUint32 segment = 1024;
+ for(option = PR_SockOpt_Linger; option < PR_SockOpt_Last; Incr(&option))
+ {
+ void *value;
+ PRInt32 *size;
+ PRInt32 ttlsize = sizeof(ttl);
+ PRInt32 segmentsize = sizeof(segment);
+ PRInt32 booleansize = sizeof(boolean);
+ PRInt32 lingersize = sizeof(linger);
+
+ PRFileDesc *socket = udp;
+ switch (option)
+ {
+ case PR_SockOpt_Linger: /* linger on close if data present */
+ socket = tcp;
+ linger.polarity = PR_TRUE;
+ linger.linger = PR_SecondsToInterval(1);
+ value = &linger;
+ size = &lingersize;
+ break;
+ case PR_SockOpt_NoDelay: /* don't delay send to coalesce packets */
+ case PR_SockOpt_Keepalive: /* keep connections alive */
+ socket = tcp;
+ case PR_SockOpt_Reuseaddr: /* allow local address reuse */
+ value = &boolean;
+ size = &booleansize;
+ break;
+#ifndef WIN32
+ case PR_SockOpt_MaxSegment: /* maximum segment size */
+ socket = tcp;
+#endif
+ case PR_SockOpt_RecvBufferSize: /* send buffer size */
+ case PR_SockOpt_SendBufferSize: /* receive buffer size */
+ value = &segment;
+ size = &segmentsize;
+ break;
+
+ case PR_SockOpt_IpTimeToLive: /* time to live */
+ value = &ttl;
+ size = &ttlsize;
+ break;
+ case PR_SockOpt_IpTypeOfService: /* type of service and precedence */
+ case PR_SockOpt_AddMember: /* add an IP group membership */
+ case PR_SockOpt_DropMember: /* drop an IP group membership */
+ case PR_SockOpt_McastInterface: /* multicast interface address */
+ case PR_SockOpt_McastTimeToLive: /* multicast timetolive */
+ case PR_SockOpt_McastLoopback: /* multicast loopback */
+ default:
+ continue;
+ }
+ /*
+ * TCP_MAXSEG can only be read, not set
+ */
+ if (option != PR_SockOpt_MaxSegment) {
+#ifdef WIN32
+ if ((option != PR_SockOpt_McastTimeToLive) &&
+ (option != PR_SockOpt_McastLoopback))
+#endif
+ {
+ rv = PR_SetSockOpt(socket, option, value, *size);
+ if (PR_FAILURE == rv) Failed("PR_SetSockOpt()",
+ tag[option]);
+ }
+ }
+
+ rv = PR_GetSockOpt(socket, option, &value, size);
+ if (PR_FAILURE == rv) Failed("PR_GetSockOpt()", tag[option]);
+ }
+ for(option = PR_SockOpt_Linger; option < PR_SockOpt_Last; Incr(&option))
+ {
+ PRSocketOptionData data;
+ PRFileDesc *fd = tcp;
+ data.option = option;
+ switch (option)
+ {
+ case PR_SockOpt_Nonblocking:
+ data.value.non_blocking = PR_TRUE;
+ break;
+ case PR_SockOpt_Linger:
+ data.value.linger.polarity = PR_TRUE;
+ data.value.linger.linger = PR_SecondsToInterval(2);
+ break;
+ case PR_SockOpt_Reuseaddr:
+ data.value.reuse_addr = PR_TRUE;
+ break;
+ case PR_SockOpt_Keepalive:
+ data.value.keep_alive = PR_TRUE;
+ break;
+ case PR_SockOpt_RecvBufferSize:
+ data.value.recv_buffer_size = segment;
+ break;
+ case PR_SockOpt_SendBufferSize:
+ data.value.send_buffer_size = segment;
+ break;
+ case PR_SockOpt_IpTimeToLive:
+ data.value.ip_ttl = 64;
+ break;
+ case PR_SockOpt_IpTypeOfService:
+ data.value.tos = 0;
+ break;
+ case PR_SockOpt_McastTimeToLive:
+ fd = udp;
+ data.value.mcast_ttl = 4;
+ break;
+ case PR_SockOpt_McastLoopback:
+ fd = udp;
+ data.value.mcast_loopback = PR_TRUE;
+ break;
+ case PR_SockOpt_NoDelay:
+ data.value.no_delay = PR_TRUE;
+ break;
+#ifndef WIN32
+ case PR_SockOpt_MaxSegment:
+ data.value.max_segment = segment;
+ break;
+#endif
+ default: continue;
+ }
+
+ /*
+ * TCP_MAXSEG can only be read, not set
+ */
+ if (option != PR_SockOpt_MaxSegment) {
+#ifdef WIN32
+ if ((option != PR_SockOpt_McastTimeToLive) &&
+ (option != PR_SockOpt_McastLoopback))
+#endif
+ {
+ rv = PR_SetSocketOption(fd, &data);
+ if (PR_FAILURE == rv)
+ Failed("PR_SetSocketOption()", tag[option]);
+ }
+ }
+
+ rv = PR_GetSocketOption(fd, &data);
+ if (PR_FAILURE == rv) Failed("PR_GetSocketOption()", tag[option]);
+ }
+ }
+#ifndef XP_MAC
+ PR_fprintf(err, "%s\n", (failed) ? "FAILED" : "PASSED");
+#else
+ printf("%s\n", (failed) ? "FAILED" : "PASSED");
+#endif
+ return (failed) ? 1 : 0;
+} /* main */
+
+/* sockopt.c */
+
diff --git a/pr/tests/sprintf.c b/pr/tests/sprintf.c
new file mode 100644
index 00000000..438c3921
--- /dev/null
+++ b/pr/tests/sprintf.c
@@ -0,0 +1,435 @@
+/* -*- 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: sprintf.c
+ * Description:
+ * This is a test program for the PR_snprintf() functions defined
+ * in prprf.c. This test program is based on ns/nspr/tests/sprintf.c,
+ * revision 1.10.
+ * Modification History:
+ * 20-May-1997 AGarcia replaced printf statment to return PASS\n. This is to be used by the
+ * regress tool parsing routine.
+ ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+ * recognize the return code from tha main program.
+ */
+
+#include "prinit.h"
+#include "prprf.h"
+#include "prlog.h"
+#include "prlong.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define countof(a) (sizeof(a)/sizeof(a[0]))
+
+static char sbuf[20000];
+
+
+/*
+** Perform a three way test against PR_smprintf, PR_snprintf, and sprintf.
+** Make sure the results are identical
+*/
+static void test_i(char *pattern, int i)
+{
+ char *s;
+ char buf[200];
+ int n;
+
+ /* try all three routines */
+ s = PR_smprintf(pattern, i);
+ PR_ASSERT(s != 0);
+ n = PR_snprintf(buf, sizeof(buf), pattern, i);
+ PR_ASSERT(n <= sizeof(buf));
+ sprintf(sbuf, pattern, i);
+
+ /* compare results */
+ if ((strncmp(s, buf, sizeof(buf)) != 0) ||
+ (strncmp(s, sbuf, sizeof(sbuf)) != 0)) {
+ fprintf(stderr,
+ "pattern='%s' i=%d\nPR_smprintf='%s'\nPR_snprintf='%s'\n sprintf='%s'\n",
+ pattern, i, s, buf, sbuf);
+ PR_smprintf_free(s);
+ exit(-1);
+ }
+ PR_smprintf_free(s);
+}
+
+static void TestI(void)
+{
+ static int nums[] = {
+ 0, 1, -1, 10, -10,
+ 32767, -32768,
+ };
+ static char *signs[] = {
+ "",
+ "0", "-", "+", " ",
+ "0-", "0+", "0 ", "-0", "-+", "- ",
+ "+0", "+-", "+ ", " 0", " -", " +",
+ "0-+", "0- ", "0+-", "0+ ", "0 -", "0 +",
+ "-0+", "-0 ", "-+0", "-+ ", "- 0", "- +",
+ "+0-", "+0 ", "+-0", "+- ", "+ 0", "+ -",
+ " 0-", " 0+", " -0", " -+", " +0", " +-",
+ "0-+ ", "0- +", "0+- ", "0+ -", "0 -+", "0 +-",
+ "-0+ ", "-0 +", "-+0 ", "-+ 0", "- 0+", "- +0",
+ "+0- ", "+0 -", "+-0 ", "+- 0", "+ 0-", "+ -0",
+ " 0-+", " 0+-", " -0+", " -+0", " +0-", " +-0",
+ };
+ static char *precs[] = {
+ "", "3", "5", "43",
+ "7.3", "7.5", "7.11", "7.43",
+ };
+ static char *formats[] = {
+ "d", "o", "x", "u",
+ "hd", "ho", "hx", "hu"
+ };
+ int f, s, n, p;
+ char fmt[20];
+
+ for (f = 0; f < countof(formats); f++) {
+ for (s = 0; s < countof(signs); s++) {
+ for (p = 0; p < countof(precs); p++) {
+ fmt[0] = '%';
+ fmt[1] = 0;
+ if (signs[s]) strcat(fmt, signs[s]);
+ if (precs[p]) strcat(fmt, precs[p]);
+ if (formats[f]) strcat(fmt, formats[f]);
+ for (n = 0; n < countof(nums); n++) {
+ test_i(fmt, nums[n]);
+ }
+ }
+ }
+ }
+}
+
+/************************************************************************/
+
+/*
+** Perform a three way test against PR_smprintf, PR_snprintf, and sprintf.
+** Make sure the results are identical
+*/
+static void test_l(char *pattern, char *spattern, PRInt32 l)
+{
+ char *s;
+ char buf[200];
+ int n;
+
+ /* try all three routines */
+ s = PR_smprintf(pattern, l);
+ PR_ASSERT(s != 0);
+ n = PR_snprintf(buf, sizeof(buf), pattern, l);
+ PR_ASSERT(n <= sizeof(buf));
+ sprintf(sbuf, spattern, l);
+
+ /* compare results */
+ if ((strncmp(s, buf, sizeof(buf)) != 0) ||
+ (strncmp(s, sbuf, sizeof(sbuf)) != 0)) {
+ fprintf(stderr,
+ "pattern='%s' l=%ld\nPR_smprintf='%s'\nPR_snprintf='%s'\n sprintf='%s'\n",
+ pattern, l, s, buf, sbuf);
+ PR_smprintf_free(s);
+ exit(-1);
+ }
+ PR_smprintf_free(s);
+}
+
+static void TestL(void)
+{
+ static PRInt32 nums[] = {
+ 0,
+ 1,
+ -1,
+ 10,
+ -10,
+ 32767,
+ -32768,
+ PR_INT32(0x7fffffff), /* 2147483647L */
+ -1 - PR_INT32(0x7fffffff) /* -2147483648L */
+ };
+ static char *signs[] = {
+ "",
+ "0", "-", "+", " ",
+ "0-", "0+", "0 ", "-0", "-+", "- ",
+ "+0", "+-", "+ ", " 0", " -", " +",
+ "0-+", "0- ", "0+-", "0+ ", "0 -", "0 +",
+ "-0+", "-0 ", "-+0", "-+ ", "- 0", "- +",
+ "+0-", "+0 ", "+-0", "+- ", "+ 0", "+ -",
+ " 0-", " 0+", " -0", " -+", " +0", " +-",
+ "0-+ ", "0- +", "0+- ", "0+ -", "0 -+", "0 +-",
+ "-0+ ", "-0 +", "-+0 ", "-+ 0", "- 0+", "- +0",
+ "+0- ", "+0 -", "+-0 ", "+- 0", "+ 0-", "+ -0",
+ " 0-+", " 0+-", " -0+", " -+0", " +0-", " +-0",
+ };
+ static char *precs[] = {
+ "", "3", "5", "43",
+ ".3", ".43",
+ "7.3", "7.5", "7.11", "7.43",
+ };
+ static char *formats[] = { "ld", "lo", "lx", "lu" };
+
+#if PR_BYTES_PER_INT == 4
+ static char *sformats[] = { "d", "o", "x", "u" };
+#elif PR_BYTES_PER_LONG == 4
+ static char *sformats[] = { "ld", "lo", "lx", "lu" };
+#else
+#error Neither int nor long is 4 bytes on this platform
+#endif
+
+ int f, s, n, p;
+ char fmt[40], sfmt[40];
+
+ for (f = 0; f < countof(formats); f++) {
+ for (s = 0; s < countof(signs); s++) {
+ for (p = 0; p < countof(precs); p++) {
+ fmt[0] = '%';
+ fmt[1] = 0;
+ if (signs[s]) strcat(fmt, signs[s]);
+ if (precs[p]) strcat(fmt, precs[p]);
+ strcpy(sfmt, fmt);
+ if (formats[f]) strcat(fmt, formats[f]);
+ if (sformats[f]) strcat(sfmt, sformats[f]);
+ for (n = 0; n < countof(nums); n++) {
+ test_l(fmt, sfmt, nums[n]);
+ }
+ }
+ }
+ }
+}
+
+/************************************************************************/
+
+/*
+** Perform a three way test against PR_smprintf, PR_snprintf, and sprintf.
+** Make sure the results are identical
+*/
+static void test_ll(char *pattern, char *spattern, PRInt64 l)
+{
+ char *s;
+ char buf[200];
+ int n;
+
+ /* try all three routines */
+ s = PR_smprintf(pattern, l);
+ PR_ASSERT(s != 0);
+ n = PR_snprintf(buf, sizeof(buf), pattern, l);
+ PR_ASSERT(n <= sizeof(buf));
+#if defined(HAVE_LONG_LONG)
+ sprintf(sbuf, spattern, l);
+
+ /* compare results */
+ if ((strncmp(s, buf, sizeof(buf)) != 0) ||
+ (strncmp(s, sbuf, sizeof(sbuf)) != 0)) {
+#if PR_BYTES_PER_LONG == 8
+#define FORMAT_SPEC "%ld"
+#elif defined(WIN16)
+#define FORMAT_SPEC "%Ld"
+#elif defined(WIN32)
+#define FORMAT_SPEC "%I64d"
+#else
+#define FORMAT_SPEC "%lld"
+#endif
+ fprintf(stderr,
+ "pattern='%s' ll=" FORMAT_SPEC "\nPR_smprintf='%s'\nPR_snprintf='%s'\n sprintf='%s'\n",
+ pattern, l, s, buf, sbuf);
+ printf("FAIL\n");
+ PR_smprintf_free(s);
+ exit(-1);
+ }
+ PR_smprintf_free(s);
+#else
+ /* compare results */
+ if ((strncmp(s, buf, sizeof(buf)) != 0)) {
+ fprintf(stderr,
+ "pattern='%s'\nPR_smprintf='%s'\nPR_snprintf='%s'\n sprintf='%s'\n",
+ pattern, s, buf, sbuf);
+ printf("FAIL\n");
+ PR_smprintf_free(s);
+ exit(-1);
+ }
+ PR_smprintf_free(s);
+#endif
+}
+
+static void TestLL(void)
+{
+ static PRInt64 nums[] = {
+ LL_INIT(0, 0),
+ LL_INIT(0, 1),
+ LL_INIT(0xffffffff, 0xffffffff), /* -1 */
+ LL_INIT(0, 10),
+ LL_INIT(0xffffffff, 0xfffffff6), /* -10 */
+ LL_INIT(0, 32767),
+ LL_INIT(0xffffffff, 0xffff8000), /* -32768 */
+ LL_INIT(0, 0x7fffffff), /* 2147483647 */
+ LL_INIT(0xffffffff, 0x80000000), /* -2147483648 */
+ LL_INIT(0x7fffffff, 0xffffffff), /* 9223372036854775807 */
+ LL_INIT(0x80000000, 0) /* -9223372036854775808 */
+ };
+
+ static char *signs[] = {
+ "",
+ "0", "-", "+", " ",
+ "0-", "0+", "0 ", "-0", "-+", "- ",
+ "+0", "+-", "+ ", " 0", " -", " +",
+ "0-+", "0- ", "0+-", "0+ ", "0 -", "0 +",
+ "-0+", "-0 ", "-+0", "-+ ", "- 0", "- +",
+ "+0-", "+0 ", "+-0", "+- ", "+ 0", "+ -",
+ " 0-", " 0+", " -0", " -+", " +0", " +-",
+ "0-+ ", "0- +", "0+- ", "0+ -", "0 -+", "0 +-",
+ "-0+ ", "-0 +", "-+0 ", "-+ 0", "- 0+", "- +0",
+ "+0- ", "+0 -", "+-0 ", "+- 0", "+ 0-", "+ -0",
+ " 0-+", " 0+-", " -0+", " -+0", " +0-", " +-0",
+ };
+ static char *precs[] = {
+ "", "3", "5", "43",
+ ".3", ".43",
+ "7.3", "7.5", "7.11", "7.43",
+ };
+ static char *formats[] = { "lld", "llo", "llx", "llu" };
+
+#if PR_BYTES_PER_LONG == 8
+ static char *sformats[] = { "ld", "lo", "lx", "lu" };
+#elif defined(WIN16)
+ /* Watcom uses the format string "%Ld" instead of "%lld". */
+ static char *sformats[] = { "Ld", "Lo", "Lx", "Lu" };
+#elif defined(WIN32)
+ static char *sformats[] = { "I64d", "I64o", "I64x", "I64u" };
+#else
+ static char *sformats[] = { "lld", "llo", "llx", "llu" };
+#endif
+
+ int f, s, n, p;
+ char fmt[40], sfmt[40];
+
+ for (f = 0; f < countof(formats); f++) {
+ for (s = 0; s < countof(signs); s++) {
+ for (p = 0; p < countof(precs); p++) {
+ fmt[0] = '%';
+ fmt[1] = 0;
+ if (signs[s]) strcat(fmt, signs[s]);
+ if (precs[p]) strcat(fmt, precs[p]);
+ strcpy(sfmt, fmt);
+ if (formats[f]) strcat(fmt, formats[f]);
+ if (sformats[f]) strcat(sfmt, sformats[f]);
+ for (n = 0; n < countof(nums); n++) {
+ test_ll(fmt, sfmt, nums[n]);
+ }
+ }
+ }
+ }
+}
+
+/************************************************************************/
+
+/*
+** Perform a three way test against PR_smprintf, PR_snprintf, and sprintf.
+** Make sure the results are identical
+*/
+static void test_s(char *pattern, char *ss)
+{
+ char *s;
+ unsigned char before[8];
+ char buf[200];
+ unsigned char after[8];
+ int n;
+
+ memset(before, 0xBB, 8);
+ memset(after, 0xAA, 8);
+
+ /* try all three routines */
+ s = PR_smprintf(pattern, ss);
+ PR_ASSERT(s != 0);
+ n = PR_snprintf(buf, sizeof(buf), pattern, ss);
+ PR_ASSERT(n <= sizeof(buf));
+ sprintf(sbuf, pattern, ss);
+
+ for (n = 0; n < 8; n++) {
+ PR_ASSERT(before[n] == 0xBB);
+ PR_ASSERT(after[n] == 0xAA);
+ }
+
+ /* compare results */
+ if ((strncmp(s, buf, sizeof(buf)) != 0) ||
+ (strncmp(s, sbuf, sizeof(sbuf)) != 0)) {
+ fprintf(stderr,
+ "pattern='%s' ss=%.20s\nPR_smprintf='%s'\nPR_snprintf='%s'\n sprintf='%s'\n",
+ pattern, ss, s, buf, sbuf);
+ printf("FAIL\n");
+ PR_smprintf_free(s);
+ exit(-1);
+ }
+ PR_smprintf_free(s);
+}
+
+static void TestS(void)
+{
+ static char *strs[] = {
+ "",
+ "a",
+ "abc",
+ "abcde",
+ "abcdefABCDEF",
+ "abcdefghijklmnopqrstuvwxyz0123456789!@#$"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$"
+ "abcdefghijklmnopqrstuvwxyz0123456789!@#$",
+ };
+ /* '0' is not relevant to printing strings */
+ static char *signs[] = {
+ "",
+ "-", "+", " ",
+ "-+", "- ", "+-", "+ ", " -", " +",
+ "-+ ", "- +", "+- ", "+ -", " -+", " +-",
+ };
+ static char *precs[] = {
+ "", "3", "5", "43",
+ ".3", ".43",
+ "7.3", "7.5", "7.11", "7.43",
+ };
+ static char *formats[] = { "s" };
+ int f, s, n, p;
+ char fmt[40];
+
+ for (f = 0; f < countof(formats); f++) {
+ for (s = 0; s < countof(signs); s++) {
+ for (p = 0; p < countof(precs); p++) {
+ fmt[0] = '%';
+ fmt[1] = 0;
+ if (signs[s]) strcat(fmt+strlen(fmt), signs[s]);
+ if (precs[p]) strcat(fmt+strlen(fmt), precs[p]);
+ if (formats[f]) strcat(fmt+strlen(fmt), formats[f]);
+ for (n = 0; n < countof(strs); n++) {
+ test_s(fmt, strs[n]);
+ }
+ }
+ }
+ }
+}
+
+/************************************************************************/
+
+int main(int argc, char **argv)
+{
+ PR_STDIO_INIT();
+ TestI();
+ TestL();
+ TestLL();
+ TestS();
+ printf("PASS\n");
+ return 0;
+}
diff --git a/pr/tests/sproc_ch.c b/pr/tests/sproc_ch.c
new file mode 100644
index 00000000..886d2ba2
--- /dev/null
+++ b/pr/tests/sproc_ch.c
@@ -0,0 +1,100 @@
+/* -*- 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.
+ */
+
+/*
+ * Test sproc_ch.c
+ *
+ * The purpose of this test and the sproc_p.c test is to test the shutdown
+ * of all the IRIX sprocs in a program when one of them dies due to an error.
+ *
+ * There are three sprocs in this test: the parent, the child, and the
+ * grandchild. The parent and child sprocs never stop on their own.
+ * The grandchild sproc gets a segmentation fault and dies. You should
+ * You should use "ps" to see if the parent and child sprocs are killed
+ * after the grandchild dies.
+ */
+
+#include "prinit.h"
+#include <stdio.h>
+
+#if !defined(IRIX)
+
+int main()
+{
+ printf("This test applies to IRIX only.\n");
+ return 0;
+}
+
+#else /* IRIX */
+
+#include "prthread.h"
+#include <sys/types.h>
+#include <unistd.h>
+
+void SegFault(void *unused)
+{
+ int *p = 0;
+
+ printf("The grandchild sproc has pid %d.\n", getpid());
+ printf("The grandchild sproc will get a segmentation fault and die.\n");
+ printf("The parent and child sprocs should be killed after the "
+ "grandchild sproc dies.\n");
+ printf("Use 'ps' to make sure this is so.\n");
+ fflush(stdout);
+ /* Force a segmentation fault */
+ *p = 0;
+}
+
+void NeverStops(void *unused)
+{
+ int i = 0;
+
+ printf("The child sproc has pid %d.\n", getpid());
+ printf("The child sproc won't stop on its own.\n");
+ fflush(stdout);
+
+ /* create the grandchild sproc */
+ PR_CreateThread(PR_USER_THREAD, SegFault, NULL,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0);
+
+ while (1) {
+ i++;
+ }
+}
+
+int main()
+{
+ int i= 0;
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+
+ printf("The parent sproc has pid %d.\n", getpid());
+ printf("The parent sproc won't stop on its own.\n");
+ fflush(stdout);
+
+ /* create the child sproc */
+ PR_CreateThread(PR_USER_THREAD, NeverStops, NULL,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0);
+
+ while (1) {
+ i++;
+ }
+ return 0;
+}
+
+#endif /* IRIX */
diff --git a/pr/tests/sproc_p.c b/pr/tests/sproc_p.c
new file mode 100644
index 00000000..3145816d
--- /dev/null
+++ b/pr/tests/sproc_p.c
@@ -0,0 +1,82 @@
+/* -*- 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.
+ */
+
+/*
+ * Test sproc_p.c
+ *
+ * The purpose of this test and the sproc_ch.c test is to test the shutdown
+ * of all the IRIX sprocs in a program when one of them dies due to an error.
+ *
+ * In this test, the parent sproc gets a segmentation fault and dies.
+ * The child sproc never stops on its own. You should use "ps" to see if
+ * the child sproc is killed after the parent dies.
+ */
+
+#include "prinit.h"
+#include <stdio.h>
+
+#if !defined(IRIX)
+
+int main()
+{
+ printf("This test applies to IRIX only.\n");
+ return 0;
+}
+
+#else /* IRIX */
+
+#include "prthread.h"
+#include <sys/types.h>
+#include <unistd.h>
+
+void NeverStops(void *unused)
+{
+ int i = 0;
+
+ printf("The child sproc has pid %d.\n", getpid());
+ printf("The child sproc won't stop on its own.\n");
+ fflush(stdout);
+
+ /* I never stop */
+ while (1) {
+ i++;
+ }
+}
+
+int main()
+{
+ int *p = 0;
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+
+ printf("The parent sproc has pid %d.\n", getpid());
+ printf("The parent sproc will first create a child sproc.\n");
+ printf("Then the parent sproc will get a segmentation fault and die.\n");
+ printf("The child sproc should be killed after the parent sproc dies.\n");
+ printf("Use 'ps' to make sure this is so.\n");
+ fflush(stdout);
+
+ PR_CreateThread(PR_USER_THREAD, NeverStops, NULL,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0);
+
+ /* Force a segmentation fault */
+ *p = 0;
+ return 0;
+}
+
+#endif /* IRIX */
diff --git a/pr/tests/stack.c b/pr/tests/stack.c
new file mode 100644
index 00000000..d48e3ddb
--- /dev/null
+++ b/pr/tests/stack.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.
+ */
+
+
+/*
+ *
+ * Test atomic stack operations
+ *
+ * Two stacks are created and threads add data items (each containing
+ * one of the first n integers) to the first stack, remove data items
+ * from the first stack and add them to the second stack. The primordial
+ * thread compares the sum of the first n integers to the sum of the
+ * integers in the data items in the second stack. The test succeeds if
+ * they are equal.
+ */
+
+#include "nspr.h"
+#include "plgetopt.h"
+
+typedef struct _DataRecord {
+ PRInt32 data;
+ PRStackElem link;
+} DataRecord;
+
+#define RECORD_LINK_PTR(lp) ((DataRecord*) ((char*) (lp) - offsetof(DataRecord,link)))
+
+#define MAX_THREAD_CNT 100
+#define DEFAULT_THREAD_CNT 4
+#define DEFAULT_DATA_CNT 4
+/*
+ * sum of the first n numbers using the formula n*(n+1)/2
+ */
+#define SUM_OF_NUMBERS(n) ((n & 1) ? (((n + 1)/2) * n) : ((n/2) * (n+1)))
+
+typedef struct stack_data {
+ PRStack *list1;
+ PRStack *list2;
+ PRInt32 initial_data_value;
+ PRInt32 data_cnt;
+} stack_data;
+
+static void stackop(void *arg);
+
+static int _debug_on;
+
+PRFileDesc *output;
+PRFileDesc *errhandle;
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PRInt32 rv, cnt, sum;
+ DataRecord *Item;
+ PRStack *list1, *list2;
+ PRStackElem *node;
+ PRStatus rc;
+
+ PRInt32 thread_cnt = DEFAULT_THREAD_CNT;
+ PRInt32 data_cnt = DEFAULT_DATA_CNT;
+ PRThread **threads;
+ stack_data *thread_args;
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dt:c:");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ _debug_on = 1;
+ break;
+ case 't': /* thread count */
+ thread_cnt = atoi(opt->value);
+ break;
+ case 'c': /* data count */
+ data_cnt = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_SetConcurrency(4);
+
+ output = PR_GetSpecialFD(PR_StandardOutput);
+ errhandle = PR_GetSpecialFD(PR_StandardError);
+ list1 = PR_CreateStack("Stack_1");
+ if (list1 == NULL) {
+ PR_fprintf(errhandle, "PR_CreateStack failed - error %d\n",
+ PR_GetError());
+ return 1;
+ }
+
+ list2 = PR_CreateStack("Stack_2");
+ if (list2 == NULL) {
+ PR_fprintf(errhandle, "PR_CreateStack failed - error %d\n",
+ PR_GetError());
+ return 1;
+ }
+
+
+ threads = (PRThread**) PR_CALLOC(sizeof(PRThread*) * thread_cnt);
+ thread_args = (stack_data *) PR_CALLOC(sizeof(stack_data) * thread_cnt);
+
+ if (_debug_on)
+ PR_fprintf(output,"%s: thread_cnt = %d data_cnt = %d\n", argv[0],
+ thread_cnt, data_cnt);
+ for(cnt = 0; cnt < thread_cnt; cnt++) {
+ PRThreadScope scope;
+
+ thread_args[cnt].list1 = list1;
+ thread_args[cnt].list2 = list2;
+ thread_args[cnt].data_cnt = data_cnt;
+ thread_args[cnt].initial_data_value = 1 + cnt * data_cnt;
+
+ if (cnt & 1)
+ scope = PR_GLOBAL_THREAD;
+ else
+ scope = PR_LOCAL_THREAD;
+
+
+ threads[cnt] = PR_CreateThread(PR_USER_THREAD,
+ stackop, &thread_args[cnt],
+ PR_PRIORITY_NORMAL,
+ scope,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (threads[cnt] == NULL) {
+ PR_fprintf(errhandle, "PR_CreateThread failed - error %d\n",
+ PR_GetError());
+ PR_ProcessExit(2);
+ }
+ if (_debug_on)
+ PR_fprintf(output,"%s: created thread = 0x%x\n", argv[0],
+ threads[cnt]);
+ }
+
+ for(cnt = 0; cnt < thread_cnt; cnt++) {
+ rc = PR_JoinThread(threads[cnt]);
+ PR_ASSERT(rc == PR_SUCCESS);
+ }
+
+ node = PR_StackPop(list1);
+ /*
+ * list1 should be empty
+ */
+ if (node != NULL) {
+ PR_fprintf(errhandle, "Error - Stack 1 not empty\n");
+ PR_ASSERT(node == NULL);
+ PR_ProcessExit(4);
+ }
+
+ cnt = data_cnt * thread_cnt;
+ sum = 0;
+ while (cnt-- > 0) {
+ node = PR_StackPop(list2);
+ /*
+ * There should be at least 'cnt' number of records
+ */
+ if (node == NULL) {
+ PR_fprintf(errhandle, "Error - PR_StackPop returned NULL\n");
+ PR_ProcessExit(3);
+ }
+ Item = RECORD_LINK_PTR(node);
+ sum += Item->data;
+ }
+ node = PR_StackPop(list2);
+ /*
+ * there should be exactly 'cnt' number of records
+ */
+ if (node != NULL) {
+ PR_fprintf(errhandle, "Error - Stack 2 not empty\n");
+ PR_ASSERT(node == NULL);
+ PR_ProcessExit(4);
+ }
+ PR_DELETE(threads);
+ PR_DELETE(thread_args);
+
+ PR_DestroyStack(list1);
+ PR_DestroyStack(list2);
+
+ if (sum == SUM_OF_NUMBERS(data_cnt * thread_cnt)) {
+ PR_fprintf(output, "%s successful\n", argv[0]);
+ PR_fprintf(output, "\t\tsum = 0x%x, expected = 0x%x\n", sum,
+ SUM_OF_NUMBERS(thread_cnt * data_cnt));
+ return 0;
+ } else {
+ PR_fprintf(output, "%s failed: sum = 0x%x, expected = 0x%x\n",
+ argv[0], sum,
+ SUM_OF_NUMBERS(data_cnt * thread_cnt));
+ return 2;
+ }
+}
+
+static void stackop(void *thread_arg)
+{
+ PRInt32 val, cnt, index;
+ DataRecord *Items, *Item;
+ PRStack *list1, *list2;
+ PRStackElem *node;
+ stack_data *arg = (stack_data *) thread_arg;
+
+ val = arg->initial_data_value;
+ cnt = arg->data_cnt;
+ list1 = arg->list1;
+ list2 = arg->list2;
+
+ /*
+ * allocate memory for the data records
+ */
+ Items = (DataRecord *) PR_CALLOC(sizeof(DataRecord) * cnt);
+ PR_ASSERT(Items != NULL);
+ index = 0;
+
+ if (_debug_on)
+ PR_fprintf(output,
+ "Thread[0x%x] init_val = %d cnt = %d data1 = 0x%x datan = 0x%x\n",
+ PR_GetCurrentThread(), val, cnt, &Items[0], &Items[cnt-1]);
+
+
+ /*
+ * add the data records to list1
+ */
+ while (cnt-- > 0) {
+ Items[index].data = val++;
+ PR_StackPush(list1, &Items[index].link);
+ index++;
+ }
+
+ /*
+ * remove the data records from list1 and add them to list2
+ */
+ cnt = arg->data_cnt;
+ while (cnt-- > 0) {
+ node = PR_StackPop(list1);
+ if (node == NULL) {
+ PR_fprintf(errhandle, "Error - PR_StackPop returned NULL\n");
+ PR_ASSERT(node != NULL);
+ PR_ProcessExit(3);
+ }
+ PR_StackPush(list2, node);
+ }
+ if (_debug_on)
+ PR_fprintf(output,
+ "Thread[0x%x] init_val = %d cnt = %d exiting\n",
+ PR_GetCurrentThread(), val, cnt);
+
+}
+
diff --git a/pr/tests/stat.c b/pr/tests/stat.c
new file mode 100644
index 00000000..e2c2f69e
--- /dev/null
+++ b/pr/tests/stat.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.
+ */
+
+/*
+ * Program to test different ways to get file info; right now it
+ * only works for solaris.
+ *
+ */
+#include "nspr.h"
+#include "prpriv.h"
+#include "prinrval.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define DEFAULT_COUNT 100000
+PRInt32 count;
+
+static void statPRStat(void)
+{
+ PRFileInfo finfo;
+ PRInt32 index = count;
+
+ for (;index--;) {
+ PR_GetFileInfo("/etc/passwd", &finfo);
+ }
+}
+
+static void statStat(void)
+{
+ struct stat finfo;
+ PRInt32 index = count;
+
+ for (;index--;) {
+ stat("/etc/passwd", &finfo);
+ }
+}
+
+/************************************************************************/
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+ PRInt32 tot;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+ tot = PR_IntervalToMilliseconds(stop-start);
+
+ printf("%40s: %6.2f usec avg, %d msec total\n", msg, d / count, tot);
+}
+
+void main(int argc, char **argv)
+{
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ if (argc > 1) {
+ count = atoi(argv[1]);
+ } else {
+ count = DEFAULT_COUNT;
+ }
+
+ Measure(statPRStat, "time to call PR_GetFileInfo()");
+ Measure(statStat, "time to call stat()");
+
+ PR_Cleanup();
+}
diff --git a/pr/tests/stdio.c b/pr/tests/stdio.c
new file mode 100644
index 00000000..6e4d76d0
--- /dev/null
+++ b/pr/tests/stdio.c
@@ -0,0 +1,64 @@
+/* -*- 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: stdio.c
+ * Description: testing the "special" fds
+ * Modification History:
+ * 20-May-1997 AGarcia - Replace Test succeeded status with PASS. This is used by the
+ * regress tool parsing code.
+ ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
+** recognize the return code from tha main program.
+ */
+
+
+#include "prlog.h"
+#include "prinit.h"
+#include "prio.h"
+
+#include <stdio.h>
+#include <string.h>
+
+static PRIntn PR_CALLBACK stdio(PRIntn argc, char **argv)
+{
+ PRInt32 rv;
+
+ PRFileDesc *out = PR_GetSpecialFD(PR_StandardOutput);
+ PRFileDesc *err = PR_GetSpecialFD(PR_StandardError);
+
+ rv = PR_Write(
+ out, "This to standard out\n",
+ strlen("This to standard out\n"));
+ PR_ASSERT((PRInt32)strlen("This to standard out\n") == rv);
+ rv = PR_Write(
+ err, "This to standard err\n",
+ strlen("This to standard err\n"));
+ PR_ASSERT((PRInt32)strlen("This to standard err\n") == rv);
+
+ return 0;
+
+} /* stdio */
+
+int main(int argc, char **argv)
+{
+ PR_STDIO_INIT();
+ return PR_Initialize(stdio, argc, argv, 0);
+} /* main */
+
+
+/* stdio.c */
diff --git a/pr/tests/strod.c b/pr/tests/strod.c
new file mode 100644
index 00000000..673d5d29
--- /dev/null
+++ b/pr/tests/strod.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 "prinit.h"
+#include "prio.h"
+#include "prprf.h"
+#include "prdtoa.h"
+#include "plgetopt.h"
+
+#include <stdlib.h>
+
+static void Help(void)
+{
+ PRFileDesc *err = PR_GetSpecialFD(PR_StandardError);
+ PR_fprintf(err, "Usage: /.strod [-c n] [-l n] [-h]\n");
+ PR_fprintf(err, "\t-n n Number to translate (default: 1234567890123456789)\n");
+ PR_fprintf(err, "\t-l n Times to loop the test (default: 1)\n");
+ PR_fprintf(err, "\t-h This message and nothing else\n");
+} /* Help */
+
+static PRIntn PR_CALLBACK RealMain(PRIntn argc, char **argv)
+{
+ PLOptStatus os;
+ PRIntn loops = 1;
+ PRFloat64 answer;
+ const char *number = "1234567890123456789";
+ PRFileDesc *err = PR_GetSpecialFD(PR_StandardError);
+ PLOptState *opt = PL_CreateOptState(argc, argv, "hc:l:");
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'n': /* number to translate */
+ number = opt->value;
+ break;
+ case 'l': /* number of times to run the tests */
+ loops = atoi(opt->value);
+ break;
+ case 'h': /* user wants some guidance */
+ Help(); /* so give him an earful */
+ return 2; /* but not a lot else */
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_fprintf(err, "Settings\n");
+ PR_fprintf(err, "\tNumber to translate %s\n", number);
+ PR_fprintf(err, "\tLoops to run test: %d\n", loops);
+
+ while (loops--)
+ {
+ answer = PR_strtod(number, NULL);
+ PR_fprintf(err, "Translation = %20.0f\n", answer);
+ }
+ return 2;
+}
+
+
+
+PRIntn main(PRIntn argc, char *argv[])
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
diff --git a/pr/tests/suspend.c b/pr/tests/suspend.c
new file mode 100644
index 00000000..e44dc97b
--- /dev/null
+++ b/pr/tests/suspend.c
@@ -0,0 +1,201 @@
+/* -*- 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 "nspr.h"
+#include "prpriv.h"
+#include "prinrval.h"
+
+#if defined(XP_MAC)
+#include "gcint.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef XP_MAC
+#include "gcint.h"
+#include "prlog.h"
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+#endif
+
+PRMonitor *mon;
+PRInt32 count;
+PRInt32 alive;
+
+#define SLEEP_TIME 4 /* secs */
+
+void PR_CALLBACK
+Level_2_Thread(void *arg)
+{
+ PR_Sleep(PR_MillisecondsToInterval(4 * 1000));
+ printf("Level_2_Thread[0x%lx] exiting\n",PR_GetCurrentThread());
+ return;
+}
+
+void PR_CALLBACK
+Level_1_Thread(void *arg)
+{
+ PRUint32 tmp = (PRUint32)arg;
+ PRThreadScope scope = (PRThreadScope) tmp;
+ PRThread *thr;
+
+ thr = PR_CreateThreadGCAble(PR_USER_THREAD,
+ Level_2_Thread,
+ NULL,
+ PR_PRIORITY_HIGH,
+ scope,
+ PR_JOINABLE_THREAD,
+ 0);
+
+ if (!thr) {
+ printf("Could not create thread!\n");
+ } else {
+ printf("Level_1_Thread[0x%lx] created %15s thread 0x%lx\n",
+ PR_GetCurrentThread(),
+ (scope == PR_GLOBAL_THREAD) ?
+ "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD",
+ thr);
+ PR_JoinThread(thr);
+ }
+ PR_EnterMonitor(mon);
+ alive--;
+ PR_Notify(mon);
+ PR_ExitMonitor(mon);
+ printf("Thread[0x%lx] exiting\n",PR_GetCurrentThread());
+}
+
+static PRStatus PR_CALLBACK print_thread(PRThread *thread, int i, void *arg)
+{
+ PRInt32 words;
+ PRWord *registers;
+
+ printf(
+ "\nprint_thread[0x%lx]: %-20s - i = %ld\n",thread,
+ (PR_GLOBAL_THREAD == PR_GetThreadScope(thread)) ?
+ "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD", i);
+ registers = PR_GetGCRegisters(thread, 0, (int *)&words);
+ printf("Regsters R0 = 0x%x R1 = 0x%x R2 = 0x%x R3 = 0x%x\n",
+ registers[0],registers[1],registers[2],registers[3]);
+ printf("Stack Pointer = 0x%lx\n", PR_GetSP(thread));
+ return PR_SUCCESS;
+}
+
+static void Level_0_Thread(PRThreadScope scope1, PRThreadScope scope2)
+{
+ PRThread *thr;
+ PRThread *me = PR_GetCurrentThread();
+ int n;
+ PRInt32 words;
+ PRWord *registers;
+
+ alive = 0;
+ mon = PR_NewMonitor();
+
+ alive = count;
+ for (n=0; n<count; n++) {
+ thr = PR_CreateThreadGCAble(PR_USER_THREAD,
+ Level_1_Thread,
+ (void *)scope2,
+ PR_PRIORITY_NORMAL,
+ scope1,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (!thr) {
+ printf("Could not create thread!\n");
+ alive--;
+ }
+ printf("Level_0_Thread[0x%lx] created %15s thread 0x%lx\n",
+ PR_GetCurrentThread(),
+ (scope1 == PR_GLOBAL_THREAD) ?
+ "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD",
+ thr);
+
+ PR_Sleep(0);
+ }
+ PR_SuspendAll();
+ PR_EnumerateThreads(print_thread, NULL);
+ registers = PR_GetGCRegisters(me, 1, (int *)&words);
+ printf("My Registers: R0 = 0x%x R1 = 0x%x R2 = 0x%x R3 = 0x%x\n",
+ registers[0],registers[1],registers[2],registers[3]);
+ printf("My Stack Pointer = 0x%lx\n", PR_GetSP(me));
+ PR_ResumeAll();
+
+ /* Wait for all threads to exit */
+ PR_EnterMonitor(mon);
+ while (alive) {
+ PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
+ }
+
+ PR_ExitMonitor(mon);
+ PR_DestroyMonitor(mon);
+}
+
+static void CreateThreadsUU(void)
+{
+ Level_0_Thread(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
+}
+
+static void CreateThreadsUK(void)
+{
+ Level_0_Thread(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
+}
+
+static void CreateThreadsKU(void)
+{
+ Level_0_Thread(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
+}
+
+static void CreateThreadsKK(void)
+{
+ Level_0_Thread(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
+}
+
+
+void
+main(int argc, char **argv)
+{
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("suspend.log");
+#endif
+
+ if (argc > 1) {
+ count = atoi(argv[1]);
+ } else {
+ count = 5;
+ }
+
+ printf("\n\n%20s%30s\n\n"," ","Suspend_Resume Test");
+ CreateThreadsUU();
+ CreateThreadsUK();
+ CreateThreadsKU();
+ CreateThreadsKK();
+ PR_SetConcurrency(2);
+
+ printf("\n%20s%30s\n\n"," ","Added 2nd CPU\n");
+
+ CreateThreadsUK();
+ CreateThreadsKK();
+ CreateThreadsUU();
+ CreateThreadsKU();
+ PR_Cleanup();
+}
diff --git a/pr/tests/switch.c b/pr/tests/switch.c
new file mode 100644
index 00000000..878d7d15
--- /dev/null
+++ b/pr/tests/switch.c
@@ -0,0 +1,255 @@
+/* -*- 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: switch.c
+** Description: trying to time context switches
+*/
+
+#include "prinit.h"
+#include "prcvar.h"
+#include "prmem.h"
+#include "prinrval.h"
+#include "prlock.h"
+#include "prlog.h"
+#include "prthread.h"
+#include "prprf.h"
+
+#include "plerror.h"
+#include "plgetopt.h"
+
+#if defined(XP_MAC)
+#include "pprio.h"
+#define printf PR_LogPrint
+#else
+#include "private/pprio.h"
+#endif
+
+#include <stdlib.h>
+
+#define INNER_LOOPS 100
+#define DEFAULT_LOOPS 100
+#define DEFAULT_THREADS 10
+
+static PRFileDesc *debug_out = NULL;
+static PRBool debug_mode = PR_FALSE, verbosity = PR_FALSE, failed = PR_FALSE;
+
+typedef struct Shared
+{
+ PRLock *ml;
+ PRCondVar *cv;
+ PRBool twiddle;
+ PRThread *thread;
+ struct Shared *next;
+} Shared;
+
+static void Help(void)
+{
+ debug_out = PR_STDOUT;
+
+ PR_fprintf(
+ debug_out, "Usage: >./switch [-c n] [-t n] [-d] [-v] [-G] [-C n]\n");
+ PR_fprintf(
+ debug_out, "-c n\tloops at thread level (default: %d)\n", DEFAULT_LOOPS);
+ PR_fprintf(
+ debug_out, "-t n\tnumber of threads (default: %d)\n", DEFAULT_THREADS);
+ PR_fprintf(debug_out, "-d\tturn on debugging output (default: FALSE)\n");
+ PR_fprintf(debug_out, "-v\tturn on verbose output (default: FALSE)\n");
+ PR_fprintf(debug_out, "-G\tglobal threads only (default: FALSE)\n");
+ PR_fprintf(debug_out, "-C n\tconcurrency setting (default: 1)\n");
+} /* Help */
+
+static void PR_CALLBACK Notified(void *arg)
+{
+ Shared *shared = (Shared*)arg;
+ PRStatus status = PR_SUCCESS;
+ while (PR_SUCCESS == status)
+ {
+ PR_Lock(shared->ml);
+ while (shared->twiddle && (PR_SUCCESS == status))
+ status = PR_WaitCondVar(shared->cv, PR_INTERVAL_NO_TIMEOUT);
+ if (verbosity) PR_fprintf(debug_out, "+");
+ shared->twiddle = PR_TRUE;
+ shared->next->twiddle = PR_FALSE;
+ PR_NotifyCondVar(shared->next->cv);
+ PR_Unlock(shared->ml);
+ }
+} /* Notified */
+
+static Shared home;
+PRIntn PR_CALLBACK Switch(PRIntn argc, char **argv)
+{
+ PLOptStatus os;
+ PRStatus status;
+ PRBool help = PR_FALSE;
+ PRUintn concurrency = 1;
+ Shared *shared, *link;
+ PRIntervalTime timein, timeout;
+ PRThreadScope thread_scope = PR_LOCAL_THREAD;
+ PRUintn thread_count, inner_count, loop_count, average;
+ PRUintn thread_limit = DEFAULT_THREADS, loop_limit = DEFAULT_LOOPS;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "hdvc:t:C:G");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'v': /* verbose mode */
+ verbosity = PR_TRUE;
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'c': /* loop counter */
+ loop_limit = atoi(opt->value);
+ break;
+ case 't': /* thread limit */
+ thread_limit = atoi(opt->value);
+ break;
+ case 'C': /* Concurrency limit */
+ concurrency = atoi(opt->value);
+ break;
+ case 'G': /* global threads only */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'h': /* help message */
+ Help();
+ help = PR_TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (help) return -1;
+
+ if (PR_TRUE == debug_mode)
+ {
+ debug_out = PR_STDOUT;
+ PR_fprintf(debug_out, "Test parameters\n");
+ PR_fprintf(debug_out, "\tThreads involved: %d\n", thread_limit);
+ PR_fprintf(debug_out, "\tIteration limit: %d\n", loop_limit);
+ PR_fprintf(debug_out, "\tConcurrency: %d\n", concurrency);
+ PR_fprintf(
+ debug_out, "\tThread type: %s\n",
+ (PR_GLOBAL_THREAD == thread_scope) ? "GLOBAL" : "LOCAL");
+ }
+
+ PR_SetConcurrency(concurrency);
+
+ link = &home;
+ home.ml = PR_NewLock();
+ home.cv = PR_NewCondVar(home.ml);
+ home.twiddle = PR_FALSE;
+ home.next = NULL;
+
+ timeout = 0;
+
+ for (thread_count = 1; thread_count <= thread_limit; ++thread_count)
+ {
+ shared = PR_NEWZAP(Shared);
+
+ shared->ml = home.ml;
+ shared->cv = PR_NewCondVar(home.ml);
+ shared->twiddle = PR_TRUE;
+ shared->next = link;
+ link = shared;
+
+ shared->thread = PR_CreateThread(
+ PR_USER_THREAD, Notified, shared,
+ PR_PRIORITY_HIGH, thread_scope,
+ PR_JOINABLE_THREAD, 0);
+ PR_ASSERT(shared->thread != NULL);
+ if (NULL == shared->thread)
+ failed = PR_TRUE;
+ }
+
+ for (loop_count = 1; loop_count <= loop_limit; ++loop_count)
+ {
+ timein = PR_IntervalNow();
+ for (inner_count = 0; inner_count < INNER_LOOPS; ++inner_count)
+ {
+ PR_Lock(home.ml);
+ home.twiddle = PR_TRUE;
+ shared->twiddle = PR_FALSE;
+ PR_NotifyCondVar(shared->cv);
+ while (home.twiddle)
+ {
+ status = PR_WaitCondVar(home.cv, PR_INTERVAL_NO_TIMEOUT);
+ if (PR_FAILURE == status)
+ failed = PR_TRUE;
+ }
+ PR_Unlock(home.ml);
+ }
+ timeout += (PR_IntervalNow() - timein);
+ }
+
+ if (debug_mode)
+ {
+ average = PR_IntervalToMicroseconds(timeout)
+ / (INNER_LOOPS * loop_limit * thread_count);
+ PR_fprintf(
+ debug_out, "Average switch times %d usecs for %d threads\n",
+ average, thread_limit);
+ }
+
+ link = shared;
+ for (thread_count = 1; thread_count <= thread_limit; ++thread_count)
+ {
+ if (&home == link) break;
+ status = PR_Interrupt(link->thread);
+ if (PR_SUCCESS != status)
+ {
+ failed = PR_TRUE;
+ if (debug_mode)
+ PL_FPrintError(debug_out, "Failed to interrupt");
+ }
+ link = link->next;
+ }
+
+ for (thread_count = 1; thread_count <= thread_limit; ++thread_count)
+ {
+ link = shared->next;
+ status = PR_JoinThread(shared->thread);
+ if (PR_SUCCESS != status)
+ {
+ failed = PR_TRUE;
+ if (debug_mode)
+ PL_FPrintError(debug_out, "Failed to join");
+ }
+ PR_DestroyCondVar(shared->cv);
+ PR_DELETE(shared);
+ if (&home == link) break;
+ shared = link;
+ }
+ PR_DestroyCondVar(home.cv);
+ PR_DestroyLock(home.ml);
+
+ PR_fprintf(PR_STDOUT, ((failed) ? "FAILED\n" : "PASSED\n"));
+ return ((failed) ? 1 : 0);
+} /* Switch */
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PRIntn result;
+ PR_STDIO_INIT();
+ result = PR_Initialize(Switch, argc, argv, 0);
+ return result;
+} /* main */
+
+/* switch.c */
diff --git a/pr/tests/system.c b/pr/tests/system.c
new file mode 100644
index 00000000..909b0802
--- /dev/null
+++ b/pr/tests/system.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 "prio.h"
+#include "prmem.h"
+#include "prprf.h"
+#include "prsystem.h"
+
+#include "plerror.h"
+
+static char *tag[] =
+{
+ "PR_SI_HOSTNAME",
+ "PR_SI_SYSNAME",
+ "PR_SI_RELEASE",
+ "PR_SI_ARCHITECTURE"
+};
+
+static PRSysInfo Incr(PRSysInfo *cmd)
+{
+ PRIntn tmp = (PRIntn)*cmd + 1;
+ *cmd = (PRSysInfo)tmp;
+ return (PRSysInfo)tmp;
+} /* Incr */
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PRStatus rv;
+ PRSysInfo cmd;
+ PRFileDesc *output = PR_GetSpecialFD(PR_StandardOutput);
+
+ char *info = (char*)PR_Calloc(SYS_INFO_BUFFER_LENGTH, 1);
+ for (cmd = PR_SI_HOSTNAME; cmd <= PR_SI_ARCHITECTURE; Incr(&cmd))
+ {
+ rv = PR_GetSystemInfo(cmd, info, SYS_INFO_BUFFER_LENGTH);
+ if (PR_SUCCESS == rv) PR_fprintf(output, "%s: %s\n", tag[cmd], info);
+ else PL_FPrintError(output, tag[cmd]);
+ }
+ PR_DELETE(info);
+
+ PR_fprintf(output, "Host page size is %d\n", PR_GetPageSize());
+ PR_fprintf(output, "Page shift is %d\n", PR_GetPageShift());
+
+ return 0;
+} /* main */
+
+/* system.c */
diff --git a/pr/tests/testbit.c b/pr/tests/testbit.c
new file mode 100644
index 00000000..fb36c44a
--- /dev/null
+++ b/pr/tests/testbit.c
@@ -0,0 +1,110 @@
+/* -*- 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: lazyinit.c
+** Description: Test the functions and macros declared in prbit.h
+**
+*/
+
+#include "nspr.h"
+
+#define ErrorReport(x) { printf((x)); failed = 1; }
+
+prbitmap_t myMap[512/32] = { 0 };
+
+PRInt32 rc;
+PRInt32 i;
+PRIntn failed = 0;
+
+PRIntn main(PRIntn argc, char **argv )
+{
+ /*
+ ** Test bitmap things.
+ */
+ if ( PR_TEST_BIT( myMap, 0 ))
+ ErrorReport("Test 0.0: Failed\n");
+
+ if ( PR_TEST_BIT( myMap, 31 ))
+ ErrorReport("Test 0.1: Failed\n");
+
+ if ( PR_TEST_BIT( myMap, 128 ))
+ ErrorReport("Test 0.2: Failed\n");
+
+ if ( PR_TEST_BIT( myMap, 129 ))
+ ErrorReport("Test 0.3: Failed\n");
+
+
+ PR_SET_BIT( myMap, 0 );
+ if ( !PR_TEST_BIT( myMap, 0 ))
+ ErrorReport("Test 1.0: Failed\n");
+
+ PR_CLEAR_BIT( myMap, 0 );
+ if ( PR_TEST_BIT( myMap, 0 ))
+ ErrorReport("Test 1.0.1: Failed\n");
+
+ PR_SET_BIT( myMap, 31 );
+ if ( !PR_TEST_BIT( myMap, 31 ))
+ ErrorReport("Test 1.1: Failed\n");
+
+ PR_CLEAR_BIT( myMap, 31 );
+ if ( PR_TEST_BIT( myMap, 31 ))
+ ErrorReport("Test 1.1.1: Failed\n");
+
+ PR_SET_BIT( myMap, 128 );
+ if ( !PR_TEST_BIT( myMap, 128 ))
+ ErrorReport("Test 1.2: Failed\n");
+
+ PR_CLEAR_BIT( myMap, 128 );
+ if ( PR_TEST_BIT( myMap, 128 ))
+ ErrorReport("Test 1.2.1: Failed\n");
+
+ PR_SET_BIT( myMap, 129 );
+ if ( !PR_TEST_BIT( myMap, 129 ))
+ ErrorReport("Test 1.3: Failed\n");
+
+ PR_CLEAR_BIT( myMap, 129 );
+ if ( PR_TEST_BIT( myMap, 129 ))
+ ErrorReport("Test 1.3.1: Failed\n");
+
+
+ /*
+ ** Test Ceiling and Floor functions and macros
+ */
+ if ((rc = PR_CeilingLog2(32)) != 5 )
+ ErrorReport("Test 10.0: Failed\n");
+
+ if ((rc = PR_FloorLog2(32)) != 5 )
+ ErrorReport("Test 10.1: Failed\n");
+
+
+ /*
+ ** Evaluate results and exit
+ */
+ if (failed)
+ {
+ printf("FAILED\n");
+ return(1);
+ }
+ else
+ {
+ printf("PASSED\n");
+ return(0);
+ }
+} /* end main() */
+/* end testbit.c */
diff --git a/pr/tests/testfile.c b/pr/tests/testfile.c
new file mode 100644
index 00000000..2f5129d4
--- /dev/null
+++ b/pr/tests/testfile.c
@@ -0,0 +1,799 @@
+/* -*- 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 "nspr.h"
+#include "prpriv.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+static int _debug_on = 0;
+
+#ifdef XP_MAC
+#include "prlog.h"
+#include "primpl.h"
+#define printf PR_LogPrint
+#define setbuf(x,y)
+extern void SetupMacPrintfLog(char *logFile);
+#endif
+
+#ifdef XP_PC
+#define mode_t int
+#endif
+
+#define DPRINTF(arg) if (_debug_on) printf arg
+
+PRLock *lock;
+PRMonitor *mon;
+PRInt32 count;
+int thread_count;
+
+#ifdef WIN16
+#define BUF_DATA_SIZE 256 * 120
+#else
+#define BUF_DATA_SIZE 256 * 1024
+#endif
+
+#define NUM_RDWR_THREADS 10
+#define CHUNK_SIZE 512
+
+typedef struct buffer {
+ char data[BUF_DATA_SIZE];
+} buffer;
+
+typedef struct File_Rdwr_Param {
+ char *pathname;
+ char *buf;
+ int offset;
+ int len;
+} File_Rdwr_Param;
+
+#ifdef XP_PC
+char *TEST_DIR = "C:\\temp\\prdir";
+char *FILE_NAME = "pr_testfile";
+char *HIDDEN_FILE_NAME = "hidden_pr_testfile";
+#else
+char *TEST_DIR = "/tmp/testfile_dir";
+char *FILE_NAME = "pr_testfile";
+char *HIDDEN_FILE_NAME = ".hidden_pr_testfile";
+#endif
+buffer *in_buf, *out_buf;
+char pathname[256], renamename[256];
+
+static void PR_CALLBACK File_Write(void *arg)
+{
+PRFileDesc *fd_file;
+File_Rdwr_Param *fp = (File_Rdwr_Param *) arg;
+char *name, *buf;
+int offset, len;
+
+ setbuf(stdout, NULL);
+ name = fp->pathname;
+ buf = fp->buf;
+ offset = fp->offset;
+ len = fp->len;
+
+ fd_file = PR_Open(name, PR_RDWR | PR_CREATE_FILE, 0777);
+ if (fd_file == NULL) {
+ printf("testfile failed to create/open file %s\n",name);
+ return;
+ }
+ if (PR_Seek(fd_file, offset, PR_SEEK_SET) < 0) {
+ printf("testfile failed to seek in file %s\n",name);
+ return;
+ }
+ if ((PR_Write(fd_file, buf, len)) < 0) {
+ printf("testfile failed to write to file %s\n",name);
+ return;
+ }
+ DPRINTF(("Write out_buf[0] = 0x%x\n",(*((int *) buf))));
+ PR_Close(fd_file);
+ PR_DELETE(fp);
+
+ PR_EnterMonitor(mon);
+ --thread_count;
+ PR_Notify(mon);
+ PR_ExitMonitor(mon);
+}
+
+static void PR_CALLBACK File_Read(void *arg)
+{
+PRFileDesc *fd_file;
+File_Rdwr_Param *fp = (File_Rdwr_Param *) arg;
+char *name, *buf;
+int offset, len;
+
+ setbuf(stdout, NULL);
+ name = fp->pathname;
+ buf = fp->buf;
+ offset = fp->offset;
+ len = fp->len;
+
+ fd_file = PR_Open(name, PR_RDONLY, 0);
+ if (fd_file == NULL) {
+ printf("testfile failed to open file %s\n",name);
+ return;
+ }
+ if (PR_Seek(fd_file, offset, PR_SEEK_SET) < 0) {
+ printf("testfile failed to seek in file %s\n",name);
+ return;
+ }
+ if ((PR_Read(fd_file, buf, len)) < 0) {
+ printf("testfile failed to read to file %s\n",name);
+ return;
+ }
+ DPRINTF(("Read in_buf[0] = 0x%x\n",(*((int *) buf))));
+ PR_Close(fd_file);
+ PR_DELETE(fp);
+
+ PR_EnterMonitor(mon);
+ --thread_count;
+ PR_Notify(mon);
+ PR_ExitMonitor(mon);
+}
+
+
+static void Misc_File_Tests(char *pathname)
+{
+PRFileDesc *fd_file;
+int len;
+PRFileInfo file_info, file_info1;
+char tmpname[1024];
+
+ setbuf(stdout, NULL);
+ /*
+ * Test PR_Available, PR_Seek, PR_GetFileInfo, PR_Rename, PR_Access
+ */
+
+ fd_file = PR_Open(pathname, PR_RDWR | PR_CREATE_FILE, 0777);
+
+ if (fd_file == NULL) {
+ printf("testfile failed to create/open file %s\n",pathname);
+ return;
+ }
+ if (PR_GetOpenFileInfo(fd_file, &file_info) < 0) {
+ printf("testfile PR_GetFileInfo failed on file %s\n",pathname);
+ goto cleanup;
+ }
+ if (PR_Access(pathname, PR_ACCESS_EXISTS) != 0) {
+ printf("testfile PR_Access failed on file %s\n",pathname);
+ goto cleanup;
+ }
+ if (PR_Access(pathname, PR_ACCESS_WRITE_OK) != 0) {
+ printf("testfile PR_Access failed on file %s\n",pathname);
+ goto cleanup;
+ }
+ if (PR_Access(pathname, PR_ACCESS_READ_OK) != 0) {
+ printf("testfile PR_Access failed on file %s\n",pathname);
+ goto cleanup;
+ }
+
+
+ if (PR_GetFileInfo(pathname, &file_info) < 0) {
+ printf("testfile PR_GetFileInfo failed on file %s\n",pathname);
+ return;
+ }
+ if (file_info.type != PR_FILE_FILE) {
+ printf(
+ "testfile PR_GetFileInfo returned incorrect type for file %s\n",
+ pathname);
+ goto cleanup;
+ }
+ if (file_info.size != 0) {
+ printf(
+ "testfile PR_GetFileInfo returned incorrect size (%d should be 0) for file %s\n",
+ file_info.size, pathname);
+ goto cleanup;
+ }
+ file_info1 = file_info;
+
+ len = PR_Available(fd_file);
+ if (len < 0) {
+ printf("testfile PR_Available failed on file %s\n",pathname);
+ goto cleanup;
+ } else if (len != 0) {
+ printf(
+ "testfile PR_Available failed: expected/returned = %d/%d bytes\n",
+ 0, len);
+ goto cleanup;
+ }
+ len = PR_Write(fd_file, out_buf->data, CHUNK_SIZE);
+ if (len < 0) {
+ printf("testfile failed to write to file %s\n",pathname);
+ goto cleanup;
+ }
+ if (PR_GetOpenFileInfo(fd_file, &file_info) < 0) {
+ printf("testfile PR_GetFileInfo failed on file %s\n",pathname);
+ goto cleanup;
+ }
+ if (file_info.size != CHUNK_SIZE) {
+ printf(
+ "testfile PR_GetFileInfo returned incorrect size (%d should be %d) for file %s\n",
+ file_info.size, CHUNK_SIZE, pathname);
+ goto cleanup;
+ }
+ if (LL_NE(file_info.creationTime , file_info1.creationTime)) {
+ printf(
+ "testfile PR_GetFileInfo returned incorrect creation time: %s\n",
+ pathname);
+ goto cleanup;
+ }
+ if (LL_CMP(file_info.modifyTime, > , file_info1.modifyTime)) {
+ printf(
+ "testfile PR_GetFileInfo returned incorrect modify time: %s\n",
+ pathname);
+ goto cleanup;
+ }
+
+ len = PR_Available(fd_file);
+ if (len < 0) {
+ printf("testfile PR_Available failed on file %s\n",pathname);
+ goto cleanup;
+ } else if (len != 0) {
+ printf(
+ "testfile PR_Available failed: expected/returned = %d/%d bytes\n",
+ 0, len);
+ goto cleanup;
+ }
+
+ PR_Seek(fd_file, 0, PR_SEEK_SET);
+ len = PR_Available(fd_file);
+ if (len < 0) {
+ printf("testfile PR_Available failed on file %s\n",pathname);
+ return;
+ } else if (len != CHUNK_SIZE) {
+ printf(
+ "testfile PR_Available failed: expected/returned = %d/%d bytes\n",
+ CHUNK_SIZE, len);
+ goto cleanup;
+ }
+ PR_Close(fd_file);
+
+ strcpy(tmpname,pathname);
+ strcat(tmpname,".RENAMED");
+ if (PR_FAILURE == PR_Rename(pathname, tmpname)) {
+ printf("testfile failed to rename file %s\n",pathname);
+ goto cleanup;
+ }
+
+ fd_file = PR_Open(pathname, PR_RDWR | PR_CREATE_FILE, 0777);
+ len = PR_Write(fd_file, out_buf->data, CHUNK_SIZE);
+ PR_Close(fd_file);
+ if (PR_SUCCESS == PR_Rename(pathname, tmpname)) {
+ printf("testfile renamed to existing file %s\n",pathname);
+ }
+
+ if ((PR_Delete(tmpname)) < 0)
+ printf("testfile failed to unlink file %s\n",tmpname);
+
+cleanup:
+ if ((PR_Delete(pathname)) < 0)
+ printf("testfile failed to unlink file %s\n",pathname);
+}
+
+
+static PRInt32 PR_CALLBACK FileTest(void)
+{
+PRDir *fd_dir;
+int i, offset, len;
+PRThread *t;
+PRThreadScope scope;
+File_Rdwr_Param *fparamp;
+
+ /*
+ * Create Test dir
+ */
+ if ((PR_MkDir(TEST_DIR, 0777)) < 0) {
+ printf("testfile failed to create dir %s\n",TEST_DIR);
+ return -1;
+ }
+ fd_dir = PR_OpenDir(TEST_DIR);
+ if (fd_dir == NULL) {
+ printf("testfile failed to open dir %s\n",TEST_DIR);
+ return -1;
+ }
+
+ PR_CloseDir(fd_dir);
+
+ strcat(pathname, TEST_DIR);
+ strcat(pathname, "/");
+ strcat(pathname, FILE_NAME);
+
+ in_buf = PR_NEW(buffer);
+ if (in_buf == NULL) {
+ printf(
+ "testfile failed to alloc buffer struct\n");
+ return -1;
+ }
+ out_buf = PR_NEW(buffer);
+ if (out_buf == NULL) {
+ printf(
+ "testfile failed to alloc buffer struct\n");
+ return -1;
+ }
+
+ /*
+ * Start a bunch of writer threads
+ */
+ offset = 0;
+ len = CHUNK_SIZE;
+ PR_EnterMonitor(mon);
+ for (i = 0; i < NUM_RDWR_THREADS; i++) {
+ fparamp = PR_NEW(File_Rdwr_Param);
+ if (fparamp == NULL) {
+ printf(
+ "testfile failed to alloc File_Rdwr_Param struct\n");
+ return -1;
+ }
+ fparamp->pathname = pathname;
+ fparamp->buf = out_buf->data + offset;
+ fparamp->offset = offset;
+ fparamp->len = len;
+ memset(fparamp->buf, i, len);
+ /*
+ * Create LOCAL and GLOBAL Threads, alternately
+ */
+ if (i % 1)
+ scope = PR_GLOBAL_THREAD;
+ else
+ scope = PR_LOCAL_THREAD;
+
+ t = PR_CreateThread(PR_USER_THREAD,
+ File_Write, (void *)fparamp,
+ PR_PRIORITY_NORMAL,
+ scope,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ offset += len;
+ }
+ thread_count = i;
+ /* Wait for writer threads to exit */
+ while (thread_count) {
+ PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_ExitMonitor(mon);
+
+
+ /*
+ * Start a bunch of reader threads
+ */
+ offset = 0;
+ len = CHUNK_SIZE;
+ PR_EnterMonitor(mon);
+ for (i = 0; i < NUM_RDWR_THREADS; i++) {
+ fparamp = PR_NEW(File_Rdwr_Param);
+ if (fparamp == NULL) {
+ printf(
+ "testfile failed to alloc File_Rdwr_Param struct\n");
+ return -1;
+ }
+ fparamp->pathname = pathname;
+ fparamp->buf = in_buf->data + offset;
+ fparamp->offset = offset;
+ fparamp->len = len;
+ /*
+ * Create LOCAL and GLOBAL Threads, alternately
+ */
+ if (i % 1)
+ scope = PR_LOCAL_THREAD;
+ else
+ scope = PR_GLOBAL_THREAD;
+
+ t = PR_CreateThread(PR_USER_THREAD,
+ File_Read, (void *)fparamp,
+ PR_PRIORITY_NORMAL,
+ scope,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ offset += len;
+ if ((offset + len) > BUF_DATA_SIZE)
+ break;
+ }
+ thread_count = i;
+
+ /* Wait for reader threads to exit */
+ while (thread_count) {
+ PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_ExitMonitor(mon);
+
+ if (memcmp(in_buf->data, out_buf->data, offset) != 0) {
+ printf("File Test failed: file data corrupted\n");
+ }
+
+ if ((PR_Delete(pathname)) < 0) {
+ printf("testfile failed to unlink file %s\n",pathname);
+ return -1;
+ }
+
+ /*
+ * Test PR_Available, PR_Seek, PR_GetFileInfo, PR_Rename, PR_Access
+ */
+ Misc_File_Tests(pathname);
+
+ if ((PR_RmDir(TEST_DIR)) < 0) {
+ printf("testfile failed to rmdir %s\n", TEST_DIR);
+ return -1;
+ }
+ return 0;
+}
+
+static PRInt32 PR_CALLBACK DirTest(void)
+{
+PRFileDesc *fd_file;
+PRDir *fd_dir;
+int i;
+int path_len;
+PRDirEntry *dirEntry;
+PRFileInfo info;
+PRInt32 num_files = 0;
+#if defined(XP_PC) && defined(WIN32)
+HANDLE hfile;
+#endif
+
+#define FILES_IN_DIR 20
+
+ /*
+ * Create Test dir
+ */
+ DPRINTF(("Creating test dir %s\n",TEST_DIR));
+ if ((PR_MkDir(TEST_DIR, 0777)) < 0) {
+ printf(
+ "testfile failed to create dir %s [%d, %d]\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+ fd_dir = PR_OpenDir(TEST_DIR);
+ if (fd_dir == NULL) {
+ printf(
+ "testfile failed to open dirctory %s [%d, %d]\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+
+ strcpy(pathname, TEST_DIR);
+ strcat(pathname, "/");
+ strcat(pathname, FILE_NAME);
+ path_len = strlen(pathname);
+
+ for (i = 0; i < FILES_IN_DIR; i++) {
+
+ sprintf(pathname + path_len,"%d%s",i,"");
+
+ DPRINTF(("Creating test file %s\n",pathname));
+
+ fd_file = PR_Open(pathname, PR_RDWR | PR_CREATE_FILE, 0777);
+
+ if (fd_file == NULL) {
+ printf(
+ "testfile failed to create/open file %s [%d, %d]\n",
+ pathname, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+ PR_Close(fd_file);
+ }
+#if defined(XP_UNIX) || defined(XP_MAC) || (defined(XP_PC) && defined(WIN32))
+ /*
+ * Create a hidden file - a platform-dependent operation
+ */
+ strcpy(pathname, TEST_DIR);
+ strcat(pathname, "/");
+ strcat(pathname, HIDDEN_FILE_NAME);
+#if defined(XP_UNIX) || defined(XP_MAC)
+ DPRINTF(("Creating hidden test file %s\n",pathname));
+ fd_file = PR_Open(pathname, PR_RDWR | PR_CREATE_FILE, 0777);
+
+ if (fd_file == NULL) {
+ printf(
+ "testfile failed to create/open hidden file %s [%d, %d]\n",
+ pathname, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+
+#if defined(XP_MAC)
+ {
+#include <files.h>
+
+ OSErr err;
+ FCBPBRec fcbpb;
+ CInfoPBRec pb;
+ Str255 pascalMacPath;
+
+ fcbpb.ioNamePtr = pascalMacPath;
+ fcbpb.ioVRefNum = 0;
+ fcbpb.ioRefNum = fd_file->secret->md.osfd;
+ fcbpb.ioFCBIndx = 0;
+
+ err = PBGetFCBInfoSync(&fcbpb);
+ if (err != noErr) {
+ PR_Close(fd_file);
+ return -1;
+ }
+
+ pb.hFileInfo.ioNamePtr = pascalMacPath;
+ pb.hFileInfo.ioVRefNum = fcbpb.ioFCBVRefNum;
+ pb.hFileInfo.ioDirID = fcbpb.ioFCBParID;
+ pb.hFileInfo.ioFDirIndex = 0;
+
+ err = PBGetCatInfoSync(&pb);
+ if (err != noErr) {
+ PR_Close(fd_file);
+ return -1;
+ }
+
+ pb.hFileInfo.ioNamePtr = pascalMacPath;
+ pb.hFileInfo.ioVRefNum = fcbpb.ioFCBVRefNum;
+ pb.hFileInfo.ioDirID = fcbpb.ioFCBParID;
+ pb.hFileInfo.ioFDirIndex = 0;
+
+ pb.hFileInfo.ioFlFndrInfo.fdFlags |= fInvisible;
+
+ err = PBSetCatInfoSync(&pb);
+ if (err != noErr) {
+ PR_Close(fd_file);
+ return -1;
+ }
+
+ }
+#endif
+
+ PR_Close(fd_file);
+
+
+#elif defined(XP_PC) && defined(WIN32)
+ DPRINTF(("Creating hidden test file %s\n",pathname));
+ hfile = CreateFile(pathname, GENERIC_READ,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ CREATE_NEW,
+ FILE_ATTRIBUTE_HIDDEN,
+ NULL);
+ if (hfile == INVALID_HANDLE_VALUE) {
+ printf("testfile failed to create/open hidden file %s [0, %d]\n",
+ pathname, GetLastError());
+ return -1;
+ }
+ CloseHandle(hfile);
+
+#endif /* XP _UNIX || XP_MAC*/
+
+#endif /* XP_UNIX || XP_MAC ||(XP_PC && WIN32) */
+
+
+ if (PR_FAILURE == PR_CloseDir(fd_dir))
+ {
+ printf(
+ "testfile failed to close dirctory %s [%d, %d]\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+ fd_dir = PR_OpenDir(TEST_DIR);
+ if (fd_dir == NULL) {
+ printf(
+ "testfile failed to reopen dirctory %s [%d, %d]\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+
+ /*
+ * List all files, including hidden files
+ */
+ DPRINTF(("Listing all files in directory %s\n",TEST_DIR));
+#if defined(XP_UNIX) || defined(XP_MAC) || (defined(XP_PC) && defined(WIN32))
+ num_files = FILES_IN_DIR + 1;
+#else
+ num_files = FILES_IN_DIR;
+#endif
+ while ((dirEntry = PR_ReadDir(fd_dir, PR_SKIP_BOTH)) != NULL) {
+ num_files--;
+ strcpy(pathname, TEST_DIR);
+ strcat(pathname, "/");
+ strcat(pathname, dirEntry->name);
+ DPRINTF(("\t%s\n",dirEntry->name));
+
+ if ((PR_GetFileInfo(pathname, &info)) < 0) {
+ printf(
+ "testfile failed to GetFileInfo file %s [%d, %d]\n",
+ pathname, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+
+ if (info.type != PR_FILE_FILE) {
+ printf(
+ "testfile incorrect fileinfo for file %s [%d, %d]\n",
+ pathname, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+ }
+ if (num_files != 0)
+ {
+ printf(
+ "testfile failed to find all files in directory %s [%d, %d]\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+
+ PR_CloseDir(fd_dir);
+
+#if defined(XP_UNIX) || defined(XP_MAC) || (defined(XP_PC) && defined(WIN32))
+
+ /*
+ * List all files, except hidden files
+ */
+
+ fd_dir = PR_OpenDir(TEST_DIR);
+ if (fd_dir == NULL) {
+ printf(
+ "testfile failed to reopen dirctory %s [%d, %d]\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+
+ DPRINTF(("Listing non-hidden files in directory %s\n",TEST_DIR));
+ while ((dirEntry = PR_ReadDir(fd_dir, PR_SKIP_HIDDEN)) != NULL) {
+ DPRINTF(("\t%s\n",dirEntry->name));
+ if (!strcmp(HIDDEN_FILE_NAME, dirEntry->name)) {
+ printf("testfile found hidden file %s\n", pathname);
+ return -1;
+ }
+
+ }
+ /*
+ * Delete hidden file
+ */
+ strcpy(pathname, TEST_DIR);
+ strcat(pathname, "/");
+ strcat(pathname, HIDDEN_FILE_NAME);
+ if (PR_FAILURE == PR_Delete(pathname)) {
+ printf(
+ "testfile failed to delete hidden file %s [%d, %d]\n",
+ pathname, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+
+ PR_CloseDir(fd_dir);
+#endif /* XP_UNIX || XP_MAC || (XP_PC && WIN32) */
+
+ strcpy(renamename, TEST_DIR);
+ strcat(renamename, ".RENAMED");
+ if (PR_FAILURE == PR_Rename(TEST_DIR, renamename)) {
+ printf(
+ "testfile failed to rename directory %s [%d, %d]\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+
+ if (PR_FAILURE == PR_MkDir(TEST_DIR, 0777)) {
+ printf(
+ "testfile failed to recreate dir %s [%d, %d]\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+ if (PR_SUCCESS == PR_Rename(renamename, TEST_DIR)) {
+ printf(
+ "testfile renamed directory to existing name %s\n",
+ renamename);
+ return -1;
+ }
+
+ if (PR_FAILURE == PR_RmDir(TEST_DIR)) {
+ printf(
+ "testfile failed to rmdir %s [%d, %d]\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+
+ if (PR_FAILURE == PR_Rename(renamename, TEST_DIR)) {
+ printf(
+ "testfile failed to rename directory %s [%d, %d]\n",
+ renamename, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+ fd_dir = PR_OpenDir(TEST_DIR);
+ if (fd_dir == NULL) {
+ printf(
+ "testfile failed to reopen directory %s [%d, %d]\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+
+ strcpy(pathname, TEST_DIR);
+ strcat(pathname, "/");
+ strcat(pathname, FILE_NAME);
+ path_len = strlen(pathname);
+
+ for (i = 0; i < FILES_IN_DIR; i++) {
+
+ sprintf(pathname + path_len,"%d%s",i,"");
+
+ if (PR_FAILURE == PR_Delete(pathname)) {
+ printf(
+ "testfile failed to delete file %s [%d, %d]\n",
+ pathname, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+ }
+
+ PR_CloseDir(fd_dir);
+
+ if (PR_FAILURE == PR_RmDir(TEST_DIR)) {
+ printf(
+ "testfile failed to rmdir %s [%d, %d]\n",
+ TEST_DIR, PR_GetError(), PR_GetOSError());
+ return -1;
+ }
+
+ return 0;
+}
+/************************************************************************/
+
+/*
+ * Test file and directory NSPR APIs
+ */
+
+int main(int argc, char **argv)
+{
+#ifdef XP_UNIX
+ int opt;
+ extern char *optarg;
+ extern int optind;
+#endif
+#ifdef XP_UNIX
+ while ((opt = getopt(argc, argv, "d")) != EOF) {
+ switch(opt) {
+ case 'd':
+ _debug_on = 1;
+ break;
+ default:
+ break;
+ }
+ }
+#endif
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("testfile.log");
+#endif
+
+ mon = PR_NewMonitor();
+ if (mon == NULL) {
+ printf("testfile: PR_NewMonitor failed\n");
+ exit(2);
+ }
+
+ if (FileTest() < 0) {
+ printf("File Test failed\n");
+ exit(2);
+ }
+ printf("File Test passed\n");
+
+ if (DirTest() < 0) {
+ printf("Dir Test failed\n");
+ exit(2);
+ }
+ printf("Dir Test passed\n");
+
+ PR_DestroyMonitor(mon);
+ PR_Cleanup();
+ return 0;
+}
diff --git a/pr/tests/threads.c b/pr/tests/threads.c
new file mode 100644
index 00000000..b57884ed
--- /dev/null
+++ b/pr/tests/threads.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 "nspr.h"
+#include "prinrval.h"
+#include "plgetopt.h"
+#include "pprthred.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+#endif
+
+PRMonitor *mon;
+PRInt32 count, iterations, alive;
+
+PRBool debug_mode = PR_FALSE, passed = PR_TRUE;
+
+void
+PR_CALLBACK
+ReallyDumbThread(void *arg)
+{
+ return;
+}
+
+void
+PR_CALLBACK
+DumbThread(void *arg)
+{
+ PRInt32 tmp = (PRInt32)arg;
+ PRThreadScope scope = (PRThreadScope)tmp;
+ PRThread *thr;
+
+ thr = PR_CreateThread(PR_USER_THREAD,
+ ReallyDumbThread,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ scope,
+ PR_JOINABLE_THREAD,
+ 0);
+
+ if (!thr) {
+ if (debug_mode) {
+ printf("Could not create really dumb thread (%d, %d)!\n",
+ PR_GetError(), PR_GetOSError());
+ }
+ passed = PR_FALSE;
+ } else {
+ PR_JoinThread(thr);
+ }
+ PR_EnterMonitor(mon);
+ alive--;
+ PR_Notify(mon);
+ PR_ExitMonitor(mon);
+}
+
+static void CreateThreads(PRThreadScope scope1, PRThreadScope scope2)
+{
+ PRThread *thr;
+ int n;
+
+ alive = 0;
+ mon = PR_NewMonitor();
+
+ alive = count;
+ for (n=0; n<count; n++) {
+ thr = PR_CreateThread(PR_USER_THREAD,
+ DumbThread,
+ (void *)scope2,
+ PR_PRIORITY_NORMAL,
+ scope1,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (!thr) {
+ if (debug_mode) {
+ printf("Could not create dumb thread (%d, %d)!\n",
+ PR_GetError(), PR_GetOSError());
+ }
+ passed = PR_FALSE;
+ alive--;
+ }
+
+ PR_Sleep(0);
+ }
+
+ /* Wait for all threads to exit */
+ PR_EnterMonitor(mon);
+ while (alive) {
+ PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
+ }
+
+ PR_ExitMonitor(mon);
+ PR_DestroyMonitor(mon);
+}
+
+static void CreateThreadsUU(void)
+{
+ CreateThreads(PR_LOCAL_THREAD, PR_LOCAL_THREAD);
+}
+
+static void CreateThreadsUK(void)
+{
+ CreateThreads(PR_LOCAL_THREAD, PR_GLOBAL_THREAD);
+}
+
+static void CreateThreadsKU(void)
+{
+ CreateThreads(PR_GLOBAL_THREAD, PR_LOCAL_THREAD);
+}
+
+static void CreateThreadsKK(void)
+{
+ CreateThreads(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD);
+}
+
+/************************************************************************/
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ if (debug_mode)
+ {
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+ printf("%40s: %6.2f usec\n", msg, d / count);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int index;
+
+ PR_STDIO_INIT();
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_HIGH, 0);
+
+ {
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dc:i:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ case 'c': /* loop counter */
+ count = atoi(opt->value);
+ break;
+ case 'i': /* loop counter */
+ iterations = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ }
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("threads.log");
+ count = 10;
+ iterations = 10;
+ debug_mode = PR_TRUE;
+#else
+ if (0 == count) count = 50;
+ if (0 == iterations) iterations = 10;
+
+#endif
+
+ if (debug_mode)
+ {
+ printf("\
+** Tests lots of thread creations. \n\
+** Create %ld native threads %ld times. \n\
+** Create %ld user threads %ld times \n", iterations,count,iterations,count);
+ }
+
+ for (index=0; index<iterations; index++) {
+ Measure(CreateThreadsUU, "Create user/user threads");
+ Measure(CreateThreadsUK, "Create user/native threads");
+ Measure(CreateThreadsKU, "Create native/user threads");
+ Measure(CreateThreadsKK, "Create native/native threads");
+ }
+
+ if (debug_mode) printf("\nNow switch to recycling threads \n\n");
+ PR_SetThreadRecycleMode(1);
+
+ for (index=0; index<iterations; index++) {
+ Measure(CreateThreadsUU, "Create user/user threads");
+ Measure(CreateThreadsUK, "Create user/native threads");
+ Measure(CreateThreadsKU, "Create native/user threads");
+ Measure(CreateThreadsKK, "Create native/native threads");
+ }
+
+
+ printf("%s\n", ((passed) ? "PASS" : "FAIL"));
+
+ PR_Cleanup();
+
+ if (passed) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
diff --git a/pr/tests/thruput.c b/pr/tests/thruput.c
new file mode 100644
index 00000000..23843e19
--- /dev/null
+++ b/pr/tests/thruput.c
@@ -0,0 +1,398 @@
+/* -*- 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: thruput.c
+** Description: Test server's throughput capability comparing various
+** implmentation strategies.
+**
+** Note: Requires a server machine and an aribitrary number of
+** clients to bang on it. Trust the numbers on the server
+** more than those being displayed by the various clients.
+*/
+
+#include "prerror.h"
+#include "prinrval.h"
+#include "prinit.h"
+#include "prio.h"
+#include "prlock.h"
+#include "prmem.h"
+#include "prnetdb.h"
+#include "prprf.h"
+#include "prthread.h"
+
+#include "pprio.h"
+
+#include "plerror.h"
+#include "plgetopt.h"
+
+#define ADDR_BUFFER 100
+#define PORT_NUMBER 51877
+#define SAMPLING_INTERVAL 10
+#define BUFFER_SIZE (32 * 1024)
+
+static PRInt32 domain = PR_AF_INET;
+static PRInt32 protocol = 6; /* TCP */
+static PRFileDesc *err = NULL;
+static PRIntn concurrency = 1;
+static PRInt32 xport_buffer = -1;
+static PRUint32 initial_streams = 1;
+static PRInt32 buffer_size = BUFFER_SIZE;
+static PRThreadScope thread_scope = PR_LOCAL_THREAD;
+
+typedef struct Shared
+{
+ PRLock *ml;
+ PRUint32 sampled;
+ PRUint32 threads;
+ PRIntervalTime timein;
+ PRNetAddr server_address;
+} Shared;
+
+static Shared *shared = NULL;
+
+static PRStatus PrintAddress(const PRNetAddr* address)
+{
+ char buffer[ADDR_BUFFER];
+ PRStatus rv = PR_NetAddrToString(address, buffer, sizeof(buffer));
+ if (PR_SUCCESS == rv)
+ PR_fprintf(err, "%s:%u\n", buffer, PR_ntohs(address->inet.port));
+ else PL_FPrintError(err, "PR_NetAddrToString");
+ return rv;
+} /* PrintAddress */
+
+
+static void PR_CALLBACK Clientel(void *arg)
+{
+ PRStatus rv;
+ PRFileDesc *xport;
+ PRInt32 bytes, sampled;
+ PRIntervalTime now, interval;
+ PRBool do_display = PR_FALSE;
+ Shared *shared = (Shared*)arg;
+ char *buffer = (char*)PR_Malloc(buffer_size);
+ PRNetAddr *server_address = &shared->server_address;
+ PRIntervalTime connect_timeout = PR_SecondsToInterval(5);
+ PRIntervalTime sampling_interval = PR_SecondsToInterval(SAMPLING_INTERVAL);
+
+ PR_fprintf(err, "Client connecting to ");
+ (void)PrintAddress(server_address);
+
+ do
+ {
+ xport = PR_Socket(domain, PR_SOCK_STREAM, protocol);
+ if (NULL == xport)
+ {
+ PL_FPrintError(err, "PR_Socket");
+ return;
+ }
+
+ if (xport_buffer != -1)
+ {
+ PRSocketOptionData data;
+ data.option = PR_SockOpt_RecvBufferSize;
+ data.value.recv_buffer_size = (PRSize)xport_buffer;
+ rv = PR_SetSocketOption(xport, &data);
+ if (PR_FAILURE == rv)
+ PL_FPrintError(err, "PR_SetSocketOption - ignored");
+ data.option = PR_SockOpt_SendBufferSize;
+ data.value.send_buffer_size = (PRSize)xport_buffer;
+ rv = PR_SetSocketOption(xport, &data);
+ if (PR_FAILURE == rv)
+ PL_FPrintError(err, "PR_SetSocketOption - ignored");
+ }
+
+ rv = PR_Connect(xport, server_address, connect_timeout);
+ if (PR_FAILURE == rv)
+ {
+ PL_FPrintError(err, "PR_Connect");
+ if (PR_IO_TIMEOUT_ERROR != PR_GetError())
+ PR_Sleep(connect_timeout);
+ PR_Close(xport); /* delete it and start over */
+ }
+ } while (PR_FAILURE == rv);
+
+ do
+ {
+ bytes = PR_Recv(
+ xport, buffer, buffer_size, 0, PR_INTERVAL_NO_TIMEOUT);
+ PR_Lock(shared->ml);
+ now = PR_IntervalNow();
+ shared->sampled += bytes;
+ interval = now - shared->timein;
+ if (interval > sampling_interval)
+ {
+ sampled = shared->sampled;
+ shared->timein = now;
+ shared->sampled = 0;
+ do_display = PR_TRUE;
+ }
+ PR_Unlock(shared->ml);
+
+ if (do_display)
+ {
+ PRUint32 rate = sampled / PR_IntervalToMilliseconds(interval);
+ PR_fprintf(err, "%u streams @ %u Kbytes/sec\n", shared->threads, rate);
+ do_display = PR_FALSE;
+ }
+
+ } while (bytes > 0);
+} /* Clientel */
+
+static void Client(const char *server_name)
+{
+ PRStatus rv;
+ PRHostEnt host;
+ char buffer[PR_NETDB_BUF_SIZE];
+ PRIntervalTime dally = PR_SecondsToInterval(60);
+ PR_fprintf(err, "Translating the name %s\n", server_name);
+ rv = PR_GetHostByName(server_name, buffer, sizeof(buffer), &host);
+ if (PR_FAILURE == rv)
+ PL_FPrintError(err, "PR_GetHostByName");
+ else
+ {
+ if (PR_EnumerateHostEnt(
+ 0, &host, PORT_NUMBER, &shared->server_address) < 0)
+ PL_FPrintError(err, "PR_EnumerateHostEnt");
+ else
+ {
+ do
+ {
+ shared->threads += 1;
+ (void)PR_CreateThread(
+ PR_USER_THREAD, Clientel, shared,
+ PR_PRIORITY_NORMAL, thread_scope,
+ PR_UNJOINABLE_THREAD, 8 * 1024);
+ if (shared->threads == initial_streams)
+ {
+ PR_Sleep(dally);
+ initial_streams += 1;
+ }
+ } while (PR_TRUE);
+ }
+ }
+}
+
+static void PR_CALLBACK Servette(void *arg)
+{
+ PRInt32 bytes, sampled;
+ PRIntervalTime now, interval;
+ PRBool do_display = PR_FALSE;
+ PRFileDesc *client = (PRFileDesc*)arg;
+ char *buffer = (char*)PR_Malloc(buffer_size);
+ PRIntervalTime sampling_interval = PR_SecondsToInterval(SAMPLING_INTERVAL);
+
+ if (xport_buffer != -1)
+ {
+ PRStatus rv;
+ PRSocketOptionData data;
+ data.option = PR_SockOpt_RecvBufferSize;
+ data.value.recv_buffer_size = (PRSize)xport_buffer;
+ rv = PR_SetSocketOption(client, &data);
+ if (PR_FAILURE == rv)
+ PL_FPrintError(err, "PR_SetSocketOption - ignored");
+ data.option = PR_SockOpt_SendBufferSize;
+ data.value.send_buffer_size = (PRSize)xport_buffer;
+ rv = PR_SetSocketOption(client, &data);
+ if (PR_FAILURE == rv)
+ PL_FPrintError(err, "PR_SetSocketOption - ignored");
+ }
+
+ do
+ {
+ bytes = PR_Send(
+ client, buffer, buffer_size, 0, PR_INTERVAL_NO_TIMEOUT);
+
+ PR_Lock(shared->ml);
+ now = PR_IntervalNow();
+ shared->sampled += bytes;
+ interval = now - shared->timein;
+ if (interval > sampling_interval)
+ {
+ sampled = shared->sampled;
+ shared->timein = now;
+ shared->sampled = 0;
+ do_display = PR_TRUE;
+ }
+ PR_Unlock(shared->ml);
+
+ if (do_display)
+ {
+ PRUint32 rate = sampled / PR_IntervalToMilliseconds(interval);
+ PR_fprintf(err, "%u streams @ %u Kbytes/sec\n", shared->threads, rate);
+ do_display = PR_FALSE;
+ }
+ } while (bytes > 0);
+} /* Servette */
+
+static void Server(void)
+{
+ PRStatus rv;
+ PRNetAddr server_address, client_address;
+ PRFileDesc *xport = PR_Socket(domain, PR_SOCK_STREAM, protocol);
+
+ if (NULL == xport)
+ {
+ PL_FPrintError(err, "PR_Socket");
+ return;
+ }
+
+ rv = PR_InitializeNetAddr(PR_IpAddrAny, PORT_NUMBER, &server_address);
+ if (PR_FAILURE == rv) PL_FPrintError(err, "PR_InitializeNetAddr");
+ else
+ {
+ rv = PR_Bind(xport, &server_address);
+ if (PR_FAILURE == rv) PL_FPrintError(err, "PR_Bind");
+ else
+ {
+ PRFileDesc *client;
+ rv = PR_Listen(xport, 10);
+ PR_fprintf(err, "Server listening on ");
+ (void)PrintAddress(&server_address);
+ do
+ {
+ client = PR_Accept(
+ xport, &client_address, PR_INTERVAL_NO_TIMEOUT);
+ if (NULL == client) PL_FPrintError(err, "PR_Accept");
+ else
+ {
+ PR_fprintf(err, "Server accepting from ");
+ (void)PrintAddress(&client_address);
+ shared->threads += 1;
+ (void)PR_CreateThread(
+ PR_USER_THREAD, Servette, client,
+ PR_PRIORITY_NORMAL, thread_scope,
+ PR_UNJOINABLE_THREAD, 8 * 1024);
+ }
+ } while (PR_TRUE);
+
+ }
+ }
+} /* Server */
+
+static void Help(void)
+{
+ PR_fprintf(err, "Usage: [-h] [<server>]\n");
+ PR_fprintf(err, "\t-s <n> Initial # of connections (default: 1)\n");
+ PR_fprintf(err, "\t-C <n> Set 'concurrency' (default: 1)\n");
+ PR_fprintf(err, "\t-b <nK> Client buffer size (default: 32k)\n");
+ PR_fprintf(err, "\t-B <nK> Transport recv/send buffer size (default: sys)\n");
+ PR_fprintf(err, "\t-G Use GLOBAL threads (default: LOCAL)\n");
+ PR_fprintf(err, "\t-X Use XTP transport (default: TCP)\n");
+#ifdef _PR_INET6
+ PR_fprintf(err, "\t-6 Use IPv6 (default: IPv4)\n");
+#endif /* _PR_INET6 */
+ PR_fprintf(err, "\t-h This message and nothing else\n");
+ PR_fprintf(err, "\t<server> DNS name of server\n");
+ PR_fprintf(err, "\t\tIf <server> is not specified, this host will be\n");
+ PR_fprintf(err, "\t\tthe server and not act as a client.\n");
+} /* Help */
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PLOptStatus os;
+ const char *server_name = NULL;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "hGX6C:b:s:B:");
+
+ err = PR_GetSpecialFD(PR_StandardError);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 0: /* Name of server */
+ server_name = opt->value;
+ break;
+ case 'G': /* Globular threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'X': /* Use XTP as the transport */
+ protocol = 36;
+ break;
+#ifdef _PR_INET6
+ case '6': /* Use IPv6 */
+ domain = PR_AF_INET6;
+ PR_SetIPv6Enable(PR_TRUE);
+ break;
+#endif /* _PR_INET6 */
+ case 's': /* initial_streams */
+ initial_streams = atoi(opt->value);
+ break;
+ case 'C': /* concurrency */
+ concurrency = atoi(opt->value);
+ break;
+ case 'b': /* buffer size */
+ buffer_size = 1024 * atoi(opt->value);
+ break;
+ case 'B': /* buffer size */
+ xport_buffer = 1024 * atoi(opt->value);
+ break;
+ case 'h': /* user wants some guidance */
+ default:
+ Help(); /* so give him an earful */
+ return 2; /* but not a lot else */
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ shared = PR_NEWZAP(Shared);
+ shared->ml = PR_NewLock();
+
+ PR_fprintf(err,
+ "This machine is %s\n",
+ (NULL == server_name) ? "the SERVER" : "a CLIENT");
+
+ PR_fprintf(err,
+ "Transport being used is %s\n",
+ (6 == protocol) ? "TCP" : "XTP");
+
+ if (PR_GLOBAL_THREAD == thread_scope)
+ {
+ if (1 != concurrency)
+ {
+ PR_fprintf(err, " **Concurrency > 1 and GLOBAL threads!?!?\n");
+ PR_fprintf(err, " **Ignoring concurrency\n");
+ concurrency = 1;
+ }
+ }
+
+ if (1 != concurrency)
+ {
+ PR_SetConcurrency(concurrency);
+ PR_fprintf(err, "Concurrency set to %u\n", concurrency);
+ }
+
+ PR_fprintf(err,
+ "All threads will be %s\n",
+ (PR_GLOBAL_THREAD == thread_scope) ? "GLOBAL" : "LOCAL");
+
+ PR_fprintf(err, "Client buffer size will be %u\n", buffer_size);
+
+ if (-1 != xport_buffer)
+ PR_fprintf(
+ err, "Transport send & receive buffer size will be %u\n", xport_buffer);
+
+
+ if (NULL == server_name) Server();
+ else Client(server_name);
+
+} /* main */
+
+/* thruput.c */
+
diff --git a/pr/tests/time.c b/pr/tests/time.c
new file mode 100644
index 00000000..84a30870
--- /dev/null
+++ b/pr/tests/time.c
@@ -0,0 +1,182 @@
+/* -*- 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.
+ */
+
+/*
+ * Program to test different ways to get the time; right now it is tuned
+ * only for solaris.
+ * solaris results (100000 iterations):
+ * time to get time with time(): 4.63 usec avg, 463 msec total
+ * time to get time with gethrtime(): 2.17 usec avg, 217 msec total
+ * time to get time with gettimeofday(): 1.25 usec avg, 125 msec total
+ *
+ *
+ */
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+#include "prpriv.h"
+#include "prinrval.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define DEFAULT_COUNT 100000
+PRInt32 count;
+
+time_t itime;
+hrtime_t ihrtime;
+
+void
+ftime_init()
+{
+ itime = time(NULL);
+ ihrtime = gethrtime();
+}
+
+time_t
+ftime()
+{
+ hrtime_t now = gethrtime();
+
+ return itime + ((now - ihrtime) / 1000000000ll);
+}
+
+static void timeTime(void)
+{
+ PRInt32 index = count;
+ time_t rv;
+
+ for (;index--;)
+ rv = time(NULL);
+}
+
+static void timeGethrtime(void)
+{
+ PRInt32 index = count;
+ time_t rv;
+
+ for (;index--;)
+ rv = ftime();
+}
+
+#include <sys/time.h>
+static void timeGettimeofday(void)
+{
+ PRInt32 index = count;
+ time_t rv;
+ struct timeval tp;
+
+ for (;index--;)
+ rv = gettimeofday(&tp, NULL);
+}
+
+static void timePRTime32(void)
+{
+ PRInt32 index = count;
+ PRInt32 rv32;
+ PRTime q;
+ PRTime rv;
+
+ LL_I2L(q, 1000000);
+
+ for (;index--;) {
+ rv = PR_Now();
+ LL_DIV(rv, rv, q);
+ LL_L2I(rv32, rv);
+ }
+}
+
+static void timePRTime64(void)
+{
+ PRInt32 index = count;
+ PRTime rv;
+
+ for (;index--;)
+ rv = PR_Now();
+}
+
+/************************************************************************/
+
+static void Measure(void (*func)(void), const char *msg)
+{
+ PRIntervalTime start, stop;
+ double d;
+ PRInt32 tot;
+
+ start = PR_IntervalNow();
+ (*func)();
+ stop = PR_IntervalNow();
+
+ d = (double)PR_IntervalToMicroseconds(stop - start);
+ tot = PR_IntervalToMilliseconds(stop-start);
+
+ if (debug_mode) printf("%40s: %6.2f usec avg, %d msec total\n", msg, d / count, tot);
+}
+
+void main(int argc, char **argv)
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+
+ if (argc > 1) {
+ count = atoi(argv[1]);
+ } else {
+ count = DEFAULT_COUNT;
+ }
+
+ ftime_init();
+
+ Measure(timeTime, "time to get time with time()");
+ Measure(timeGethrtime, "time to get time with gethrtime()");
+ Measure(timeGettimeofday, "time to get time with gettimeofday()");
+ Measure(timePRTime32, "time to get time with PR_Time() (32bit)");
+ Measure(timePRTime64, "time to get time with PR_Time() (64bit)");
+
+ PR_Cleanup();
+ return 0;
+}
+
+
+
diff --git a/pr/tests/timemac.c b/pr/tests/timemac.c
new file mode 100644
index 00000000..fc4b78d5
--- /dev/null
+++ b/pr/tests/timemac.c
@@ -0,0 +1,134 @@
+/* -*- 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: timemac.c
+ * description: test time and date routines on the Mac
+ */
+#include <stdio.h>
+#include "prinit.h"
+#include "prtime.h"
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+#endif
+
+
+static char *dayOfWeek[] =
+ { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "???" };
+static char *month[] =
+ { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???" };
+
+static void printExplodedTime(const PRExplodedTime *et) {
+ PRInt32 totalOffset;
+ PRInt32 hourOffset, minOffset;
+ const char *sign;
+
+ /* Print day of the week, month, day, hour, minute, and second */
+ printf( "%s %s %ld %02ld:%02ld:%02ld ",
+ dayOfWeek[et->tm_wday], month[et->tm_month], et->tm_mday,
+ et->tm_hour, et->tm_min, et->tm_sec);
+
+ /* Print time zone */
+ totalOffset = et->tm_params.tp_gmt_offset + et->tm_params.tp_dst_offset;
+ if (totalOffset == 0) {
+ printf("UTC ");
+ } else {
+ sign = "";
+ if (totalOffset < 0) {
+ totalOffset = -totalOffset;
+ sign = "-";
+ }
+ hourOffset = totalOffset / 3600;
+ minOffset = (totalOffset % 3600) / 60;
+ printf("%s%02ld%02ld ", sign, hourOffset, minOffset);
+ }
+
+ /* Print year */
+ printf("%d", et->tm_year);
+}
+
+int main(int argc, char** argv)
+{
+ PR_STDIO_INIT();
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("timemac.log");
+#endif
+
+ /*
+ *************************************************************
+ **
+ ** Testing PR_Now(), PR_ExplodeTime, and PR_ImplodeTime
+ ** on the current time
+ **
+ *************************************************************
+ */
+
+ {
+ PRTime t1, t2;
+ PRExplodedTime et;
+
+ printf("*********************************************\n");
+ printf("** **\n");
+ printf("** Testing PR_Now(), PR_ExplodeTime, and **\n");
+ printf("** PR_ImplodeTime on the current time **\n");
+ printf("** **\n");
+ printf("*********************************************\n\n");
+ t1 = PR_Now();
+
+ /* First try converting to UTC */
+
+ PR_ExplodeTime(t1, PR_GMTParameters, &et);
+ if (et.tm_params.tp_gmt_offset || et.tm_params.tp_dst_offset) {
+ printf("ERROR: UTC has nonzero gmt or dst offset.\n");
+ return 1;
+ }
+ printf("Current UTC is ");
+ printExplodedTime(&et);
+ printf("\n");
+
+ t2 = PR_ImplodeTime(&et);
+ if (LL_NE(t1, t2)) {
+ printf("ERROR: Explode and implode are NOT inverse.\n");
+ return 1;
+ }
+
+ /* Next, try converting to local (US Pacific) time */
+
+ PR_ExplodeTime(t1, PR_LocalTimeParameters, &et);
+ printf("Current local time is ");
+ printExplodedTime(&et);
+ printf("\n");
+ printf("GMT offset is %ld, DST offset is %ld\n",
+ et.tm_params.tp_gmt_offset, et.tm_params.tp_dst_offset);
+ t2 = PR_ImplodeTime(&et);
+ if (LL_NE(t1, t2)) {
+ printf("ERROR: Explode and implode are NOT inverse.\n");
+ return 1;
+ }
+ }
+
+ printf("Please examine the results\n");
+ return 0;
+}
diff --git a/pr/tests/timetest.c b/pr/tests/timetest.c
new file mode 100644
index 00000000..5d7b2a62
--- /dev/null
+++ b/pr/tests/timetest.c
@@ -0,0 +1,760 @@
+/* -*- 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: timetest.c
+ * description: test time and date routines
+ */
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "prinit.h"
+#include "prtime.h"
+#include "prprf.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef XP_MAC
+#include "prlog.h"
+int fprintf(FILE *stream, const char *fmt, ...)
+{
+PR_LogPrint(fmt);
+return 0;
+}
+#define printf PR_LogPrint
+extern void SetupMacPrintfLog(char *logFile);
+#endif
+
+int failed_already=0;
+PRBool debug_mode = PR_FALSE;
+
+static char *dayOfWeek[] =
+ { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "???" };
+static char *month[] =
+ { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???" };
+
+static void printExplodedTime(const PRExplodedTime *et) {
+ PRInt32 totalOffset;
+ PRInt32 hourOffset, minOffset;
+ const char *sign;
+
+ /* Print day of the week, month, day, hour, minute, and second */
+ if (debug_mode) printf("%s %s %ld %02ld:%02ld:%02ld ",
+ dayOfWeek[et->tm_wday], month[et->tm_month], et->tm_mday,
+ et->tm_hour, et->tm_min, et->tm_sec);
+
+ /* Print time zone */
+ totalOffset = et->tm_params.tp_gmt_offset + et->tm_params.tp_dst_offset;
+ if (totalOffset == 0) {
+ if (debug_mode) printf("UTC ");
+ } else {
+ sign = "+";
+ if (totalOffset < 0) {
+ totalOffset = -totalOffset;
+ sign = "-";
+ }
+ hourOffset = totalOffset / 3600;
+ minOffset = (totalOffset % 3600) / 60;
+ if (debug_mode)
+ printf("%s%02ld%02ld ", sign, hourOffset, minOffset);
+ }
+
+ /* Print year */
+ if (debug_mode) printf("%hd", et->tm_year);
+}
+
+static int explodedTimeIsEqual(const PRExplodedTime *et1,
+ const PRExplodedTime *et2)
+{
+ if (et1->tm_usec == et2->tm_usec &&
+ et1->tm_sec == et2->tm_sec &&
+ et1->tm_min == et2->tm_min &&
+ et1->tm_hour == et2->tm_hour &&
+ et1->tm_mday == et2->tm_mday &&
+ et1->tm_month == et2->tm_month &&
+ et1->tm_year == et2->tm_year &&
+ et1->tm_wday == et2->tm_wday &&
+ et1->tm_yday == et2->tm_yday &&
+ et1->tm_params.tp_gmt_offset == et2->tm_params.tp_gmt_offset &&
+ et1->tm_params.tp_dst_offset == et2->tm_params.tp_dst_offset) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static void
+testParseTimeString(PRTime t)
+{
+ PRExplodedTime et;
+ PRTime t2;
+ char timeString[128];
+ char buf[128];
+ PRInt32 totalOffset;
+ PRInt32 hourOffset, minOffset;
+ const char *sign;
+ PRInt64 usec_per_sec;
+
+ /* Truncate the microsecond part of PRTime */
+ LL_I2L(usec_per_sec, PR_USEC_PER_SEC);
+ LL_DIV(t, t, usec_per_sec);
+ LL_MUL(t, t, usec_per_sec);
+
+ PR_ExplodeTime(t, PR_LocalTimeParameters, &et);
+
+ /* Print day of the week, month, day, hour, minute, and second */
+ PR_snprintf(timeString, 128, "%s %s %ld %02ld:%02ld:%02ld ",
+ dayOfWeek[et.tm_wday], month[et.tm_month], et.tm_mday,
+ et.tm_hour, et.tm_min, et.tm_sec);
+ /* Print time zone */
+ totalOffset = et.tm_params.tp_gmt_offset + et.tm_params.tp_dst_offset;
+ if (totalOffset == 0) {
+ strcat(timeString, "GMT "); /* I wanted to use "UTC" here, but
+ * PR_ParseTimeString doesn't
+ * understand "UTC". */
+ } else {
+ sign = "+";
+ if (totalOffset < 0) {
+ totalOffset = -totalOffset;
+ sign = "-";
+ }
+ hourOffset = totalOffset / 3600;
+ minOffset = (totalOffset % 3600) / 60;
+ PR_snprintf(buf, 128, "%s%02ld%02ld ", sign, hourOffset, minOffset);
+ strcat(timeString, buf);
+ }
+ /* Print year */
+ PR_snprintf(buf, 128, "%hd", et.tm_year);
+ strcat(timeString, buf);
+
+ if (PR_ParseTimeString(timeString, PR_FALSE, &t2) == PR_FAILURE) {
+ fprintf(stderr, "PR_ParseTimeString() failed\n");
+ exit(1);
+ }
+ if (LL_NE(t, t2)) {
+ fprintf(stderr, "PR_ParseTimeString() incorrect\n");
+ PR_snprintf(buf, 128, "t is %lld, t2 is %lld, time string is %s\n",
+ t, t2, timeString);
+ fprintf(stderr, "%s\n", buf);
+ exit(1);
+ }
+}
+
+int main(int argc, char** argv)
+{
+ /* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d
+ */
+ PLOptStatus os;
+ PLOptState *opt;
+
+ PR_STDIO_INIT();
+ opt = PL_CreateOptState(argc, argv, "d");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = PR_TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ /* main test */
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("timetest.log");
+ debug_mode = PR_TRUE;
+#endif
+ /* Testing zero PRTime (the epoch) */
+ {
+ PRTime t;
+ PRExplodedTime et;
+
+ LL_I2L(t, 0);
+ if (debug_mode) printf("The NSPR epoch is:\n");
+ PR_ExplodeTime(t, PR_LocalTimeParameters, &et);
+ printExplodedTime(&et);
+ if (debug_mode) printf("\n");
+ PR_ExplodeTime(t, PR_GMTParameters, &et);
+ printExplodedTime(&et);
+ if (debug_mode) printf("\n\n");
+ testParseTimeString(t);
+ }
+
+ /*
+ *************************************************************
+ **
+ ** Testing PR_Now(), PR_ExplodeTime, and PR_ImplodeTime
+ ** on the current time
+ **
+ *************************************************************
+ */
+
+ {
+ PRTime t1, t2;
+ PRExplodedTime et;
+
+ if (debug_mode) {
+ printf("*********************************************\n");
+ printf("** **\n");
+ printf("** Testing PR_Now(), PR_ExplodeTime, and **\n");
+ printf("** PR_ImplodeTime on the current time **\n");
+ printf("** **\n");
+ printf("*********************************************\n\n");
+ }
+ t1 = PR_Now();
+
+ /* First try converting to UTC */
+
+ PR_ExplodeTime(t1, PR_GMTParameters, &et);
+ if (et.tm_params.tp_gmt_offset || et.tm_params.tp_dst_offset) {
+ if (debug_mode) printf("ERROR: UTC has nonzero gmt or dst offset.\n");
+ else failed_already=1;
+ return 1;
+ }
+ if (debug_mode) printf("Current UTC is ");
+ printExplodedTime(&et);
+ if (debug_mode) printf("\n");
+
+ t2 = PR_ImplodeTime(&et);
+ if (LL_NE(t1, t2)) {
+ if (debug_mode) printf("ERROR: Explode and implode are NOT inverse.\n");
+ else printf("FAIL\n");
+ return 1;
+ }
+
+ /* Next, try converting to local (US Pacific) time */
+
+ PR_ExplodeTime(t1, PR_LocalTimeParameters, &et);
+ if (debug_mode) printf("Current local time is ");
+ printExplodedTime(&et);
+ if (debug_mode) printf("\n");
+ if (debug_mode) printf("GMT offset is %ld, DST offset is %ld\n",
+ et.tm_params.tp_gmt_offset, et.tm_params.tp_dst_offset);
+ t2 = PR_ImplodeTime(&et);
+ if (LL_NE(t1, t2)) {
+ if (debug_mode) printf("ERROR: Explode and implode are NOT inverse.\n");
+ return 1;
+ }
+
+ if (debug_mode) printf("Please examine the results\n");
+ testParseTimeString(t1);
+ }
+
+
+ /*
+ *******************************************
+ **
+ ** Testing PR_NormalizeTime()
+ **
+ *******************************************
+ */
+
+ /* July 4, 2001 is Wednesday */
+ {
+ PRExplodedTime et;
+
+ if (debug_mode) {
+ printf("\n");
+ printf("**********************************\n");
+ printf("** **\n");
+ printf("** Testing PR_NormalizeTime() **\n");
+ printf("** **\n");
+ printf("**********************************\n\n");
+ }
+ et.tm_year = 2001;
+ et.tm_month = 7 - 1;
+ et.tm_mday = 4;
+ et.tm_hour = 0;
+ et.tm_min = 0;
+ et.tm_sec = 0;
+ et.tm_usec = 0;
+ et.tm_params = PR_GMTParameters(&et);
+
+ PR_NormalizeTime(&et, PR_GMTParameters);
+
+ if (debug_mode) printf("July 4, 2001 is %s.\n", dayOfWeek[et.tm_wday]);
+ if (et.tm_wday == 3) {
+ if (debug_mode) printf("PASS\n");
+ } else {
+ if (debug_mode) printf("ERROR: It should be Wednesday\n");
+ else failed_already=1;
+ return 1;
+ }
+ testParseTimeString(PR_ImplodeTime(&et));
+
+ /* June 12, 1997 23:00 PST == June 13, 1997 00:00 PDT */
+ et.tm_year = 1997;
+ et.tm_month = 6 - 1;
+ et.tm_mday = 12;
+ et.tm_hour = 23;
+ et.tm_min = 0;
+ et.tm_sec = 0;
+ et.tm_usec = 0;
+ et.tm_params.tp_gmt_offset = -8 * 3600;
+ et.tm_params.tp_dst_offset = 0;
+
+ PR_NormalizeTime(&et, PR_USPacificTimeParameters);
+
+ if (debug_mode) {
+ printf("Thu Jun 12, 1997 23:00:00 PST is ");
+ }
+ printExplodedTime(&et);
+ if (debug_mode) printf(".\n");
+ if (et.tm_wday == 5) {
+ if (debug_mode) printf("PASS\n");
+ } else {
+ if (debug_mode) printf("ERROR: It should be Friday\n");
+ else failed_already=1;
+ return 1;
+ }
+ testParseTimeString(PR_ImplodeTime(&et));
+
+ /* Feb 14, 1997 00:00:00 PDT == Feb 13, 1997 23:00:00 PST */
+ et.tm_year = 1997;
+ et.tm_month = 2 - 1;
+ et.tm_mday = 14;
+ et.tm_hour = 0;
+ et.tm_min = 0;
+ et.tm_sec = 0;
+ et.tm_usec = 0;
+ et.tm_params.tp_gmt_offset = -8 * 3600;
+ et.tm_params.tp_dst_offset = 3600;
+
+ PR_NormalizeTime(&et, PR_USPacificTimeParameters);
+
+ if (debug_mode) {
+ printf("Fri Feb 14, 1997 00:00:00 PDT is ");
+ }
+ printExplodedTime(&et);
+ if (debug_mode) printf(".\n");
+ if (et.tm_wday == 4) {
+ if (debug_mode) printf("PASS\n");
+ } else {
+ if (debug_mode) printf("ERROR: It should be Thursday\n");
+ else failed_already=1;
+ return 1;
+ }
+ testParseTimeString(PR_ImplodeTime(&et));
+
+ /* What time is Nov. 7, 1996, 18:29:23 PDT? */
+ et.tm_year = 1996;
+ et.tm_month = 11 - 1;
+ et.tm_mday = 7;
+ et.tm_hour = 18;
+ et.tm_min = 29;
+ et.tm_sec = 23;
+ et.tm_usec = 0;
+ et.tm_params.tp_gmt_offset = -8 * 3600; /* PDT */
+ et.tm_params.tp_dst_offset = 3600;
+
+ PR_NormalizeTime(&et, PR_LocalTimeParameters);
+ if (debug_mode) printf("Nov 7 18:29:23 PDT 1996 is ");
+ printExplodedTime(&et);
+ if (debug_mode) printf(".\n");
+ testParseTimeString(PR_ImplodeTime(&et));
+
+ /* What time is Oct. 7, 1995, 18:29:23 PST? */
+ et.tm_year = 1995;
+ et.tm_month = 10 - 1;
+ et.tm_mday = 7;
+ et.tm_hour = 18;
+ et.tm_min = 29;
+ et.tm_sec = 23;
+ et.tm_params.tp_gmt_offset = -8 * 3600; /* PST */
+ et.tm_params.tp_dst_offset = 0;
+
+ PR_NormalizeTime(&et, PR_LocalTimeParameters);
+ if (debug_mode) printf("Oct 7 18:29:23 PST 1995 is ");
+ printExplodedTime(&et);
+ if (debug_mode) printf(".\n");
+ testParseTimeString(PR_ImplodeTime(&et));
+
+ if (debug_mode) printf("Please examine the results\n");
+ }
+
+ /*
+ **************************************************************
+ **
+ ** Testing range of years
+ **
+ **************************************************************
+ */
+
+ {
+ PRExplodedTime et1, et2;
+ PRTime ttt;
+ PRTime secs;
+
+ if (debug_mode) {
+ printf("\n");
+ printf("***************************************\n");
+ printf("** **\n");
+ printf("** Testing range of years **\n");
+ printf("** **\n");
+ printf("***************************************\n\n");
+ }
+ /* April 4, 1917 GMT */
+ et1.tm_usec = 0;
+ et1.tm_sec = 0;
+ et1.tm_min = 0;
+ et1.tm_hour = 0;
+ et1.tm_mday = 4;
+ et1.tm_month = 4 - 1;
+ et1.tm_year = 1917;
+ et1.tm_params = PR_GMTParameters(&et1);
+ PR_NormalizeTime(&et1, PR_LocalTimeParameters);
+ secs = PR_ImplodeTime(&et1);
+ if (LL_GE_ZERO(secs)) {
+ if (debug_mode)
+ printf("ERROR: April 4, 1917 GMT returns a nonnegative second count\n");
+ failed_already = 1;
+ return 1;
+ }
+ PR_ExplodeTime(secs, PR_LocalTimeParameters, &et2);
+ if (!explodedTimeIsEqual(&et1, &et2)) {
+ if (debug_mode)
+ printf("ERROR: PR_ImplodeTime and PR_ExplodeTime are not inverse for April 4, 1917 GMT\n");
+ failed_already=1;
+ return 1;
+ }
+ ttt = PR_ImplodeTime(&et1);
+ testParseTimeString( ttt );
+
+ if (debug_mode) printf("Test passed for April 4, 1917\n");
+
+ /* July 4, 2050 */
+ et1.tm_usec = 0;
+ et1.tm_sec = 0;
+ et1.tm_min = 0;
+ et1.tm_hour = 0;
+ et1.tm_mday = 4;
+ et1.tm_month = 7 - 1;
+ et1.tm_year = 2050;
+ et1.tm_params = PR_GMTParameters(&et1);
+ PR_NormalizeTime(&et1, PR_LocalTimeParameters);
+ secs = PR_ImplodeTime(&et1);
+ if (!LL_GE_ZERO(secs)) {
+ if (debug_mode)
+ printf("ERROR: July 4, 2050 GMT returns a negative second count\n");
+ failed_already = 1;
+ return 1;
+ }
+ PR_ExplodeTime(secs, PR_LocalTimeParameters, &et2);
+ if (!explodedTimeIsEqual(&et1, &et2)) {
+ if (debug_mode)
+ printf("ERROR: PR_ImplodeTime and PR_ExplodeTime are not inverse for July 4, 2050 GMT\n");
+ failed_already=1;
+ return 1;
+ }
+ testParseTimeString(PR_ImplodeTime(&et1));
+
+ if (debug_mode) printf("Test passed for July 4, 2050\n");
+
+ }
+
+ /*
+ **************************************************************
+ **
+ ** Stress test
+ *
+ ** Go through four years, starting from
+ ** 00:00:00 PST Jan. 1, 1993, incrementing
+ ** every 10 minutes.
+ **
+ **************************************************************
+ */
+
+ {
+ PRExplodedTime et, et1, et2;
+ PRInt64 usecPer10Min;
+ int day, hour, min;
+ PRTime usecs;
+ int dstInEffect = 0;
+
+ if (debug_mode) {
+ printf("\n");
+ printf("*******************************************************\n");
+ printf("** **\n");
+ printf("** Stress test **\n");
+ printf("** Starting from midnight Jan. 1, 1993 PST, **\n");
+ printf("** going through four years in 10-minute increment **\n");
+ printf("** **\n");
+ printf("*******************************************************\n\n");
+ }
+ LL_I2L(usecPer10Min, 600000000L);
+
+ /* 00:00:00 PST Jan. 1, 1993 */
+ et.tm_usec = 0;
+ et.tm_sec = 0;
+ et.tm_min = 0;
+ et.tm_hour = 0;
+ et.tm_mday = 1;
+ et.tm_month = 0;
+ et.tm_year = 1993;
+ et.tm_params.tp_gmt_offset = -8 * 3600;
+ et.tm_params.tp_dst_offset = 0;
+ usecs = PR_ImplodeTime(&et);
+
+ for (day = 0; day < 4 * 365 + 1; day++) {
+ for (hour = 0; hour < 24; hour++) {
+ for (min = 0; min < 60; min += 10) {
+ LL_ADD(usecs, usecs, usecPer10Min);
+ PR_ExplodeTime(usecs, PR_USPacificTimeParameters, &et1);
+
+ et2 = et;
+ et2.tm_usec += 600000000L;
+ PR_NormalizeTime(&et2, PR_USPacificTimeParameters);
+
+ if (!explodedTimeIsEqual(&et1, &et2)) {
+ if (debug_mode) printf("ERROR: componentwise comparison failed\n");
+ printExplodedTime(&et1);
+ if (debug_mode) printf("\n");
+ printExplodedTime(&et2);
+ if (debug_mode) printf("\n");
+ failed_already=1;
+ return 1;
+ }
+
+ if (LL_NE(usecs, PR_ImplodeTime(&et1))) {
+ if (debug_mode)
+ printf("ERROR: PR_ExplodeTime and PR_ImplodeTime are not inverse\n");
+ printExplodedTime(&et1);
+ if (debug_mode) printf("\n");
+ failed_already=1;
+ return 1;
+ }
+ testParseTimeString(usecs);
+
+ if (!dstInEffect && et1.tm_params.tp_dst_offset) {
+ dstInEffect = 1;
+ if (debug_mode) printf("DST changeover from ");
+ printExplodedTime(&et);
+ if (debug_mode) printf(" to ");
+ printExplodedTime(&et1);
+ if (debug_mode) printf(".\n");
+ } else if (dstInEffect && !et1.tm_params.tp_dst_offset) {
+ dstInEffect = 0;
+ if (debug_mode) printf("DST changeover from ");
+ printExplodedTime(&et);
+ if (debug_mode) printf(" to ");
+ printExplodedTime(&et1);
+ if (debug_mode) printf(".\n");
+ }
+
+ et = et1;
+ }
+ }
+ }
+ if (debug_mode) printf("Test passed\n");
+ }
+
+
+ /* Same stress test, but with PR_LocalTimeParameters */
+
+ {
+ PRExplodedTime et, et1, et2;
+ PRInt64 usecPer10Min;
+ int day, hour, min;
+ PRTime usecs;
+ int dstInEffect = 0;
+
+ if (debug_mode) {
+ printf("\n");
+ printf("*******************************************************\n");
+ printf("** **\n");
+ printf("** Stress test **\n");
+ printf("** Starting from midnight Jan. 1, 1993 PST, **\n");
+ printf("** going through four years in 10-minute increment **\n");
+ printf("** **\n");
+ printf("*******************************************************\n\n");
+ }
+
+ LL_I2L(usecPer10Min, 600000000L);
+
+ /* 00:00:00 PST Jan. 1, 1993 */
+ et.tm_usec = 0;
+ et.tm_sec = 0;
+ et.tm_min = 0;
+ et.tm_hour = 0;
+ et.tm_mday = 1;
+ et.tm_month = 0;
+ et.tm_year = 1993;
+ et.tm_params.tp_gmt_offset = -8 * 3600;
+ et.tm_params.tp_dst_offset = 0;
+ usecs = PR_ImplodeTime(&et);
+
+ for (day = 0; day < 4 * 365 + 1; day++) {
+ for (hour = 0; hour < 24; hour++) {
+ for (min = 0; min < 60; min += 10) {
+ LL_ADD(usecs, usecs, usecPer10Min);
+ PR_ExplodeTime(usecs, PR_LocalTimeParameters, &et1);
+
+ et2 = et;
+ et2.tm_usec += 600000000L;
+ PR_NormalizeTime(&et2, PR_LocalTimeParameters);
+
+ if (!explodedTimeIsEqual(&et1, &et2)) {
+ if (debug_mode) printf("ERROR: componentwise comparison failed\n");
+ printExplodedTime(&et1);
+ if (debug_mode) printf("\n");
+ printExplodedTime(&et2);
+ if (debug_mode) printf("\n");
+ return 1;
+ }
+
+ if (LL_NE(usecs, PR_ImplodeTime(&et1))) {
+ printf("ERROR: PR_ExplodeTime and PR_ImplodeTime are not inverse\n");
+ printExplodedTime(&et1);
+ if (debug_mode) printf("\n");
+ failed_already=1;
+ return 1;
+ }
+ testParseTimeString(usecs);
+
+ if (!dstInEffect && et1.tm_params.tp_dst_offset) {
+ dstInEffect = 1;
+ if (debug_mode) printf("DST changeover from ");
+ printExplodedTime(&et);
+ if (debug_mode) printf(" to ");
+ printExplodedTime(&et1);
+ if (debug_mode) printf(".\n");
+ } else if (dstInEffect && !et1.tm_params.tp_dst_offset) {
+ dstInEffect = 0;
+ if (debug_mode) printf("DST changeover from ");
+ printExplodedTime(&et);
+ if (debug_mode) printf(" to ");
+ printExplodedTime(&et1);
+ if (debug_mode) printf(".\n");
+ }
+
+ et = et1;
+ }
+ }
+ }
+ if (debug_mode) printf("Test passed\n");
+ }
+
+ /* Same stress test, but with PR_LocalTimeParameters and going backward */
+
+ {
+ PRExplodedTime et, et1, et2;
+ PRInt64 usecPer10Min;
+ int day, hour, min;
+ PRTime usecs;
+ int dstInEffect = 0;
+
+ if (debug_mode) {
+ printf("\n");
+ printf("*******************************************************\n");
+ printf("** **\n");
+ printf("** Stress test **\n");
+ printf("** Starting from midnight Jan. 1, 1997 PST, **\n");
+ printf("** going back four years in 10-minute increment **\n");
+ printf("** **\n");
+ printf("*******************************************************\n\n");
+ }
+
+ LL_I2L(usecPer10Min, 600000000L);
+
+ /* 00:00:00 PST Jan. 1, 1997 */
+ et.tm_usec = 0;
+ et.tm_sec = 0;
+ et.tm_min = 0;
+ et.tm_hour = 0;
+ et.tm_mday = 1;
+ et.tm_month = 0;
+ et.tm_year = 1997;
+ et.tm_params.tp_gmt_offset = -8 * 3600;
+ et.tm_params.tp_dst_offset = 0;
+ usecs = PR_ImplodeTime(&et);
+
+ for (day = 0; day < 4 * 365 + 1; day++) {
+ for (hour = 0; hour < 24; hour++) {
+ for (min = 0; min < 60; min += 10) {
+ LL_SUB(usecs, usecs, usecPer10Min);
+ PR_ExplodeTime(usecs, PR_LocalTimeParameters, &et1);
+
+ et2 = et;
+ et2.tm_usec -= 600000000L;
+ PR_NormalizeTime(&et2, PR_LocalTimeParameters);
+
+ if (!explodedTimeIsEqual(&et1, &et2)) {
+ if (debug_mode) printf("ERROR: componentwise comparison failed\n");
+ printExplodedTime(&et1);
+ if (debug_mode) printf("\n");
+ printExplodedTime(&et2);
+ if (debug_mode) printf("\n");
+ return 1;
+ }
+
+ if (LL_NE(usecs, PR_ImplodeTime(&et1))) {
+ if (debug_mode)
+ printf("ERROR: PR_ExplodeTime and PR_ImplodeTime are not inverse\n");
+ printExplodedTime(&et1);
+ if (debug_mode) printf("\n");
+ failed_already=1;
+ return 1;
+ }
+ testParseTimeString(usecs);
+
+ if (!dstInEffect && et1.tm_params.tp_dst_offset) {
+ dstInEffect = 1;
+ if (debug_mode) printf("DST changeover from ");
+ printExplodedTime(&et);
+ if (debug_mode) printf(" to ");
+ printExplodedTime(&et1);
+ if (debug_mode) printf(".\n");
+ } else if (dstInEffect && !et1.tm_params.tp_dst_offset) {
+ dstInEffect = 0;
+ if (debug_mode) printf("DST changeover from ");
+ printExplodedTime(&et);
+ if (debug_mode) printf(" to ");
+ printExplodedTime(&et1);
+ if (debug_mode) printf(".\n");
+ }
+
+ et = et1;
+ }
+ }
+ }
+ }
+
+ if (failed_already) return 1;
+ else return 0;
+
+}
diff --git a/pr/tests/tmoacc.c b/pr/tests/tmoacc.c
new file mode 100644
index 00000000..f637be5e
--- /dev/null
+++ b/pr/tests/tmoacc.c
@@ -0,0 +1,309 @@
+/* -*- 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 "nspr.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "plerror.h"
+#include "plgetopt.h"
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+#endif
+
+#define BASE_PORT 9867
+#define DEFAULT_THREADS 1
+#define DEFAULT_BACKLOG 10
+#define DEFAULT_TIMEOUT 10
+#define RANDOM_RANGE 100 /* should be significantly smaller than RAND_MAX */
+
+typedef enum {running, stopped} Status;
+
+typedef struct Shared
+{
+ PRLock *ml;
+ PRCondVar *cv;
+ PRBool passed;
+ PRBool random;
+ PRFileDesc *debug;
+ PRIntervalTime timeout;
+ PRFileDesc *listenSock;
+ Status status;
+} Shared;
+
+static PRIntervalTime Timeout(const Shared *shared)
+{
+ PRIntervalTime timeout = shared->timeout;
+ if (shared->random)
+ {
+ PRIntervalTime half = timeout >> 1; /* one half of the interval */
+ PRIntervalTime quarter = half >> 1; /* one quarter of the interval */
+ /* something in [0..timeout / 2) */
+ PRUint32 random = (rand() % RANDOM_RANGE) * half / RANDOM_RANGE;
+ timeout = (3 * quarter) + random; /* [75..125)% */
+ }
+ return timeout;
+} /* Timeout */
+
+static void Accept(void *arg)
+{
+ PRStatus rv;
+ char *buffer = NULL;
+ PRNetAddr clientAddr;
+ Shared *shared = (Shared*)arg;
+ PRInt32 recv_length = 0, flags = 0;
+ PRFileDesc *clientSock;
+ PRIntn toread, byte, bytes, loop = 0;
+ struct Descriptor { PRInt32 length; PRUint32 checksum; } descriptor;
+
+ do
+ {
+ PRUint32 checksum = 0;
+ if (NULL != shared->debug)
+ PR_fprintf(shared->debug, "[%d]accepting ... ", loop++);
+ clientSock = PR_Accept(
+ shared->listenSock, &clientAddr, Timeout(shared));
+ if (clientSock != NULL)
+ {
+ if (NULL != shared->debug)
+ PR_fprintf(shared->debug, "reading length ... ");
+ bytes = PR_Recv(
+ clientSock, &descriptor, sizeof(descriptor),
+ flags, Timeout(shared));
+ if (sizeof(descriptor) == bytes)
+ {
+ /* and, before doing something stupid ... */
+ descriptor.length = PR_ntohl(descriptor.length);
+ descriptor.checksum = PR_ntohl(descriptor.checksum);
+ if (NULL != shared->debug)
+ PR_fprintf(shared->debug, "%d bytes ... ", descriptor.length);
+ toread = descriptor.length;
+ if (recv_length < descriptor.length)
+ {
+ if (NULL != buffer) PR_DELETE(buffer);
+ buffer = (char*)PR_MALLOC(descriptor.length);
+ recv_length = descriptor.length;
+ }
+ for (toread = descriptor.length; toread > 0; toread -= bytes)
+ {
+ bytes = PR_Recv(
+ clientSock, &buffer[descriptor.length - toread],
+ toread, flags, Timeout(shared));
+ if (-1 == bytes)
+ {
+ if (NULL != shared->debug)
+ PR_fprintf(shared->debug, "read data failed...");
+ bytes = 0;
+ }
+ }
+ }
+ else if (NULL != shared->debug)
+ {
+ PR_fprintf(shared->debug, "read desciptor failed...");
+ descriptor.length = -1;
+ }
+ if (NULL != shared->debug)
+ PR_fprintf(shared->debug, "closing");
+ rv = PR_Shutdown(clientSock, PR_SHUTDOWN_BOTH);
+ if ((PR_FAILURE == rv) && (NULL != shared->debug))
+ {
+ PR_fprintf(shared->debug, " failed");
+ shared->passed = PR_FALSE;
+ }
+ rv = PR_Close(clientSock);
+ if (PR_FAILURE == rv) if (NULL != shared->debug)
+ {
+ PR_fprintf(shared->debug, " failed");
+ shared->passed = PR_FALSE;
+ }
+ if (descriptor.length > 0)
+ {
+ for (byte = 0; byte < descriptor.length; ++byte)
+ {
+ PRUint32 overflow = checksum & 0x80000000;
+ checksum = (checksum << 1);
+ if (0x00000000 != overflow) checksum += 1;
+ checksum += buffer[byte];
+ }
+ if ((descriptor.checksum != checksum) && (NULL != shared->debug))
+ {
+ PR_fprintf(shared->debug, " ... data mismatch");
+ shared->passed = PR_FALSE;
+ }
+ }
+ else if (0 == descriptor.length)
+ {
+ PR_Lock(shared->ml);
+ shared->status = stopped;
+ PR_NotifyCondVar(shared->cv);
+ PR_Unlock(shared->ml);
+ }
+ if (NULL != shared->debug)
+ PR_fprintf(shared->debug, "\n");
+ }
+ else
+ {
+ if (PR_PENDING_INTERRUPT_ERROR != PR_GetError())
+ {
+ if (NULL != shared->debug) PL_PrintError("Accept");
+ shared->passed = PR_FALSE;
+ }
+ }
+ } while (running == shared->status);
+ if (NULL != buffer) PR_DELETE(buffer);
+} /* Accept */
+
+PRIntn Tmoacc(PRIntn argc, char **argv)
+{
+ PRStatus rv;
+ PRIntn index;
+ Shared *shared;
+ PLOptStatus os;
+ PRThread **thread;
+ PRNetAddr listenAddr;
+ PRIntn timeout = DEFAULT_TIMEOUT;
+ PRIntn threads = DEFAULT_THREADS;
+ PRIntn backlog = DEFAULT_BACKLOG;
+ PRThreadScope thread_scope = PR_LOCAL_THREAD;
+
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dGb:t:T:R");
+
+#ifdef _PR_INET6
+ PR_SetIPv6Enable(PR_TRUE);
+#endif
+
+ shared = PR_NEWZAP(Shared); /* this is leaked */
+
+ shared->debug = NULL;
+ shared->passed = PR_TRUE;
+ shared->random = PR_TRUE;
+ shared->status = running;
+ shared->ml = PR_NewLock();
+ shared->cv = PR_NewCondVar(shared->ml);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ shared->debug = PR_GetSpecialFD(PR_StandardError);
+ break;
+ case 'G': /* use global threads */
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'b': /* size of listen backlog */
+ backlog = atoi(opt->value);
+ break;
+ case 't': /* number of threads doing accept */
+ threads = atoi(opt->value);
+ break;
+ case 'T': /* timeout used for network operations */
+ timeout = atoi(opt->value);
+ break;
+ case 'R': /* randomize the timeout values */
+ shared->random = PR_TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ if (0 == threads) threads = DEFAULT_THREADS;
+ if (0 == backlog) backlog = DEFAULT_BACKLOG;
+ if (0 == timeout) timeout = DEFAULT_TIMEOUT;
+
+ PR_STDIO_INIT();
+ rv = PR_InitializeNetAddr(PR_IpAddrAny, BASE_PORT, &listenAddr);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ shared->timeout = PR_SecondsToInterval(timeout);
+
+ /* First bind to the socket */
+ shared->listenSock = PR_NewTCPSocket();
+ if (shared->listenSock)
+ {
+ rv = PR_Bind(shared->listenSock, &listenAddr);
+ if (rv != PR_FAILURE)
+ {
+ rv = PR_Listen(shared->listenSock, threads + backlog);
+ if (PR_SUCCESS == rv)
+ {
+ thread = (PRThread**)PR_CALLOC(threads * sizeof(PRThread*));
+ for (index = 0; index < threads; ++index)
+ {
+ thread[index] = PR_CreateThread(
+ PR_USER_THREAD, Accept, shared,
+ PR_PRIORITY_NORMAL, thread_scope,
+ PR_JOINABLE_THREAD, 0);
+ PR_ASSERT(NULL != thread[index]);
+ }
+
+ PR_Lock(shared->ml);
+ while (shared->status == running)
+ PR_WaitCondVar(shared->cv, PR_INTERVAL_NO_TIMEOUT);
+ PR_Unlock(shared->ml);
+ for (index = 0; index < threads; ++index)
+ {
+ rv = PR_Interrupt(thread[index]);
+ PR_ASSERT(PR_SUCCESS== rv);
+ rv = PR_JoinThread(thread[index]);
+ PR_ASSERT(PR_SUCCESS== rv);
+ }
+ PR_DELETE(thread);
+ }
+ else
+ {
+ if (shared->debug) PL_PrintError("Listen");
+ shared->passed = PR_FALSE;
+ }
+ }
+ else
+ {
+ if (shared->debug) PL_PrintError("Bind");
+ shared->passed = PR_FALSE;
+ }
+
+ PR_Close(shared->listenSock);
+ }
+ else
+ {
+ if (shared->debug) PL_PrintError("Create");
+ shared->passed = PR_FALSE;
+ }
+
+ PR_DestroyCondVar(shared->cv);
+ PR_DestroyLock(shared->ml);
+
+ PR_fprintf(
+ PR_GetSpecialFD(PR_StandardError), "%s\n",
+ ((shared->passed) ? "PASSED" : "FAILED"));
+
+ return (shared->passed) ? 0 : 1;
+}
+
+int main(int argc, char **argv)
+{
+ return (PR_VersionCheck(PR_VERSION)) ?
+ PR_Initialize(Tmoacc, argc, argv, 4) : -1;
+} /* main */
+
+/* tmoacc */
diff --git a/pr/tests/tmocon.c b/pr/tests/tmocon.c
new file mode 100644
index 00000000..a850f720
--- /dev/null
+++ b/pr/tests/tmocon.c
@@ -0,0 +1,439 @@
+/* -*- 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.
+ */
+
+/***********************************************************************
+**
+** Name: tmocon.c
+**
+** Description: test client socket connection.
+**
+** Modification History:
+** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
+** The debug mode will print all of the printfs associated with this test.
+** The regress mode will be the default mode. Since the regress tool limits
+** the output to a one line status:PASS or FAIL,all of the printf statements
+** have been handled with an if (debug_mode) statement.
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* Used to get the command line option */
+#include "plgetopt.h"
+
+#include "nspr.h"
+#include "pprio.h"
+
+#include "plerror.h"
+#include "plgetopt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* for getcwd */
+#if defined(XP_UNIX)
+#include <unistd.h>
+#elif defined(XP_PC)
+#include <direct.h>
+#endif
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+#endif
+
+
+#define BASE_PORT 9867
+
+#define DEFAULT_DALLY 1
+#define DEFAULT_THREADS 1
+#define DEFAULT_TIMEOUT 10
+#define DEFAULT_MESSAGES 100
+#define DEFAULT_MESSAGESIZE 100
+
+static PRFileDesc *debug_out = NULL;
+
+typedef struct Shared
+{
+ PRBool random;
+ PRBool failed;
+ PRBool intermittant;
+ PRIntn debug;
+ PRInt32 messages;
+ PRIntervalTime dally;
+ PRIntervalTime timeout;
+ PRInt32 message_length;
+ PRNetAddr serverAddress;
+} Shared;
+
+static PRIntervalTime Timeout(const Shared *shared)
+{
+ PRIntervalTime timeout = shared->timeout;
+ if (shared->random)
+ {
+ PRIntervalTime quarter = timeout >> 2; /* one quarter of the interval */
+ PRUint32 random = rand() % quarter; /* something in[0..timeout / 4) */
+ timeout = (((3 * quarter) + random) >> 2) + quarter; /* [75..125)% */
+ }
+ return timeout;
+} /* Timeout */
+
+static void CauseTimeout(const Shared *shared)
+{
+ if (shared->intermittant) PR_Sleep(Timeout(shared));
+} /* CauseTimeout */
+
+static PRStatus MakeReceiver(Shared *shared)
+{
+ PRStatus rv = PR_FAILURE;
+#if defined(_PR_INET6)
+ if (IN6_IS_ADDR_LOOPBACK(&shared->serverAddress.ipv6.ip))
+#else
+ if (PR_htonl(PR_INADDR_LOOPBACK) == shared->serverAddress.inet.ip)
+#endif
+ {
+ char *argv[3];
+ char path[1024 + sizeof("/tmoacc")];
+ (void)getcwd(path, sizeof(path));
+ (void)strcat(path, "/tmoacc");
+#ifdef XP_PC
+ (void)strcat(path, ".exe");
+#endif
+ argv[0] = path;
+ if (shared->debug > 0)
+ {
+ argv[1] = "-d";
+ argv[2] = NULL;
+ }
+ else argv[1] = NULL;
+ if (shared->debug > 1)
+ PR_fprintf(debug_out, " creating accept process %s ...", path);
+ fflush(stdout);
+ rv = PR_CreateProcessDetached(path, argv, NULL, NULL);
+ if (PR_SUCCESS == rv)
+ {
+ if (shared->debug > 1)
+ PR_fprintf(debug_out, " wait 5 seconds");
+ if (shared->debug > 1)
+ PR_fprintf(debug_out, " before connecting to accept process ...");
+ fflush(stdout);
+ PR_Sleep(PR_SecondsToInterval(5));
+ return rv;
+ }
+ shared->failed = PR_TRUE;
+ if (shared->debug > 0)
+ PL_FPrintError(debug_out, "PR_CreateProcessDetached failed");
+ }
+ return rv;
+} /* MakeReceiver */
+
+static void Connect(void *arg)
+{
+ PRStatus rv;
+ char *buffer = NULL;
+ PRFileDesc *clientSock;
+ Shared *shared = (Shared*)arg;
+ PRInt32 loop, bytes, flags = 0;
+ struct Descriptor { PRInt32 length; PRUint32 checksum; } descriptor;
+ debug_out = (0 == shared->debug) ? NULL : PR_GetSpecialFD(PR_StandardError);
+
+ buffer = (char*)PR_MALLOC(shared->message_length);
+
+ for (bytes = 0; bytes < shared->message_length; ++bytes)
+ buffer[bytes] = (char)bytes;
+
+ descriptor.checksum = 0;
+ for (bytes = 0; bytes < shared->message_length; ++bytes)
+ {
+ PRUint32 overflow = descriptor.checksum & 0x80000000;
+ descriptor.checksum = (descriptor.checksum << 1);
+ if (0x00000000 != overflow) descriptor.checksum += 1;
+ descriptor.checksum += buffer[bytes];
+ }
+ descriptor.checksum = PR_htonl(descriptor.checksum);
+
+ for (loop = 0; loop < shared->messages; ++loop)
+ {
+ if (shared->debug > 1)
+ PR_fprintf(debug_out, "[%d]socket ... ", loop);
+ clientSock = PR_NewTCPSocket();
+ if (clientSock)
+ {
+ /*
+ * We need to slow down the rate of generating connect requests,
+ * otherwise the listen backlog queue on the accept side may
+ * become full and we will get connection refused or timeout
+ * error.
+ */
+
+ PR_Sleep(shared->dally);
+ if (shared->debug > 1) PR_fprintf(debug_out, "connecting ... ");
+ rv = PR_Connect(
+ clientSock, &shared->serverAddress, Timeout(shared));
+ if (PR_SUCCESS == rv)
+ {
+ PRInt32 descriptor_length = (loop < (shared->messages - 1)) ?
+ shared->message_length : 0;
+ descriptor.length = PR_htonl(descriptor_length);
+ if (shared->debug > 1)
+ PR_fprintf(
+ debug_out, "sending %d bytes ... ", descriptor_length);
+ CauseTimeout(shared); /* might cause server to timeout */
+ bytes = PR_Send(
+ clientSock, &descriptor, sizeof(descriptor),
+ flags, Timeout(shared));
+ if (bytes != sizeof(descriptor))
+ {
+ shared->failed = PR_TRUE;
+ if (shared->debug > 0)
+ PL_FPrintError(debug_out, "PR_Send failed");
+ }
+ if (0 != descriptor_length)
+ {
+ CauseTimeout(shared);
+ bytes = PR_Send(
+ clientSock, buffer, descriptor_length,
+ flags, Timeout(shared));
+ if (bytes != descriptor_length)
+ {
+ shared->failed = PR_TRUE;
+ if (shared->debug > 0)
+ PL_FPrintError(debug_out, "PR_Send failed");
+ }
+ }
+ if (shared->debug > 1) PR_fprintf(debug_out, "closing ... ");
+ rv = PR_Shutdown(clientSock, PR_SHUTDOWN_BOTH);
+ rv = PR_Close(clientSock);
+ if (shared->debug > 1)
+ {
+ if (PR_SUCCESS == rv) PR_fprintf(debug_out, "\n");
+ else PL_FPrintError(debug_out, "shutdown failed");
+ }
+ }
+ else
+ {
+ if (shared->debug > 1) PL_FPrintError(debug_out, "connect failed");
+ PR_Close(clientSock);
+ if ((loop == 0) && (PR_GetError() == PR_CONNECT_REFUSED_ERROR))
+ {
+ if (MakeReceiver(shared) == PR_FAILURE) break;
+ }
+ else
+ {
+ if (shared->debug > 1) PR_fprintf(debug_out, " exiting\n");
+ break;
+ }
+ }
+ }
+ else
+ {
+ shared->failed = PR_TRUE;
+ if (shared->debug > 0) PL_FPrintError(debug_out, "create socket");
+ break;
+ }
+ }
+
+ PR_DELETE(buffer);
+} /* Connect */
+
+#ifdef _PR_INET6
+
+/*
+ * Below are some of the IPv6 hosts at Netscape and their IPv4
+ * addresses. We will use their IPv4-compatible IPv6 addresses
+ * for automatic tunneling.
+ */
+
+static const char *ipv6_host[] = {
+ "dijkstra",
+ "wirth",
+ "gandalf",
+ "raven"
+};
+
+static const unsigned char ipv6_host_ipv4_addr[] = {
+ 208, 12, 62, 49,
+ 208, 12, 62, 98,
+ 208, 12, 62, 55,
+ 206, 222, 228, 80
+};
+
+/*
+ * Return PR_TRUE if 'host' is an IPv6 host at Netscape in the table
+ * above, and fill 'addr->ipv6.ip' with its IPv4-compatible IPv6 address
+ * for automatic tunneling.
+ */
+
+static PRBool
+IPv6AutoTunnelHost(const char *host, PRNetAddr *addr)
+{
+ int nHosts = sizeof(ipv6_host) / sizeof(ipv6_host[0]);
+ int i;
+
+ for (i = 0; i < nHosts; i++)
+ {
+ if (strcmp(host, ipv6_host[i]) == 0)
+ {
+ memset(&addr->ipv6.ip, 0, 12);
+ memcpy(((unsigned char *) &addr->ipv6.ip) + 12,
+ &ipv6_host_ipv4_addr[4 * i], 4);
+ PR_ASSERT(IN6_IS_ADDR_V4COMPAT(&addr->ipv6.ip));
+ return PR_TRUE;
+ }
+ }
+ return PR_FALSE;
+}
+#endif /* _PR_INET6 */
+
+int Tmocon(int argc, char **argv)
+{
+ /*
+ * USAGE
+ * -d turn on debugging output (default = off)
+ * -v turn on verbose output (default = off)
+ * -h <n> dns name of host serving the connection (default = self)
+ * -i dally intermittantly to cause timeouts (default = off)
+ * -m <n> number of messages to send (default = 100)
+ * -s <n> size of each message (default = 100)
+ * -t <n> number of threads sending (default = 1)
+ * -G use global threads (default = local)
+ * -T <n> timeout on I/O operations (seconds) (default = 10)
+ * -D <n> dally between connect requests (seconds)(default = 0)
+ * -R randomize the dally types around 'T' (default = no)
+ */
+
+ PRStatus rv;
+ PLOptStatus os;
+ Shared *shared = NULL;
+ PRThread **thread = NULL;
+ PRIntn index, threads = DEFAULT_THREADS;
+ PRThreadScope thread_scope = PR_LOCAL_THREAD;
+ PRInt32 dally = DEFAULT_DALLY, timeout = DEFAULT_TIMEOUT;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "divGRh:m:s:t:T:D:");
+
+#ifdef _PR_INET6
+ PR_SetIPv6Enable(PR_TRUE);
+#endif
+ shared = PR_NEWZAP(Shared); /* this is leaked */
+
+ shared->debug = 0;
+ shared->failed = PR_FALSE;
+ shared->random = PR_FALSE;
+ shared->messages = DEFAULT_MESSAGES;
+ shared->message_length = DEFAULT_MESSAGESIZE;
+
+ PR_STDIO_INIT();
+ rv = PR_InitializeNetAddr(PR_IpAddrLoopback, BASE_PORT, &shared->serverAddress);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd':
+ if (0 == shared->debug) shared->debug = 1;
+ break;
+ case 'v':
+ if (0 == shared->debug) shared->debug = 2;
+ break;
+ case 'i':
+ shared->intermittant = PR_TRUE;
+ break;
+ case 'R':
+ shared->random = PR_TRUE;
+ break;
+ case 'G':
+ thread_scope = PR_GLOBAL_THREAD;
+ break;
+ case 'h': /* the value for backlock */
+ {
+#ifdef _PR_INET6
+ if (!IPv6AutoTunnelHost(opt->value, &shared->serverAddress))
+ {
+#endif
+ PRIntn es = 0;
+ PRHostEnt host;
+ char buffer[1024];
+ (void)PR_GetHostByName(
+ opt->value, buffer, sizeof(buffer), &host);
+ es = PR_EnumerateHostEnt(
+ es, &host, BASE_PORT, &shared->serverAddress);
+ PR_ASSERT(es > 0);
+#ifdef _PR_INET6
+ }
+#endif
+ }
+ break;
+ case 'm': /* number of messages to send */
+ shared->messages = atoi(opt->value);
+ break;
+ case 't': /* number of threads sending */
+ threads = atoi(opt->value);
+ break;
+ case 'D': /* dally time between transmissions */
+ dally = atoi(opt->value);
+ break;
+ case 'T': /* timeout on I/O operations */
+ timeout = atoi(opt->value);
+ break;
+ case 's': /* total size of each message */
+ shared->message_length = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (0 == timeout) timeout = DEFAULT_TIMEOUT;
+ if (0 == threads) threads = DEFAULT_THREADS;
+ if (0 == shared->messages) shared->messages = DEFAULT_MESSAGES;
+ if (0 == shared->message_length) shared->message_length = DEFAULT_MESSAGESIZE;
+
+ shared->dally = PR_SecondsToInterval(dally);
+ shared->timeout = PR_SecondsToInterval(timeout);
+
+ thread = (PRThread**)PR_CALLOC(threads * sizeof(PRThread*));
+
+ for (index = 0; index < threads; ++index)
+ thread[index] = PR_CreateThread(
+ PR_USER_THREAD, Connect, shared,
+ PR_PRIORITY_NORMAL, thread_scope,
+ PR_JOINABLE_THREAD, 0);
+ for (index = 0; index < threads; ++index)
+ rv = PR_JoinThread(thread[index]);
+
+ PR_DELETE(thread);
+
+ PR_fprintf(
+ PR_GetSpecialFD(PR_StandardError), "%s\n",
+ ((shared->failed) ? "FAILED" : "PASSED"));
+ return (shared->failed) ? 1 : 0;
+}
+
+int main(int argc, char **argv)
+{
+ return (PR_VersionCheck(PR_VERSION)) ?
+ PR_Initialize(Tmocon, argc, argv, 4) : -1;
+} /* main */
+
+/* tmocon.c */
+
+
diff --git a/pr/tests/tpd.c b/pr/tests/tpd.c
new file mode 100644
index 00000000..786948e4
--- /dev/null
+++ b/pr/tests/tpd.c
@@ -0,0 +1,317 @@
+/* -*- 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: tpd.c
+** Description: Exercising the thread private data bailywick.
+*/
+
+#include "prmem.h"
+#include "prinit.h"
+#include "prlog.h"
+#include "prprf.h"
+#include "prthread.h"
+#include "prtypes.h"
+
+#if defined(XP_MAC)
+#include "pprio.h"
+#else
+#include "private/pprio.h"
+#endif
+
+#include "plgetopt.h"
+
+static PRUintn key[128];
+static PRIntn debug = 0;
+static PRBool failed = PR_FALSE;
+static PRBool should = PR_TRUE;
+static PRBool did = PR_TRUE;
+static PRFileDesc *fout = NULL;
+
+static void PrintProgress(PRIntn line)
+{
+ failed = failed || (should && !did);
+ failed = failed || (!should && did);
+ if (debug > 0)
+ {
+#if defined(WIN16)
+ printf(
+ "@ line %d destructor should%s have been called and was%s\n",
+ line, ((should) ? "" : " NOT"), ((did) ? "" : " NOT"));
+#else
+ PR_fprintf(
+ fout, "@ line %d destructor should%s have been called and was%s\n",
+ line, ((should) ? "" : " NOT"), ((did) ? "" : " NOT"));
+#endif
+ }
+} /* PrintProgress */
+
+static void MyAssert(const char *expr, const char *file, PRIntn line)
+{
+ if (debug > 0)
+ (void)PR_fprintf(fout, "'%s' in file: %s: %d\n", expr, file, line);
+} /* MyAssert */
+
+#define MY_ASSERT(_expr) \
+ ((_expr)?((void)0):MyAssert(# _expr,__FILE__,__LINE__))
+
+
+static void PR_CALLBACK Destructor(void *data)
+{
+ MY_ASSERT(NULL != data);
+ if (should) did = PR_TRUE;
+ else failed = PR_TRUE;
+ /*
+ * We don't actually free the storage since it's actually allocated
+ * on the stack. Normally, this would not be the case and this is
+ * the opportunity to free whatever.
+ PR_Free(data);
+ */
+} /* Destructor */
+
+static void PR_CALLBACK Thread(void *null)
+{
+ void *pd;
+ PRStatus rv;
+ PRUintn keys;
+ char *key_string[] = {
+ "Key #0", "Key #1", "Key #2", "Key #3",
+ "Bogus #5", "Bogus #6", "Bogus #7", "Bogus #8"};
+
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 8; ++keys)
+ {
+ pd = PR_GetThreadPrivate(key[keys]);
+ MY_ASSERT(NULL == pd);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], key_string[keys]);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+#if !defined(DEBUG)
+ did = should = PR_FALSE;
+ for (keys = 4; keys < 8; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], key_string[keys]);
+ MY_ASSERT(PR_FAILURE == rv);
+ }
+ PrintProgress(__LINE__);
+#endif
+
+ did = PR_FALSE; should = PR_TRUE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], key_string[keys]);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = PR_FALSE; should = PR_TRUE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], NULL);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], NULL);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 8; keys < 127; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], "EXTENSION");
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = PR_FALSE; should = PR_TRUE;
+ for (keys = 8; keys < 127; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], NULL);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 8; keys < 127; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], NULL);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+
+ /* put in keys and leave them there for thread exit */
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], key_string[keys]);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+ did = PR_FALSE; should = PR_TRUE;
+
+} /* Thread */
+
+static PRIntn PR_CALLBACK Tpd(PRIntn argc, char **argv)
+{
+ void *pd;
+ PRStatus rv;
+ PRUintn keys;
+ PRThread *thread;
+ char *key_string[] = {
+ "Key #0", "Key #1", "Key #2", "Key #3",
+ "Bogus #5", "Bogus #6", "Bogus #7", "Bogus #8"};
+
+ fout = PR_STDOUT;
+
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = PR_NewThreadPrivateIndex(&key[keys], Destructor);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 8; ++keys)
+ {
+ pd = PR_GetThreadPrivate(key[keys]);
+ MY_ASSERT(NULL == pd);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], key_string[keys]);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+#if !defined(DEBUG)
+ did = should = PR_FALSE;
+ for (keys = 4; keys < 8; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], key_string[keys]);
+ MY_ASSERT(PR_FAILURE == rv);
+ }
+ PrintProgress(__LINE__);
+#endif
+
+ did = PR_FALSE; should = PR_TRUE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], key_string[keys]);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = PR_FALSE; should = PR_TRUE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], NULL);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 0; keys < 4; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], NULL);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 8; keys < 127; ++keys)
+ {
+ rv = PR_NewThreadPrivateIndex(&key[keys], Destructor);
+ MY_ASSERT(PR_SUCCESS == rv);
+ rv = PR_SetThreadPrivate(key[keys], "EXTENSION");
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = PR_FALSE; should = PR_TRUE;
+ for (keys = 8; keys < 127; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], NULL);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+ PrintProgress(__LINE__);
+
+ did = should = PR_FALSE;
+ for (keys = 8; keys < 127; ++keys)
+ {
+ rv = PR_SetThreadPrivate(key[keys], NULL);
+ MY_ASSERT(PR_SUCCESS == rv);
+ }
+
+ thread = PR_CreateThread(
+ PR_USER_THREAD, Thread, NULL, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+
+ (void)PR_JoinThread(thread);
+
+ PrintProgress(__LINE__);
+
+#if defined(WIN16)
+ printf(
+ "%s\n",((PR_TRUE == failed) ? "FAILED" : "PASSED"));
+#else
+ (void)PR_fprintf(
+ fout, "%s\n",((PR_TRUE == failed) ? "FAILED" : "PASSED"));
+#endif
+
+ return 0;
+
+} /* Tpd */
+
+PRIntn main(PRIntn argc, char *argv[])
+{
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dl:r:");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug = PR_TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+ PR_STDIO_INIT();
+ return PR_Initialize(Tpd, argc, argv, 0);
+} /* main */
+
+/* tpd.c */
diff --git a/pr/tests/udpsrv.c b/pr/tests/udpsrv.c
new file mode 100644
index 00000000..d45421d4
--- /dev/null
+++ b/pr/tests/udpsrv.c
@@ -0,0 +1,547 @@
+/* -*- 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.
+ */
+
+/*******************************************************************
+** udpsrc.c -- Test basic function of UDP server
+**
+** udpsrv operates on the same machine with program udpclt.
+** udpsrv is the server side of a udp sockets application.
+** udpclt is the client side of a udp sockets application.
+**
+** The test is designed to assist developers in porting/debugging
+** the UDP socket functions of NSPR.
+**
+** This test is not a stress test.
+**
+** main() starts two threads: UDP_Server() and UDP_Client();
+** main() uses PR_JoinThread() to wait for the threads to complete.
+**
+** UDP_Server() does repeated recvfrom()s from a socket.
+** He detects an EOF condition set by UDP_Client(). For each
+** packet received by UDP_Server(), he checks its content for
+** expected content, then sends the packet back to UDP_Client().
+**
+** UDP_Client() sends packets to UDP_Server() using sendto()
+** he recieves packets back from the server via recvfrom().
+** After he sends enough packets containing UDP_AMOUNT_TO_WRITE
+** bytes of data, he sends an EOF message.
+**
+** The test issues a pass/fail message at end.
+**
+** Notes:
+** The variable "_debug_on" can be set to 1 to cause diagnostic
+** messages related to client/server synchronization. Useful when
+** the test hangs.
+**
+** Error messages are written to stdout.
+**
+********************************************************************
+*/
+/* --- include files --- */
+#include "nspr.h"
+#include "prpriv.h"
+
+#include "plgetopt.h"
+#include "prttools.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef XP_PC
+#define mode_t int
+#endif
+
+#define UDP_BUF_SIZE 4096
+#define UDP_DGRAM_SIZE 128
+#define UDP_AMOUNT_TO_WRITE (PRInt32)((UDP_DGRAM_SIZE * 1000l) +1)
+#define NUM_UDP_CLIENTS 1
+#define NUM_UDP_DATAGRAMS_PER_CLIENT 5
+#define UDP_SERVER_PORT 9050
+#define UDP_CLIENT_PORT 9053
+#define MY_INADDR PR_INADDR_ANY
+#define PEER_INADDR PR_INADDR_LOOPBACK
+
+#define UDP_TIMEOUT 400000
+/* #define UDP_TIMEOUT PR_INTERVAL_NO_TIMEOUT */
+
+/* --- static data --- */
+static PRIntn _debug_on = 0;
+static PRBool passed = PR_TRUE;
+static PRUint32 cltBytesRead = 0;
+static PRUint32 srvBytesRead = 0;
+static PRFileDesc *output = NULL;
+
+/* --- static function declarations --- */
+#define DPRINTF(arg) if (_debug_on) PR_fprintf(output, arg)
+
+
+
+/*******************************************************************
+** ListNetAddr() -- Display the Net Address on stdout
+**
+** Description: displays the component parts of a PRNetAddr struct
+**
+** Arguments: address of PRNetAddr structure to display
+**
+** Returns: void
+**
+** Notes:
+**
+********************************************************************
+*/
+void ListNetAddr( char *msg, PRNetAddr *na )
+{
+ char mbuf[256];
+
+ sprintf( mbuf, "ListNetAddr: %s family: %d, port: %d, ip: %8.8X\n",
+ msg, na->inet.family, PR_ntohs( na->inet.port), PR_ntohl(na->inet.ip) );
+#if 0
+ DPRINTF( mbuf );
+#endif
+} /* --- end ListNetAddr() --- */
+
+/********************************************************************
+** UDP_Server() -- Test a UDP server application
+**
+** Description: The Server side of a UDP Client/Server application.
+**
+** Arguments: none
+**
+** Returns: void
+**
+** Notes:
+**
+**
+********************************************************************
+*/
+static void PR_CALLBACK UDP_Server( void *arg )
+{
+ static char svrBuf[UDP_BUF_SIZE];
+ PRFileDesc *svrSock;
+ PRInt32 rv;
+ PRNetAddr netaddr;
+ PRBool bound = PR_FALSE;
+ PRBool endOfInput = PR_FALSE;
+ PRInt32 numBytes = UDP_DGRAM_SIZE;
+
+ DPRINTF("udpsrv: UDP_Server(): starting\n" );
+
+ /* --- Create the socket --- */
+ DPRINTF("udpsrv: UDP_Server(): Creating UDP Socket\n" );
+ svrSock = PR_NewUDPSocket();
+ if ( svrSock == NULL )
+ {
+ passed = PR_FALSE;
+ if (debug_mode)
+ PR_fprintf(output,
+ "udpsrv: UDP_Server(): PR_NewUDPSocket() returned NULL\n" );
+ return;
+ }
+
+ /* --- Initialize the sockaddr_in structure --- */
+ memset( &netaddr, 0, sizeof( netaddr ));
+ netaddr.inet.family = PR_AF_INET;
+ netaddr.inet.port = PR_htons( UDP_SERVER_PORT );
+ netaddr.inet.ip = PR_htonl( MY_INADDR );
+
+ /* --- Bind the socket --- */
+ while ( !bound )
+ {
+ DPRINTF("udpsrv: UDP_Server(): Binding socket\n" );
+ rv = PR_Bind( svrSock, &netaddr );
+ if ( rv < 0 )
+ {
+ if ( PR_GetError() == PR_ADDRESS_IN_USE_ERROR )
+ {
+ if (debug_mode) PR_fprintf(output, "udpsrv: UDP_Server(): \
+ PR_Bind(): reports: PR_ADDRESS_IN_USE_ERROR\n");
+ PR_Sleep( PR_MillisecondsToInterval( 2000 ));
+ continue;
+ }
+ else
+ {
+ passed = PR_FALSE;
+ if (debug_mode) PR_fprintf(output, "udpsrv: UDP_Server(): \
+ PR_Bind(): failed: %ld with error: %ld\n",
+ rv, PR_GetError() );
+ PR_Close( svrSock );
+ return;
+ }
+ }
+ else
+ bound = PR_TRUE;
+ }
+ ListNetAddr( "UDP_Server: after bind", &netaddr );
+
+ /* --- Recv the socket --- */
+ while( !endOfInput )
+ {
+ DPRINTF("udpsrv: UDP_Server(): RecvFrom() socket\n" );
+ rv = PR_RecvFrom( svrSock, svrBuf, numBytes, 0, &netaddr, UDP_TIMEOUT );
+ if ( rv == -1 )
+ {
+ passed = PR_FALSE;
+ if (debug_mode)
+ PR_fprintf(output,
+ "udpsrv: UDP_Server(): PR_RecvFrom(): failed with error: %ld\n",
+ PR_GetError() );
+ PR_Close( svrSock );
+ return;
+ }
+ ListNetAddr( "UDP_Server after RecvFrom", &netaddr );
+
+ srvBytesRead += rv;
+
+ if ( svrBuf[0] == 'E' )
+ {
+ DPRINTF("udpsrv: UDP_Server(): EOF on input detected\n" );
+ endOfInput = PR_TRUE;
+ }
+
+ /* --- Send the socket --- */
+ DPRINTF("udpsrv: UDP_Server(): SendTo(): socket\n" );
+ rv = PR_SendTo( svrSock, svrBuf, rv, 0, &netaddr, PR_INTERVAL_NO_TIMEOUT );
+ if ( rv == -1 )
+ {
+ passed = PR_FALSE;
+ if (debug_mode)
+ PR_fprintf(output,
+ "udpsrv: UDP_Server(): PR_SendTo(): failed with error: %ld\n",
+ PR_GetError() );
+ PR_Close( svrSock );
+ return;
+ }
+ ListNetAddr( "UDP_Server after SendTo", &netaddr );
+ }
+
+ /* --- Close the socket --- */
+ DPRINTF("udpsrv: UDP_Server(): Closing socket\n" );
+ rv = PR_Close( svrSock );
+ if ( rv != PR_SUCCESS )
+ {
+ passed = PR_FALSE;
+ if (debug_mode)
+ PR_fprintf(output,
+ "udpsrv: UDP_Server(): PR_Close(): failed to close socket\n" );
+ return;
+ }
+
+ DPRINTF("udpsrv: UDP_Server(): Normal end\n" );
+} /* --- end UDP_Server() --- */
+
+
+static char cltBuf[UDP_BUF_SIZE];
+static char cltBufin[UDP_BUF_SIZE];
+/********************************************************************
+** UDP_Client() -- Test a UDP client application
+**
+** Description:
+**
+** Arguments:
+**
+**
+** Returns:
+** 0 -- Successful execution
+** 1 -- Test failed.
+**
+** Notes:
+**
+**
+********************************************************************
+*/
+static void PR_CALLBACK UDP_Client( void *arg )
+{
+ PRFileDesc *cltSock;
+ PRInt32 rv;
+ PRBool bound = PR_FALSE;
+ PRNetAddr netaddr;
+ PRNetAddr netaddrx;
+ PRBool endOfInput = PR_FALSE;
+ PRInt32 numBytes = UDP_DGRAM_SIZE;
+ PRInt32 writeThisMany = UDP_AMOUNT_TO_WRITE;
+ int i;
+
+
+ DPRINTF("udpsrv: UDP_Client(): starting\n" );
+
+ /* --- Create the socket --- */
+ cltSock = PR_NewUDPSocket();
+ if ( cltSock == NULL )
+ {
+ passed = PR_FALSE;
+ if (debug_mode)
+ PR_fprintf(output,
+ "udpsrv: UDP_Client(): PR_NewUDPSocket() returned NULL\n" );
+ return;
+ }
+
+ /* --- Initialize the sockaddr_in structure --- */
+ memset( &netaddr, 0, sizeof( netaddr ));
+ netaddr.inet.family = PR_AF_INET;
+ netaddr.inet.ip = PR_htonl( MY_INADDR );
+ netaddr.inet.port = PR_htons( UDP_CLIENT_PORT );
+
+ /* --- Initialize the write buffer --- */
+ for ( i = 0; i < UDP_BUF_SIZE ; i++ )
+ cltBuf[i] = i;
+
+ /* --- Bind the socket --- */
+ while ( !bound )
+ {
+ DPRINTF("udpsrv: UDP_Client(): Binding socket\n" );
+ rv = PR_Bind( cltSock, &netaddr );
+ if ( rv < 0 )
+ {
+ if ( PR_GetError() == PR_ADDRESS_IN_USE_ERROR )
+ {
+ if (debug_mode)
+ PR_fprintf(output,
+ "udpsrv: UDP_Client(): PR_Bind(): reports: PR_ADDRESS_IN_USE_ERROR\n");
+ PR_Sleep( PR_MillisecondsToInterval( 2000 ));
+ continue;
+ }
+ else
+ {
+ passed = PR_FALSE;
+ if (debug_mode)
+ PR_fprintf(output,
+ "udpsrv: UDP_Client(): PR_Bind(): failed: %ld with error: %ld\n",
+ rv, PR_GetError() );
+ PR_Close( cltSock );
+ return;
+ }
+ }
+ else
+ bound = PR_TRUE;
+ }
+ ListNetAddr( "UDP_Client after Bind", &netaddr );
+
+ /* --- Initialize the sockaddr_in structure --- */
+ memset( &netaddr, 0, sizeof( netaddr ));
+ netaddr.inet.family = PR_AF_INET;
+ netaddr.inet.ip = PR_htonl( PEER_INADDR );
+ netaddr.inet.port = PR_htons( UDP_SERVER_PORT );
+
+ /* --- send and receive packets until no more data left */
+ while( !endOfInput )
+ {
+ /*
+ ** Signal EOF in the data stream on the last packet
+ */
+ if ( writeThisMany <= UDP_DGRAM_SIZE )
+ {
+ DPRINTF("udpsrv: UDP_Client(): Send EOF packet\n" );
+ cltBuf[0] = 'E';
+ endOfInput = PR_TRUE;
+ }
+
+ /* --- SendTo the socket --- */
+ if ( writeThisMany > UDP_DGRAM_SIZE )
+ numBytes = UDP_DGRAM_SIZE;
+ else
+ numBytes = writeThisMany;
+ writeThisMany -= numBytes;
+ {
+ char mbuf[256];
+ sprintf( mbuf, "udpsrv: UDP_Client(): write_this_many: %d, numbytes: %d\n",
+ writeThisMany, numBytes );
+ DPRINTF( mbuf );
+ }
+
+ DPRINTF("udpsrv: UDP_Client(): SendTo(): socket\n" );
+ rv = PR_SendTo( cltSock, cltBuf, numBytes, 0, &netaddr, UDP_TIMEOUT );
+ if ( rv == -1 )
+ {
+ passed = PR_FALSE;
+ if (debug_mode)
+ PR_fprintf(output,
+ "udpsrv: UDP_Client(): PR_SendTo(): failed with error: %ld\n",
+ PR_GetError() );
+ PR_Close( cltSock );
+ return;
+ }
+ ListNetAddr( "UDP_Client after SendTo", &netaddr );
+
+ /* --- RecvFrom the socket --- */
+ memset( cltBufin, 0, UDP_BUF_SIZE );
+ DPRINTF("udpsrv: UDP_Client(): RecvFrom(): socket\n" );
+ rv = PR_RecvFrom( cltSock, cltBufin, numBytes, 0, &netaddrx, UDP_TIMEOUT );
+ if ( rv == -1 )
+ {
+ passed = PR_FALSE;
+ if (debug_mode) PR_fprintf(output,
+ "udpsrv: UDP_Client(): PR_RecvFrom(): failed with error: %ld\n",
+ PR_GetError() );
+ PR_Close( cltSock );
+ return;
+ }
+ ListNetAddr( "UDP_Client after RecvFrom()", &netaddr );
+ cltBytesRead += rv;
+
+ /* --- verify buffer --- */
+ for ( i = 0; i < rv ; i++ )
+ {
+ if ( cltBufin[i] != i )
+ {
+ /* --- special case, end of input --- */
+ if ( endOfInput && i == 0 && cltBufin[0] == 'E' )
+ continue;
+ passed = PR_FALSE;
+ if (debug_mode) PR_fprintf(output,
+ "udpsrv: UDP_Client(): return data mismatch\n" );
+ PR_Close( cltSock );
+ return;
+ }
+ }
+ if (debug_mode) PR_fprintf(output, ".");
+ }
+
+ /* --- Close the socket --- */
+ DPRINTF("udpsrv: UDP_Server(): Closing socket\n" );
+ rv = PR_Close( cltSock );
+ if ( rv != PR_SUCCESS )
+ {
+ passed = PR_FALSE;
+ if (debug_mode) PR_fprintf(output,
+ "udpsrv: UDP_Client(): PR_Close(): failed to close socket\n" );
+ return;
+ }
+ DPRINTF("udpsrv: UDP_Client(): ending\n" );
+} /* --- end UDP_Client() --- */
+
+/********************************************************************
+** main() -- udpsrv
+**
+** arguments:
+**
+** Returns:
+** 0 -- Successful execution
+** 1 -- Test failed.
+**
+** Description:
+**
+** Standard test case setup.
+**
+** Calls the function UDP_Server()
+**
+********************************************************************
+*/
+
+int main(int argc, char **argv)
+{
+ PRThread *srv, *clt;
+/* The command line argument: -d is used to determine if the test is being run
+ in debug mode. The regress tool requires only one line output:PASS or FAIL.
+ All of the printfs associated with this test has been handled with a if (debug_mode)
+ test.
+ Usage: test_name -d -v
+ */
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dv");
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode */
+ debug_mode = 1;
+ break;
+ case 'v': /* verbose mode */
+ _debug_on = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PR_STDIO_INIT();
+ output = PR_STDERR;
+
+#ifdef XP_MAC
+ SetupMacPrintfLog("udpsrv.log");
+#endif
+
+ PR_SetConcurrency(4);
+
+ /*
+ ** Create the Server thread
+ */
+ DPRINTF( "udpsrv: Creating Server Thread\n" );
+ srv = PR_CreateThread( PR_USER_THREAD,
+ UDP_Server,
+ (void *) 0,
+ PR_PRIORITY_LOW,
+ PR_LOCAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0 );
+ if ( srv == NULL )
+ {
+ if (debug_mode) PR_fprintf(output, "udpsrv: Cannot create server thread\n" );
+ passed = PR_FALSE;
+ }
+
+ /*
+ ** Give the Server time to Start
+ */
+ DPRINTF( "udpsrv: Pausing to allow Server to start\n" );
+ PR_Sleep( PR_MillisecondsToInterval(200) );
+
+ /*
+ ** Create the Client thread
+ */
+ DPRINTF( "udpsrv: Creating Client Thread\n" );
+ clt = PR_CreateThread( PR_USER_THREAD,
+ UDP_Client,
+ (void *) 0,
+ PR_PRIORITY_LOW,
+ PR_LOCAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0 );
+ if ( clt == NULL )
+ {
+ if (debug_mode) PR_fprintf(output, "udpsrv: Cannot create server thread\n" );
+ passed = PR_FALSE;
+ }
+
+ /*
+ **
+ */
+ DPRINTF("udpsrv: Waiting to join Server & Client Threads\n" );
+ PR_JoinThread( srv );
+ PR_JoinThread( clt );
+
+ /*
+ ** Evaluate test results
+ */
+ if (debug_mode) PR_fprintf(output, "\n\nudpsrv: main(): cltBytesRead(%ld), \
+ srvBytesRead(%ld), expected(%ld)\n",
+ cltBytesRead, srvBytesRead, UDP_AMOUNT_TO_WRITE );
+ if ( cltBytesRead != srvBytesRead || cltBytesRead != UDP_AMOUNT_TO_WRITE )
+ {
+ passed = PR_FALSE;
+ }
+ PR_Cleanup();
+ if ( passed )
+ return 0;
+ else
+ return 1;
+} /* --- end main() --- */
diff --git a/pr/tests/ut_ttools.h b/pr/tests/ut_ttools.h
new file mode 100644
index 00000000..236a8b39
--- /dev/null
+++ b/pr/tests/ut_ttools.h
@@ -0,0 +1,24 @@
+/* -*- 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.
+ */
+
+/* Used in Regress Tool */
+#define NOSTATUS 2
+#define PASS 1
+#define FAIL 0
+
+PRIntn debug_mode=0;
diff --git a/pr/tests/version.c b/pr/tests/version.c
new file mode 100644
index 00000000..c5c3013d
--- /dev/null
+++ b/pr/tests/version.c
@@ -0,0 +1,101 @@
+/* -*- 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 "prio.h"
+#include "prprf.h"
+#include "prlink.h"
+#include "prvrsion.h"
+
+#include "plerror.h"
+#include "plgetopt.h"
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PRIntn rv = 1;
+ PLOptStatus os;
+ PRIntn verbosity = 0;
+ PRLibrary *runtime = NULL;
+ const char *library_name = NULL;
+ const PRVersionDescription *version_info;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "d");
+
+ PRFileDesc *err = PR_GetSpecialFD(PR_StandardError);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 0: /* fully qualified library name */
+ library_name = opt->value;
+ break;
+ case 'd': /* verbodity */
+ verbosity += 1;
+ break;
+ default:
+ PR_fprintf(err, "Usage: version [-d] {fully qualified library name}\n");
+ return 2; /* but not a lot else */
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (NULL == library_name)
+ PR_fprintf(err, "Usage: version [-d] {fully qualified library name}\n");
+ else
+ {
+ runtime = PR_LoadLibrary(library_name);
+ if (NULL == runtime) PL_FPrintError(err, "PR_LoadLibrary");
+ else
+ {
+ versionEntryPointType versionPoint = (versionEntryPointType)
+ PR_FindSymbol(runtime, "libVersionPoint");
+ if (NULL == versionPoint) PL_FPrintError(err, "PR_FindSymbol");
+ else
+ {
+ char buffer[100];
+ PRExplodedTime exploded;
+ version_info = versionPoint();
+ (void)PR_fprintf(err, "Runtime library version information\n");
+ PR_ExplodeTime(
+ version_info->buildTime, PR_GMTParameters, &exploded);
+ (void)PR_FormatTime(
+ buffer, sizeof(buffer), "%d %b %Y %H:%M:%S", &exploded);
+ (void)PR_fprintf(err, " Build time: %s GMT\n", buffer);
+ (void)PR_fprintf(
+ err, " Build time: %s\n", version_info->buildTimeString);
+ (void)PR_fprintf(
+ err, " %s V%u.%u.%u (%s%s%s)\n",
+ version_info->description,
+ version_info->vMajor,
+ version_info->vMinor,
+ version_info->vPatch,
+ (version_info->beta ? " beta " : ""),
+ (version_info->debug ? " debug " : ""),
+ (version_info->special ? " special" : ""));
+ (void)PR_fprintf(err, " filename: %s\n", version_info->filename);
+ (void)PR_fprintf(err, " security: %s\n", version_info->security);
+ (void)PR_fprintf(err, " copyright: %s\n", version_info->copyright);
+ (void)PR_fprintf(err, " comment: %s\n", version_info->comment);
+ rv = 0;
+ }
+ }
+ }
+ return rv;
+}
+
+/* version.c */
diff --git a/pr/tests/w16gui/Makefile b/pr/tests/w16gui/Makefile
new file mode 100644
index 00000000..866f9970
--- /dev/null
+++ b/pr/tests/w16gui/Makefile
@@ -0,0 +1,76 @@
+#
+# 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
+
+INCLUDES = -I$(DIST)/include
+W16STDIO = $(MOD_DEPTH)/pr/src/md/windows/$(OBJDIR)/w16stdio.$(OBJ_SUFFIX)
+
+CSRCS = poppad.c \
+ popfile.c \
+ popfont.c \
+ popfind.c \
+ popprnt0.c
+
+
+INCLUDES = -I$(DIST)/include/private -I$(DIST)/include
+LIBPR = $(DIST)/lib/nspr$(MOD_VERSION).lib
+LIBPLDS = $(DIST)/lib/plds$(MOD_VERSION).lib
+TARGETS = $(OBJDIR)/poppad.exe
+OS_CFLAGS = $(OS_EXE_CFLAGS)
+
+include $(MOD_DEPTH)/config/rules.mk
+
+
+ifeq ($(OS_TARGET),WIN16)
+$(OBJDIR)/poppad.exe: $(OBJS)
+ @$(MAKE_OBJDIR)
+ echo system windows >w16link
+ echo name $@ >>w16link
+ echo option map >>w16link
+ echo option stack=16K >>w16link
+ echo debug $(DEBUGTYPE) all >>w16link
+ echo file >>w16link
+ echo $(OBJDIR)\\poppad.$(OBJ_SUFFIX), >>w16link
+ echo $(OBJDIR)\\popfile.$(OBJ_SUFFIX), >>w16link
+ echo $(OBJDIR)\\popfont.$(OBJ_SUFFIX), >>w16link
+ echo $(OBJDIR)\\popfind.$(OBJ_SUFFIX), >>w16link
+ echo $(OBJDIR)\\popprnt0.$(OBJ_SUFFIX), >>w16link
+ echo $(W16STDIO) >>w16link
+ echo library $(LIBPR) >>w16link
+ echo library $(LIBPLDS) >>w16link
+ echo library clibl, commdlg >>w16link
+ echo library winsock.lib >>w16link
+ wlink @w16link.
+ wrc -bt=windows poppad.rc $(OBJDIR)\\poppad.exe
+else
+$(OBJDIR)/poppad.exe: $(OBJS)
+ link $(LDOPTS) $< $(LIBPLC) $(LIBPR) wsock32.lib -out:$@
+endif
+
+export:: $(TARGETS)
+
+install:: export
+
+clean::
+ rm -rf $(TARGETS)
diff --git a/pr/tests/w16gui/popfile.c b/pr/tests/w16gui/popfile.c
new file mode 100644
index 00000000..6621f7db
--- /dev/null
+++ b/pr/tests/w16gui/popfile.c
@@ -0,0 +1,148 @@
+/* -*- 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.
+ */
+
+/*------------------------------------------
+ POPFILE.C -- Popup Editor File Functions
+ (c) Charles Petzold, 1992
+ ------------------------------------------*/
+
+#include <windows.h>
+#include <commdlg.h>
+#include <stdlib.h>
+
+static OPENFILENAME ofn ;
+
+void PopFileInitialize (HWND hwnd)
+ {
+ static char *szFilter[] = { "Text Files (*.TXT)", "*.txt",
+ "ASCII Files (*.ASC)", "*.asc",
+ "All Files (*.*)", "*.*",
+ "" } ;
+
+ ofn.lStructSize = sizeof (OPENFILENAME) ;
+ ofn.hwndOwner = hwnd ;
+ ofn.hInstance = NULL ;
+ ofn.lpstrFilter = szFilter [0] ;
+ ofn.lpstrCustomFilter = NULL ;
+ ofn.nMaxCustFilter = 0 ;
+ ofn.nFilterIndex = 0 ;
+ ofn.lpstrFile = NULL ; // Set in Open and Close functions
+ ofn.nMaxFile = _MAX_PATH ;
+ ofn.lpstrFileTitle = NULL ; // Set in Open and Close functions
+ ofn.nMaxFileTitle = _MAX_FNAME + _MAX_EXT ;
+ ofn.lpstrInitialDir = NULL ;
+ ofn.lpstrTitle = NULL ;
+ ofn.Flags = 0 ; // Set in Open and Close functions
+ ofn.nFileOffset = 0 ;
+ ofn.nFileExtension = 0 ;
+ ofn.lpstrDefExt = "txt" ;
+ ofn.lCustData = 0L ;
+ ofn.lpfnHook = NULL ;
+ ofn.lpTemplateName = NULL ;
+ }
+
+BOOL PopFileOpenDlg (HWND hwnd, LPSTR lpstrFileName, LPSTR lpstrTitleName)
+ {
+ ofn.hwndOwner = hwnd ;
+ ofn.lpstrFile = lpstrFileName ;
+ ofn.lpstrFileTitle = lpstrTitleName ;
+ ofn.Flags = OFN_CREATEPROMPT ;
+
+ return GetOpenFileName (&ofn) ;
+ }
+
+BOOL PopFileSaveDlg (HWND hwnd, LPSTR lpstrFileName, LPSTR lpstrTitleName)
+ {
+ ofn.hwndOwner = hwnd ;
+ ofn.lpstrFile = lpstrFileName ;
+ ofn.lpstrFileTitle = lpstrTitleName ;
+ ofn.Flags = OFN_OVERWRITEPROMPT ;
+
+ return GetSaveFileName (&ofn) ;
+ }
+
+static long PopFileLength (int hFile)
+ {
+ long lCurrentPos = _llseek (hFile, 0L, 1) ;
+ long lFileLength = _llseek (hFile, 0L, 2) ;
+
+ _llseek (hFile, lCurrentPos, 0) ;
+
+ return lFileLength ;
+ }
+
+BOOL PopFileRead (HWND hwndEdit, LPSTR lpstrFileName)
+ {
+ long lLength ;
+ HANDLE hBuffer ;
+ int hFile ;
+ LPSTR lpstrBuffer ;
+
+ if (-1 == (hFile = _lopen (lpstrFileName, OF_READ | OF_SHARE_DENY_WRITE)))
+ return FALSE ;
+
+ if ((lLength = PopFileLength (hFile)) >= 32000)
+ {
+ _lclose (hFile) ;
+ return FALSE ;
+ }
+
+ if (NULL == (hBuffer = GlobalAlloc (GHND, lLength + 1)))
+ {
+ _lclose (hFile) ;
+ return FALSE ;
+ }
+
+ lpstrBuffer = GlobalLock (hBuffer) ;
+ _lread (hFile, lpstrBuffer, (WORD) lLength) ;
+ _lclose (hFile) ;
+ lpstrBuffer [(WORD) lLength] = '\0' ;
+
+ SetWindowText (hwndEdit, lpstrBuffer) ;
+ GlobalUnlock (hBuffer) ;
+ GlobalFree (hBuffer) ;
+
+ return TRUE ;
+ }
+
+BOOL PopFileWrite (HWND hwndEdit, LPSTR lpstrFileName)
+ {
+ HANDLE hBuffer ;
+ int hFile ;
+ LPSTR lpstrBuffer ;
+ WORD wLength ;
+
+ if (-1 == (hFile = _lopen (lpstrFileName, OF_WRITE | OF_SHARE_EXCLUSIVE)))
+ if (-1 == (hFile = _lcreat (lpstrFileName, 0)))
+ return FALSE ;
+
+ wLength = GetWindowTextLength (hwndEdit) ;
+ hBuffer = (HANDLE) SendMessage (hwndEdit, EM_GETHANDLE, 0, 0L) ;
+ lpstrBuffer = (LPSTR) LocalLock (hBuffer) ;
+
+ if (wLength != _lwrite (hFile, lpstrBuffer, wLength))
+ {
+ _lclose (hFile) ;
+ return FALSE ;
+ }
+
+ _lclose (hFile) ;
+ LocalUnlock (hBuffer) ;
+
+ return TRUE ;
+ }
diff --git a/pr/tests/w16gui/popfind.c b/pr/tests/w16gui/popfind.c
new file mode 100644
index 00000000..83c2cfc1
--- /dev/null
+++ b/pr/tests/w16gui/popfind.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.
+ */
+
+/*--------------------------------------------------------
+ POPFIND.C -- Popup Editor Search and Replace Functions
+ (c) Charles Petzold, 1992
+ --------------------------------------------------------*/
+
+#include <windows.h>
+#include <commdlg.h>
+#include <string.h>
+#define MAX_STRING_LEN 256
+
+static char szFindText [MAX_STRING_LEN] ;
+static char szReplText [MAX_STRING_LEN] ;
+
+HWND PopFindFindDlg (HWND hwnd)
+ {
+ static FINDREPLACE fr ; // must be static for modeless dialog!!!
+
+ fr.lStructSize = sizeof (FINDREPLACE) ;
+ fr.hwndOwner = hwnd ;
+ fr.hInstance = NULL ;
+ fr.Flags = FR_HIDEUPDOWN | FR_HIDEMATCHCASE | FR_HIDEWHOLEWORD ;
+ fr.lpstrFindWhat = szFindText ;
+ fr.lpstrReplaceWith = NULL ;
+ fr.wFindWhatLen = sizeof (szFindText) ;
+ fr.wReplaceWithLen = 0 ;
+ fr.lCustData = 0 ;
+ fr.lpfnHook = NULL ;
+ fr.lpTemplateName = NULL ;
+
+ return FindText (&fr) ;
+ }
+
+HWND PopFindReplaceDlg (HWND hwnd)
+ {
+ static FINDREPLACE fr ; // must be static for modeless dialog!!!
+
+ fr.lStructSize = sizeof (FINDREPLACE) ;
+ fr.hwndOwner = hwnd ;
+ fr.hInstance = NULL ;
+ fr.Flags = FR_HIDEUPDOWN | FR_HIDEMATCHCASE | FR_HIDEWHOLEWORD ;
+ fr.lpstrFindWhat = szFindText ;
+ fr.lpstrReplaceWith = szReplText ;
+ fr.wFindWhatLen = sizeof (szFindText) ;
+ fr.wReplaceWithLen = sizeof (szReplText) ;
+ fr.lCustData = 0 ;
+ fr.lpfnHook = NULL ;
+ fr.lpTemplateName = NULL ;
+
+ return ReplaceText (&fr) ;
+ }
+
+BOOL PopFindFindText (HWND hwndEdit, int *piSearchOffset, LPFINDREPLACE lpfr)
+ {
+ int iPos ;
+ LOCALHANDLE hLocal ;
+ LPSTR lpstrDoc, lpstrPos ;
+
+ // Get a pointer to the edit document
+
+ hLocal = (HWND) SendMessage (hwndEdit, EM_GETHANDLE, 0, 0L) ;
+ lpstrDoc = (LPSTR) LocalLock (hLocal) ;
+
+ // Search the document for the find string
+
+ lpstrPos = _fstrstr (lpstrDoc + *piSearchOffset, lpfr->lpstrFindWhat) ;
+ LocalUnlock (hLocal) ;
+
+ // Return an error code if the string cannot be found
+
+ if (lpstrPos == NULL)
+ return FALSE ;
+
+ // Find the position in the document and the new start offset
+
+ iPos = lpstrPos - lpstrDoc ;
+ *piSearchOffset = iPos + _fstrlen (lpfr->lpstrFindWhat) ;
+
+ // Select the found text
+
+ SendMessage (hwndEdit, EM_SETSEL, 0,
+ MAKELONG (iPos, *piSearchOffset)) ;
+
+ return TRUE ;
+ }
+
+BOOL PopFindNextText (HWND hwndEdit, int *piSearchOffset)
+ {
+ FINDREPLACE fr ;
+
+ fr.lpstrFindWhat = szFindText ;
+
+ return PopFindFindText (hwndEdit, piSearchOffset, &fr) ;
+ }
+
+BOOL PopFindReplaceText (HWND hwndEdit, int *piSearchOffset, LPFINDREPLACE lpfr)
+ {
+ // Find the text
+
+ if (!PopFindFindText (hwndEdit, piSearchOffset, lpfr))
+ return FALSE ;
+
+ // Replace it
+
+ SendMessage (hwndEdit, EM_REPLACESEL, 0, (long) lpfr->lpstrReplaceWith) ;
+
+ return TRUE ;
+ }
+
+BOOL PopFindValidFind (void)
+ {
+ return *szFindText != '\0' ;
+ }
diff --git a/pr/tests/w16gui/popfont.c b/pr/tests/w16gui/popfont.c
new file mode 100644
index 00000000..7abc241d
--- /dev/null
+++ b/pr/tests/w16gui/popfont.c
@@ -0,0 +1,75 @@
+/* -*- 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.
+ */
+
+/*------------------------------------------
+ POPFONT.C -- Popup Editor Font Functions
+ (c) Charles Petzold, 1992
+ ------------------------------------------*/
+
+#include <windows.h>
+#include <commdlg.h>
+
+static LOGFONT logfont ;
+static HFONT hFont ;
+
+BOOL PopFontChooseFont (HWND hwnd)
+ {
+ CHOOSEFONT cf ;
+
+ cf.lStructSize = sizeof (CHOOSEFONT) ;
+ cf.hwndOwner = hwnd ;
+ cf.hDC = NULL ;
+ cf.lpLogFont = &logfont ;
+ cf.iPointSize = 0 ;
+ cf.Flags = CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS
+ | CF_EFFECTS ;
+ cf.rgbColors = 0L ;
+ cf.lCustData = 0L ;
+ cf.lpfnHook = NULL ;
+ cf.lpTemplateName = NULL ;
+ cf.hInstance = NULL ;
+ cf.lpszStyle = NULL ;
+ cf.nFontType = 0 ; // Returned from ChooseFont
+ cf.nSizeMin = 0 ;
+ cf.nSizeMax = 0 ;
+
+ return ChooseFont (&cf) ;
+ }
+
+void PopFontInitialize (HWND hwndEdit)
+ {
+ GetObject (GetStockObject (SYSTEM_FONT), sizeof (LOGFONT),
+ (LPSTR) &logfont) ;
+ hFont = CreateFontIndirect (&logfont) ;
+ SendMessage (hwndEdit, WM_SETFONT, hFont, 0L) ;
+ }
+
+void PopFontSetFont (HWND hwndEdit)
+ {
+ HFONT hFontNew ;
+
+ hFontNew = CreateFontIndirect (&logfont) ;
+ SendMessage (hwndEdit, WM_SETFONT, hFontNew, 0L) ;
+ DeleteObject (hFont) ;
+ hFont = hFontNew ;
+ }
+
+void PopFontDeinitialize (void)
+ {
+ DeleteObject (hFont) ;
+ }
diff --git a/pr/tests/w16gui/poppad.c b/pr/tests/w16gui/poppad.c
new file mode 100644
index 00000000..3397f3fb
--- /dev/null
+++ b/pr/tests/w16gui/poppad.c
@@ -0,0 +1,653 @@
+/* -*- 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.
+ */
+
+/*---------------------------------------
+ POPPAD.C -- Popup Editor
+ (c) Charles Petzold, 1992
+ ---------------------------------------*/
+
+#include "nspr.h"
+#include "plevent.h"
+#include <windows.h>
+#include <commdlg.h>
+#include <stdlib.h>
+#include "poppad.h"
+#include <time.h>
+
+#define EDITID 1
+#define UNTITLED "(untitled)"
+
+long FAR PASCAL _export WndProc (HWND, UINT, UINT, LONG) ;
+BOOL FAR PASCAL _export AboutDlgProc (HWND, UINT, UINT, LONG) ;
+
+/* Declarations for NSPR customization
+**
+*/
+typedef struct PadEvent
+{
+ PLEvent plEvent;
+ int unused;
+} PadEvent;
+
+static void PR_CALLBACK TimerThread( void *arg);
+static void PR_CALLBACK HandlePadEvent( PadEvent *padEvent );
+static void PR_CALLBACK DestroyPadEvent( PadEvent *padevent );
+static PRThread *tThread;
+static PLEventQueue *padQueue;
+static int quitSwitch = 0;
+static long ThreadSleepTime = 1000;
+static long timerCount = 0;
+static char *startMessage = "Poppad: NSPR GUI and event test program.\n"
+ "You should see lines of 50 characters\n"
+ "with a new character appearing at 1 second intervals.\n"
+ "Every 10 seconds gets a '+'; every 5 seconds gets a '_';\n"
+ "every 1 second gets a '.'.\n\n"
+ "You should be able to type in the window.\n\n\n";
+
+
+ // Functions in POPFILE.C
+
+void PopFileInitialize (HWND) ;
+BOOL PopFileOpenDlg (HWND, LPSTR, LPSTR) ;
+BOOL PopFileSaveDlg (HWND, LPSTR, LPSTR) ;
+BOOL PopFileRead (HWND, LPSTR) ;
+BOOL PopFileWrite (HWND, LPSTR) ;
+
+ // Functions in POPFIND.C
+
+HWND PopFindFindDlg (HWND) ;
+HWND PopFindReplaceDlg (HWND) ;
+BOOL PopFindFindText (HWND, int *, LPFINDREPLACE) ;
+BOOL PopFindReplaceText (HWND, int *, LPFINDREPLACE) ;
+BOOL PopFindNextText (HWND, int *) ;
+BOOL PopFindValidFind (void) ;
+
+ // Functions in POPFONT.C
+
+void PopFontInitialize (HWND) ;
+BOOL PopFontChooseFont (HWND) ;
+void PopFontSetFont (HWND) ;
+void PopFontDeinitialize (void) ;
+
+ // Functions in POPPRNT.C
+
+BOOL PopPrntPrintFile (HANDLE, HWND, HWND, LPSTR) ;
+
+ // Global variables
+
+static char szAppName [] = "PopPad" ;
+static HWND hDlgModeless ;
+static HWND hwndEdit ;
+
+int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
+ LPSTR lpszCmdLine, int nCmdShow)
+ {
+ MSG msg;
+ HWND hwnd ;
+ HANDLE hAccel ;
+ WNDCLASS wndclass ;
+
+ PR_STDIO_INIT();
+ PR_Init(0, 0, 0);
+
+ if (!hPrevInstance)
+ {
+ wndclass.style = CS_HREDRAW | CS_VREDRAW ;
+ wndclass.lpfnWndProc = WndProc ;
+ wndclass.cbClsExtra = 0 ;
+ wndclass.cbWndExtra = 0 ;
+ wndclass.hInstance = hInstance ;
+ wndclass.hIcon = LoadIcon (hInstance, szAppName) ;
+ wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
+ wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
+ wndclass.lpszMenuName = szAppName ;
+ wndclass.lpszClassName = szAppName ;
+
+ RegisterClass (&wndclass) ;
+ }
+
+ hwnd = CreateWindow (szAppName, NULL,
+ WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ NULL, NULL, hInstance, lpszCmdLine) ;
+
+ ShowWindow (hwnd, nCmdShow) ;
+ UpdateWindow (hwnd);
+
+ hAccel = LoadAccelerators (hInstance, szAppName) ;
+
+ for(;;)
+ {
+ if ( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ))
+ {
+ if (GetMessage(&msg, NULL, 0, 0))
+ {
+ if (hDlgModeless == NULL || !IsDialogMessage (hDlgModeless, &msg))
+ {
+ if (!TranslateAccelerator (hwnd, hAccel, &msg))
+ {
+ TranslateMessage (&msg) ;
+ DispatchMessage (&msg) ;
+ } /* end if !TranslateAccelerator */
+ }
+ }
+ else
+ {
+ break;
+ } /* end if GetMessage() */
+ }
+ else /* !PeekMessage */
+ {
+ PR_Sleep(50);
+ }/* end if PeekMessage() */
+ } /* end for() */
+
+ PR_JoinThread( tThread );
+ PL_DestroyEventQueue( padQueue );
+ PR_Cleanup();
+ return msg.wParam ;
+ }
+
+void DoCaption (HWND hwnd, char *szTitleName)
+ {
+ char szCaption [64 + _MAX_FNAME + _MAX_EXT] ;
+
+ wsprintf (szCaption, "%s - %s", (LPSTR) szAppName,
+ (LPSTR) (szTitleName [0] ? szTitleName : UNTITLED)) ;
+
+ SetWindowText (hwnd, szCaption) ;
+ }
+
+void OkMessage (HWND hwnd, char *szMessage, char *szTitleName)
+ {
+ char szBuffer [64 + _MAX_FNAME + _MAX_EXT] ;
+
+ wsprintf (szBuffer, szMessage,
+ (LPSTR) (szTitleName [0] ? szTitleName : UNTITLED)) ;
+
+ MessageBox (hwnd, szBuffer, szAppName, MB_OK | MB_ICONEXCLAMATION) ;
+ }
+
+short AskAboutSave (HWND hwnd, char *szTitleName)
+ {
+ char szBuffer [64 + _MAX_FNAME + _MAX_EXT] ;
+ short nReturn ;
+
+ wsprintf (szBuffer, "Save current changes in %s?",
+ (LPSTR) (szTitleName [0] ? szTitleName : UNTITLED)) ;
+
+ nReturn = MessageBox (hwnd, szBuffer, szAppName,
+ MB_YESNOCANCEL | MB_ICONQUESTION) ;
+
+ if (nReturn == IDYES)
+ if (!SendMessage (hwnd, WM_COMMAND, IDM_SAVE, 0L))
+ nReturn = IDCANCEL ;
+
+ return nReturn ;
+ }
+
+long FAR PASCAL _export WndProc (HWND hwnd, UINT message, UINT wParam,
+ LONG lParam)
+ {
+ static BOOL bNeedSave = FALSE ;
+ static char szFileName [_MAX_PATH] ;
+ static char szTitleName [_MAX_FNAME + _MAX_EXT] ;
+ static FARPROC lpfnAboutDlgProc ;
+ static HANDLE hInst ;
+ static int iOffset ;
+ static UINT messageFindReplace ;
+ LONG lSelect ;
+ LPFINDREPLACE lpfr ;
+ WORD wEnable ;
+
+ switch (message)
+ {
+ case WM_CREATE:
+ // Get About dialog instance address
+
+ hInst = ((LPCREATESTRUCT) lParam)->hInstance ;
+ lpfnAboutDlgProc = MakeProcInstance ((FARPROC) AboutDlgProc,
+ hInst) ;
+
+ // Create the edit control child window
+
+ hwndEdit = CreateWindow ("edit", NULL,
+ WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |
+ WS_BORDER | ES_LEFT | ES_MULTILINE |
+ ES_NOHIDESEL | ES_AUTOHSCROLL | ES_AUTOVSCROLL,
+ 0, 0, 0, 0,
+ hwnd, EDITID, hInst, NULL) ;
+
+ SendMessage (hwndEdit, EM_LIMITTEXT, 32000, 0L) ;
+
+ // Initialize common dialog box stuff
+
+ PopFileInitialize (hwnd) ;
+ PopFontInitialize (hwndEdit) ;
+
+ messageFindReplace = RegisterWindowMessage (FINDMSGSTRING) ;
+
+ // Process command line
+
+ lstrcpy (szFileName, (LPSTR)
+ (((LPCREATESTRUCT) lParam)->lpCreateParams)) ;
+
+ if (lstrlen (szFileName) > 0)
+ {
+ GetFileTitle (szFileName, szTitleName,
+ sizeof (szTitleName)) ;
+
+ if (!PopFileRead (hwndEdit, szFileName))
+ OkMessage (hwnd, "File %s cannot be read!",
+ szTitleName) ;
+ }
+
+ DoCaption (hwnd, szTitleName) ;
+
+ /* Initialize Event Processing for NSPR
+ ** Retrieve the event queue just created
+ ** Create the TimerThread
+ */
+ PL_InitializeEventsLib("someName");
+ padQueue = PL_GetMainEventQueue();
+ tThread = PR_CreateThread(PR_USER_THREAD,
+ TimerThread,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0 );
+ return 0 ;
+
+ case WM_SETFOCUS:
+ SetFocus (hwndEdit) ;
+ return 0 ;
+
+ case WM_SIZE:
+ MoveWindow (hwndEdit, 0, 0, LOWORD (lParam),
+ HIWORD (lParam), TRUE) ;
+ return 0 ;
+
+ case WM_INITMENUPOPUP:
+ switch (lParam)
+ {
+ case 1: // Edit menu
+
+ // Enable Undo if edit control can do it
+
+ EnableMenuItem (wParam, IDM_UNDO,
+ SendMessage (hwndEdit, EM_CANUNDO, 0, 0L) ?
+ MF_ENABLED : MF_GRAYED) ;
+
+ // Enable Paste if text is in the clipboard
+
+ EnableMenuItem (wParam, IDM_PASTE,
+ IsClipboardFormatAvailable (CF_TEXT) ?
+ MF_ENABLED : MF_GRAYED) ;
+
+ // Enable Cut, Copy, and Del if text is selected
+
+ lSelect = SendMessage (hwndEdit, EM_GETSEL, 0, 0L) ;
+ wEnable = HIWORD (lSelect) != LOWORD (lSelect) ?
+ MF_ENABLED : MF_GRAYED ;
+
+ EnableMenuItem (wParam, IDM_CUT, wEnable) ;
+ EnableMenuItem (wParam, IDM_COPY, wEnable) ;
+ EnableMenuItem (wParam, IDM_DEL, wEnable) ;
+ break ;
+
+ case 2: // Search menu
+
+ // Enable Find, Next, and Replace if modeless
+ // dialogs are not already active
+
+ wEnable = hDlgModeless == NULL ?
+ MF_ENABLED : MF_GRAYED ;
+
+ EnableMenuItem (wParam, IDM_FIND, wEnable) ;
+ EnableMenuItem (wParam, IDM_NEXT, wEnable) ;
+ EnableMenuItem (wParam, IDM_REPLACE, wEnable) ;
+ break ;
+ }
+ return 0 ;
+
+ case WM_COMMAND :
+ // Messages from edit control
+
+ if (LOWORD (lParam) && wParam == EDITID)
+ {
+ switch (HIWORD (lParam))
+ {
+ case EN_UPDATE:
+ bNeedSave = TRUE ;
+ return 0 ;
+
+ case EN_ERRSPACE:
+ case EN_MAXTEXT:
+ MessageBox (hwnd, "Edit control out of space.",
+ szAppName, MB_OK | MB_ICONSTOP) ;
+ return 0 ;
+ }
+ break ;
+ }
+
+ switch (wParam)
+ {
+ // Messages from File menu
+
+ case IDM_NEW:
+ if (bNeedSave && IDCANCEL ==
+ AskAboutSave (hwnd, szTitleName))
+ return 0 ;
+
+ SetWindowText (hwndEdit, "\0") ;
+ szFileName [0] = '\0' ;
+ szTitleName [0] = '\0' ;
+ DoCaption (hwnd, szTitleName) ;
+ bNeedSave = FALSE ;
+ return 0 ;
+
+ case IDM_OPEN:
+ if (bNeedSave && IDCANCEL ==
+ AskAboutSave (hwnd, szTitleName))
+ return 0 ;
+
+ if (PopFileOpenDlg (hwnd, szFileName, szTitleName))
+ {
+ if (!PopFileRead (hwndEdit, szFileName))
+ {
+ OkMessage (hwnd, "Could not read file %s!",
+ szTitleName) ;
+ szFileName [0] = '\0' ;
+ szTitleName [0] = '\0' ;
+ }
+ }
+
+ DoCaption (hwnd, szTitleName) ;
+ bNeedSave = FALSE ;
+ return 0 ;
+
+ case IDM_SAVE:
+ if (szFileName [0])
+ {
+ if (PopFileWrite (hwndEdit, szFileName))
+ {
+ bNeedSave = FALSE ;
+ return 1 ;
+ }
+ else
+ OkMessage (hwnd, "Could not write file %s",
+ szTitleName) ;
+ return 0 ;
+ }
+ // fall through
+ case IDM_SAVEAS:
+ if (PopFileSaveDlg (hwnd, szFileName, szTitleName))
+ {
+ DoCaption (hwnd, szTitleName) ;
+
+ if (PopFileWrite (hwndEdit, szFileName))
+ {
+ bNeedSave = FALSE ;
+ return 1 ;
+ }
+ else
+ OkMessage (hwnd, "Could not write file %s",
+ szTitleName) ;
+ }
+ return 0 ;
+
+ case IDM_PRINT:
+ if (!PopPrntPrintFile (hInst, hwnd, hwndEdit,
+ szTitleName))
+ OkMessage (hwnd, "Could not print file %s",
+ szTitleName) ;
+ return 0 ;
+
+ case IDM_EXIT:
+ SendMessage (hwnd, WM_CLOSE, 0, 0L) ;
+ return 0 ;
+
+ // Messages from Edit menu
+
+ case IDM_UNDO:
+ SendMessage (hwndEdit, WM_UNDO, 0, 0L) ;
+ return 0 ;
+
+ case IDM_CUT:
+ SendMessage (hwndEdit, WM_CUT, 0, 0L) ;
+ return 0 ;
+
+ case IDM_COPY:
+ SendMessage (hwndEdit, WM_COPY, 0, 0L) ;
+ return 0 ;
+
+ case IDM_PASTE:
+ SendMessage (hwndEdit, WM_PASTE, 0, 0L) ;
+ return 0 ;
+
+ case IDM_DEL:
+ SendMessage (hwndEdit, WM_CLEAR, 0, 0L) ;
+ return 0 ;
+
+ case IDM_SELALL:
+ SendMessage (hwndEdit, EM_SETSEL, 0,
+ MAKELONG (0, 32767)) ;
+ return 0 ;
+
+ // Messages from Search menu
+
+ case IDM_FIND:
+ iOffset = HIWORD (
+ SendMessage (hwndEdit, EM_GETSEL, 0, 0L)) ;
+ hDlgModeless = PopFindFindDlg (hwnd) ;
+ return 0 ;
+
+ case IDM_NEXT:
+ iOffset = HIWORD (
+ SendMessage (hwndEdit, EM_GETSEL, 0, 0L)) ;
+
+ if (PopFindValidFind ())
+ PopFindNextText (hwndEdit, &iOffset) ;
+ else
+ hDlgModeless = PopFindFindDlg (hwnd) ;
+
+ return 0 ;
+
+ case IDM_REPLACE:
+ iOffset = HIWORD (
+ SendMessage (hwndEdit, EM_GETSEL, 0, 0L)) ;
+
+ hDlgModeless = PopFindReplaceDlg (hwnd) ;
+ return 0 ;
+
+ case IDM_FONT:
+ if (PopFontChooseFont (hwnd))
+ PopFontSetFont (hwndEdit) ;
+
+ return 0 ;
+
+ // Messages from Help menu
+
+ case IDM_HELP:
+ OkMessage (hwnd, "Help not yet implemented!", NULL) ;
+ return 0 ;
+
+ case IDM_ABOUT:
+ DialogBox (hInst, "AboutBox", hwnd, lpfnAboutDlgProc);
+ return 0 ;
+ }
+ break ;
+
+ case WM_CLOSE:
+ if (!bNeedSave || IDCANCEL != AskAboutSave (hwnd, szTitleName))
+ DestroyWindow (hwnd) ;
+
+ return 0 ;
+
+ case WM_QUERYENDSESSION:
+ if (!bNeedSave || IDCANCEL != AskAboutSave (hwnd, szTitleName))
+ return 1L ;
+
+ return 0 ;
+
+ case WM_DESTROY:
+ PopFontDeinitialize () ;
+ PostQuitMessage (0) ;
+ quitSwitch = 1;
+ return 0 ;
+
+ default:
+ // Process "Find-Replace" messages
+
+ if (message == messageFindReplace)
+ {
+ lpfr = (LPFINDREPLACE) lParam ;
+
+ if (lpfr->Flags & FR_DIALOGTERM)
+ hDlgModeless = NULL ;
+
+ if (lpfr->Flags & FR_FINDNEXT)
+ if (!PopFindFindText (hwndEdit, &iOffset, lpfr))
+ OkMessage (hwnd, "Text not found!", NULL) ;
+
+ if (lpfr->Flags & FR_REPLACE ||
+ lpfr->Flags & FR_REPLACEALL)
+ if (!PopFindReplaceText (hwndEdit, &iOffset, lpfr))
+ OkMessage (hwnd, "Text not found!", NULL) ;
+
+ if (lpfr->Flags & FR_REPLACEALL)
+ while (PopFindReplaceText (hwndEdit, &iOffset, lpfr));
+
+ return 0 ;
+ }
+ break ;
+ }
+ return DefWindowProc (hwnd, message, wParam, lParam) ;
+ }
+
+BOOL FAR PASCAL _export AboutDlgProc (HWND hDlg, UINT message, UINT wParam,
+ LONG lParam)
+ {
+ switch (message)
+ {
+ case WM_INITDIALOG:
+ return TRUE ;
+
+ case WM_COMMAND:
+ switch (wParam)
+ {
+ case IDOK:
+ EndDialog (hDlg, 0) ;
+ return TRUE ;
+ }
+ break ;
+ }
+ return FALSE ;
+ }
+/*
+** TimerThread() -- The Main function of the timer pop thread
+**
+*/
+static void PR_CALLBACK TimerThread( void *arg)
+{
+ do {
+ PadEvent *ev;
+
+ /*
+ ** Should we quit now?
+ */
+ if ( quitSwitch )
+ break;
+ /*
+ ** Create and Post the event the event
+ */
+ PL_ENTER_EVENT_QUEUE_MONITOR( padQueue );
+ ev = (PadEvent *) PR_NEW( PadEvent );
+ PL_InitEvent( &ev->plEvent, NULL,
+ (PLHandleEventProc)HandlePadEvent,
+ (PLDestroyEventProc)DestroyPadEvent );
+ PL_PostEvent( padQueue, &ev->plEvent );
+ PL_EXIT_EVENT_QUEUE_MONITOR( padQueue );
+
+ PR_Sleep( ThreadSleepTime );
+ } while(1);
+ return;
+}
+
+/*
+** HandlePadEvent() -- gets called because of PostEvent
+*/
+static void PR_CALLBACK HandlePadEvent( PadEvent *padEvent )
+{
+ if ( timerCount++ == 0 )
+ {
+ char *cp;
+
+ for ( cp = startMessage; *cp != 0 ; cp++ )
+ {
+ SendMessage( hwndEdit, WM_CHAR, *cp, MAKELONG( *cp, 1 ));
+ }
+ }
+ /*
+ ** Send a WM_CHAR event the edit Window
+ */
+ if ((timerCount % 10) == 0)
+ {
+ SendMessage( hwndEdit, WM_CHAR, '+', MAKELONG( '+', 1 ));
+ }
+ else if ((timerCount % 5) == 0)
+ {
+ SendMessage( hwndEdit, WM_CHAR, '_', MAKELONG( '_', 1 ));
+ }
+ else
+ {
+ SendMessage( hwndEdit, WM_CHAR, '.', MAKELONG( '.', 1 ));
+ }
+
+ if ( (timerCount % 50) == 0)
+ {
+ SendMessage( hwndEdit, WM_CHAR, '\n', MAKELONG( '\n', 1 ));
+ }
+
+ /*
+ ** PL_RevokeEvents() is broken. Test to fix it.
+ */
+ {
+ static long revokeCounter = 0;
+
+ if (revokeCounter++ > 10 )
+ {
+ PR_Sleep( ThreadSleepTime * 10 );
+ SendMessage( hwndEdit, WM_CHAR, '*', MAKELONG( '\n', 1 ));
+ PL_RevokeEvents( padQueue, NULL );
+ revokeCounter = 0;
+ }
+ }
+ return;
+}
+
+/*
+** DestroyPadEvent() -- Called after HandlePadEvent()
+*/
+static void PR_CALLBACK DestroyPadEvent( PadEvent *padevent )
+{
+ PR_Free( padevent );
+ return;
+}
diff --git a/pr/tests/w16gui/poppad.h b/pr/tests/w16gui/poppad.h
new file mode 100644
index 00000000..038e3259
--- /dev/null
+++ b/pr/tests/w16gui/poppad.h
@@ -0,0 +1,47 @@
+/* -*- 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.
+ */
+
+/*----------------------
+ POPPAD.H header file
+ (c) Charles Petzold, 1992
+ ----------------------*/
+
+#define IDM_NEW 10
+#define IDM_OPEN 11
+#define IDM_SAVE 12
+#define IDM_SAVEAS 13
+#define IDM_PRINT 14
+#define IDM_EXIT 15
+
+#define IDM_UNDO 20
+#define IDM_CUT 21
+#define IDM_COPY 22
+#define IDM_PASTE 23
+#define IDM_DEL 24
+#define IDM_SELALL 25
+
+#define IDM_FIND 30
+#define IDM_NEXT 31
+#define IDM_REPLACE 32
+
+#define IDM_FONT 40
+
+#define IDM_HELP 50
+#define IDM_ABOUT 51
+
+#define IDD_FNAME 10
diff --git a/pr/tests/w16gui/poppad.ico b/pr/tests/w16gui/poppad.ico
new file mode 100644
index 00000000..fe7097c5
--- /dev/null
+++ b/pr/tests/w16gui/poppad.ico
Binary files differ
diff --git a/pr/tests/w16gui/poppad.rc b/pr/tests/w16gui/poppad.rc
new file mode 100644
index 00000000..efdd5dc3
--- /dev/null
+++ b/pr/tests/w16gui/poppad.rc
@@ -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.
+ */
+
+/*---------------------------
+ POPPAD.RC resource script
+ ---------------------------*/
+
+#include <windows.h>
+#include "poppad.h"
+
+PopPad ICON "poppad.ico"
+
+PopPad MENU
+ {
+ POPUP "&File"
+ {
+ MENUITEM "&New", IDM_NEW
+ MENUITEM "&Open...", IDM_OPEN
+ MENUITEM "&Save", IDM_SAVE
+ MENUITEM "Save &As...", IDM_SAVEAS
+ MENUITEM SEPARATOR
+ MENUITEM "&Print...", IDM_PRINT
+ MENUITEM SEPARATOR
+ MENUITEM "E&xit", IDM_EXIT
+ }
+ POPUP "&Edit"
+ {
+ MENUITEM "&Undo\tCtrl+Z", IDM_UNDO
+ MENUITEM SEPARATOR
+ MENUITEM "Cu&t\tCtrl+X", IDM_CUT
+ MENUITEM "&Copy\tCtrl+C", IDM_COPY
+ MENUITEM "&Paste\tCtrl+V", IDM_PASTE
+ MENUITEM "De&lete\tDel", IDM_DEL
+ MENUITEM SEPARATOR
+ MENUITEM "&Select All", IDM_SELALL
+ }
+ POPUP "&Search"
+ {
+ MENUITEM "&Find...", IDM_FIND
+ MENUITEM "Find &Next\tF3", IDM_NEXT
+ MENUITEM "&Replace...", IDM_REPLACE
+ }
+ POPUP "&Character"
+ {
+ MENUITEM "&Font...", IDM_FONT
+ }
+ POPUP "&Help"
+ {
+ MENUITEM "&Help", IDM_HELP
+ MENUITEM "&About PopPad...", IDM_ABOUT
+ }
+ }
+
+PopPad ACCELERATORS
+ {
+ "^Z", IDM_UNDO
+ VK_BACK, IDM_UNDO, VIRTKEY, ALT
+ "^X", IDM_CUT
+ VK_DELETE, IDM_CUT, VIRTKEY, SHIFT
+ "^C", IDM_COPY
+ VK_INSERT, IDM_COPY, VIRTKEY, CONTROL
+ "^V", IDM_PASTE
+ VK_INSERT, IDM_PASTE, VIRTKEY, SHIFT
+ VK_DELETE, IDM_DEL, VIRTKEY
+ VK_F3, IDM_NEXT, VIRTKEY
+ VK_F1, IDM_HELP, VIRTKEY
+ }
+
+AboutBox DIALOG 20, 20, 160, 80
+ STYLE WS_POPUP | WS_DLGFRAME
+ {
+ CTEXT "PopPad" -1, 0, 12, 160, 8
+ ICON "PopPad" -1, 8, 8, 0, 0
+ CTEXT "Popup Editor for Microsoft Windows" -1, 0, 36, 160, 8
+ CTEXT "Copyright (c) Charles Petzold, 1992" -1, 0, 48, 160, 8
+ DEFPUSHBUTTON "OK" IDOK, 64, 60, 32, 14, WS_GROUP
+ }
+
+PrintDlgBox DIALOG 20, 20, 100, 76
+ STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE
+ CAPTION "PopPad"
+ {
+ CTEXT "Sending", -1, 0, 10, 100, 8
+ CTEXT "", IDD_FNAME, 0, 20, 100, 8
+ CTEXT "to print spooler.", -1, 0, 30, 100, 8
+ DEFPUSHBUTTON "Cancel", IDCANCEL, 34, 50, 32, 14, WS_GROUP
+ }
diff --git a/pr/tests/w16gui/popprnt0.c b/pr/tests/w16gui/popprnt0.c
new file mode 100644
index 00000000..a620a4cc
--- /dev/null
+++ b/pr/tests/w16gui/popprnt0.c
@@ -0,0 +1,30 @@
+/* -*- 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.
+ */
+
+/*---------------------------------------------------------------
+ POPPRNT0.C -- Popup Editor Printing Functions (dummy version)
+ (c) Charles Petzold, 1992
+ ---------------------------------------------------------------*/
+
+#include <windows.h>
+
+BOOL PopPrntPrintFile (HANDLE hInst, HWND hwnd, HWND hwndEdit,
+ LPSTR lpstrTitleName)
+ {
+ return FALSE ;
+ }
diff --git a/pr/tests/w16gui/readme.1st b/pr/tests/w16gui/readme.1st
new file mode 100644
index 00000000..afdf847f
--- /dev/null
+++ b/pr/tests/w16gui/readme.1st
@@ -0,0 +1,37 @@
+readme.1st.
+
+The files in the pr/tests/w16gui directory are taken
+from "Programming Windows 3.1" by Charles Petzold,
+specifically, the programs in chapter 14 related
+to the "poppad4" sample application.
+
+These programs are compiled with nspr20 to test nspr 2.0
+and to demostrate the use of nspr in a gui application.
+
+Library (DLL) PLDSxx.lib PLDSxx.dll is required to be
+linked with this test case. Functions in this dll are
+in the source ns/nspr20/lib/ds/plevent.* files.
+
+Permission to use.
+
+The source for poppad.c are used under license from
+Petzold. The license to use is stated in the book.
+The following paragraph of the license grants that
+use.
+
+ 5. SAMPLE CODE. If the SOFTWARE includes Sample Code, then
+ Microsoft grants you a royalty-free right to reproduce and
+ distribute the sample code of the SOFTWARE provided that you:
+ (a) distribute the sample code only in conjunction with and
+ as part of your software product; (b) do not use Microsoft's
+ or its authors' names, logos, or trademarks to market your
+ software product; (c) include the copyright notice that appears
+ on the SOFTWARE on your product label and as a part of the
+ sign-on message for your software product; and (d) agree to
+ idemnify, hold harmless, and defend Microsoft and its authors
+ from and against any claims or lawsuits, including attorneys'
+ fees, that arise or result from the use or distribution of
+ your software product.
+
+lth. 9/24/97.
+
diff --git a/pr/tests/writev.c b/pr/tests/writev.c
new file mode 100644
index 00000000..60325bd2
--- /dev/null
+++ b/pr/tests/writev.c
@@ -0,0 +1,215 @@
+/* -*- 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 "nspr.h"
+
+#include "plgetopt.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef XP_MAC
+#include "prlog.h"
+#define printf PR_LogPrint
+#endif
+
+
+#ifndef IOV_MAX
+#define IOV_MAX 16
+#endif
+
+#define BASE_PORT 9867
+
+int PR_CALLBACK Writev(int argc, char **argv)
+{
+
+ PRStatus rv;
+ PRNetAddr serverAddr;
+ PRFileDesc *clientSock, *debug = NULL;
+
+ char *buffer = NULL;
+ PRIOVec *iov = NULL;
+ PRBool passed = PR_TRUE;
+ PRIntervalTime timein, elapsed, timeout;
+ PRIntervalTime tmo_min = 0x7fffffff, tmo_max = 0, tmo_elapsed = 0;
+ PRInt32 tmo_counted = 0, iov_index, loop, bytes, number_fragments;
+ PRInt32 message_length = 100, fragment_length = 100, messages = 100;
+ struct Descriptor { PRInt32 length; PRUint32 checksum; } descriptor;
+
+ /*
+ * USAGE
+ * -h dns name of host serving the connection (default = self)
+ * -m number of messages to send (default = 100)
+ * -s size of each message (default = 100)
+ * -f size of each message fragment (default = 100)
+ */
+
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dh:m:s:f:");
+
+ PR_STDIO_INIT();
+ rv = PR_InitializeNetAddr(PR_IpAddrLoopback, BASE_PORT, &serverAddr);
+ PR_ASSERT(PR_SUCCESS == rv);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'h': /* the remote host */
+ {
+ PRIntn es = 0;
+ PRHostEnt host;
+ char buffer[1024];
+ (void)PR_GetHostByName(opt->value, buffer, sizeof(buffer), &host);
+ es = PR_EnumerateHostEnt(es, &host, BASE_PORT, &serverAddr);
+ PR_ASSERT(es > 0);
+ }
+ break;
+ case 'd': /* debug mode */
+ debug = PR_GetSpecialFD(PR_StandardError);
+ break;
+ case 'm': /* number of messages to send */
+ messages = atoi(opt->value);
+ break;
+ case 's': /* total size of each message */
+ message_length = atoi(opt->value);
+ break;
+ case 'f': /* size of each message fragment */
+ fragment_length = atoi(opt->value);
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ buffer = (char*)malloc(message_length);
+
+ number_fragments = (message_length + fragment_length - 1) / fragment_length + 1;
+ while (IOV_MAX < number_fragments)
+ {
+ fragment_length = message_length / (IOV_MAX - 2);
+ number_fragments = (message_length + fragment_length - 1) /
+ fragment_length + 1;
+ if (NULL != debug) PR_fprintf(debug,
+ "Too many fragments - reset fragment length to %ld\n", fragment_length);
+ }
+ iov = (PRIOVec*)malloc(number_fragments * sizeof(PRIOVec));
+
+ iov[0].iov_base = (char*)&descriptor;
+ iov[0].iov_len = sizeof(descriptor);
+ for (iov_index = 1; iov_index < number_fragments; ++iov_index)
+ {
+ iov[iov_index].iov_base = buffer + (iov_index - 1) * fragment_length;
+ iov[iov_index].iov_len = fragment_length;
+ }
+
+ for (bytes = 0; bytes < message_length; ++bytes)
+ buffer[bytes] = (char)bytes;
+
+ timeout = PR_SecondsToInterval(1);
+
+ for (loop = 0; loop < messages; ++loop)
+ {
+ if (NULL != debug)
+ PR_fprintf(debug, "[%d]socket ... ", loop);
+ clientSock = PR_NewTCPSocket();
+ if (clientSock)
+ {
+ timein = PR_IntervalNow();
+ if (NULL != debug)
+ PR_fprintf(debug, "connecting ... ");
+ rv = PR_Connect(clientSock, &serverAddr, timeout);
+ if (PR_SUCCESS == rv)
+ {
+ descriptor.checksum = 0;
+ descriptor.length = (loop < (messages - 1)) ? message_length : 0;
+ if (0 == descriptor.length) number_fragments = 1;
+ else
+ for (iov_index = 0; iov_index < descriptor.length; ++iov_index)
+ {
+ PRUint32 overflow = descriptor.checksum & 0x80000000;
+ descriptor.checksum = (descriptor.checksum << 1);
+ if (0x00000000 != overflow) descriptor.checksum += 1;
+ descriptor.checksum += buffer[iov_index];
+ }
+ if (NULL != debug) PR_fprintf(
+ debug, "sending %d bytes ... ", descriptor.length);
+
+ /* then, at the last moment ... */
+ descriptor.length = PR_ntohl(descriptor.length);
+ descriptor.checksum = PR_ntohl(descriptor.checksum);
+
+ bytes = PR_Writev(clientSock, iov, number_fragments, timeout);
+ if (NULL != debug)
+ PR_fprintf(debug, "closing ... ");
+ rv = PR_Shutdown(clientSock, PR_SHUTDOWN_BOTH);
+ rv = PR_Close(clientSock);
+ if (NULL != debug) PR_fprintf(
+ debug, "%s\n", ((PR_SUCCESS == rv) ? "good" : "bad"));
+ elapsed = PR_IntervalNow() - timein;
+ if (elapsed < tmo_min) tmo_min = elapsed;
+ else if (elapsed > tmo_max) tmo_max = elapsed;
+ tmo_elapsed += elapsed;
+ tmo_counted += 1;
+ }
+ else
+ {
+ if (NULL != debug) PR_fprintf(
+ debug, "failed - retrying (%d, %d)\n",
+ PR_GetError(), PR_GetOSError());
+ PR_Close(clientSock);
+ }
+ }
+ else if (NULL != debug)
+ {
+ PR_fprintf(debug, "unable to create client socket\n");
+ passed = PR_FALSE;
+ }
+ }
+ if (NULL != debug) {
+ if (0 == tmo_counted) {
+ PR_fprintf(debug, "No connection made\n");
+ } else {
+ PR_fprintf(
+ debug, "\nTimings: %d [%d] %d (microseconds)\n",
+ PR_IntervalToMicroseconds(tmo_min),
+ PR_IntervalToMicroseconds(tmo_elapsed / tmo_counted),
+ PR_IntervalToMicroseconds(tmo_max));
+ }
+ }
+
+ PR_DELETE(buffer);
+ PR_DELETE(iov);
+
+ PR_fprintf(
+ PR_GetSpecialFD(PR_StandardError),
+ "%s\n", (passed) ? "PASSED" : "FAILED");
+ return (passed) ? 0 : 1;
+}
+
+int main(int argc, char **argv)
+{
+ return (PR_VersionCheck(PR_VERSION)) ?
+ PR_Initialize(Writev, argc, argv, 4) : -1;
+} /* main */
+
+/* writev.c */
+
+
diff --git a/pr/tests/xnotify.c b/pr/tests/xnotify.c
new file mode 100644
index 00000000..5d846c28
--- /dev/null
+++ b/pr/tests/xnotify.c
@@ -0,0 +1,370 @@
+/* -*- 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 "plerror.h"
+#include "plgetopt.h"
+
+#include "prinit.h"
+#include "prprf.h"
+#include "prio.h"
+#include "prcvar.h"
+#include "prmon.h"
+#include "prcmon.h"
+#include "prlock.h"
+#include "prerror.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prthread.h"
+
+static PRLock *ml = NULL;
+static PRIntervalTime base;
+static PRFileDesc *err = NULL;
+
+typedef struct CMonShared
+{
+ PRInt32 o1, o2;
+} CMonShared;
+
+typedef struct MonShared
+{
+ PRMonitor *o1, *o2;
+} MonShared;
+
+typedef struct LockShared
+{
+ PRLock *o1, *o2;
+ PRCondVar *cv1, *cv2;
+} LockShared;
+
+static void LogNow(const char *msg, PRStatus rv)
+{
+ PRIntervalTime now = PR_IntervalNow();
+ PR_Lock(ml);
+ PR_fprintf(err, "%6ld: %s", (now - base), msg);
+ if (PR_FAILURE == rv) PL_FPrintError(err, " ");
+ else PR_fprintf(err, "\n");
+ PR_Unlock(ml);
+} /* LogNow */
+
+static void Help(void)
+{
+ PR_fprintf(err, "Usage: [-[d][l][m][c]] [-h]\n");
+ PR_fprintf(err, "\t-d debug mode (default: FALSE)\n");
+ PR_fprintf(err, "\t-l test with locks (default: FALSE)\n");
+ PR_fprintf(err, "\t-m tests with monitors (default: FALSE)\n");
+ PR_fprintf(err, "\t-c tests with cmonitors (default: FALSE)\n");
+ PR_fprintf(err, "\t-h help\n");
+} /* Help */
+
+static void PR_CALLBACK T2CMon(void *arg)
+{
+ PRStatus rv;
+ CMonShared *shared = (CMonShared*)arg;
+
+ PR_CEnterMonitor(&shared->o1);
+ LogNow("T2 waiting 5 seconds on o1", PR_SUCCESS);
+ rv = PR_CWait(&shared->o1, PR_SecondsToInterval(5));
+ if (PR_SUCCESS == rv) LogNow("T2 resuming on o1", rv);
+ else LogNow("T2 wait failed on o1", rv);
+
+ rv = PR_CNotify(&shared->o1);
+ if (PR_SUCCESS == rv) LogNow("T2 notified o1", rv);
+ else LogNow("T2 notify on o1 failed", rv);
+
+ PR_CExitMonitor(&shared->o1);
+} /* T2CMon */
+
+static void PR_CALLBACK T3CMon(void *arg)
+{
+ PRStatus rv;
+ CMonShared *shared = (CMonShared*)arg;
+
+ PR_CEnterMonitor(&shared->o2);
+ LogNow("T3 waiting 5 seconds on o2", PR_SUCCESS);
+ rv = PR_CWait(&shared->o2, PR_SecondsToInterval(5));
+ if (PR_SUCCESS == rv) LogNow("T3 resuming on o2", rv);
+ else LogNow("T3 wait failed on o2", rv);
+ rv = PR_CNotify(&shared->o2);
+ LogNow("T3 notify on o2", rv);
+ PR_CExitMonitor(&shared->o2);
+
+} /* T3CMon */
+
+static CMonShared sharedCM;
+
+static void T1CMon(void)
+{
+ PRStatus rv;
+ PRThread *t2, *t3;
+
+ PR_fprintf(err, "\n**********************************\n");
+ PR_fprintf(err, " CACHED MONITORS\n");
+ PR_fprintf(err, "**********************************\n");
+
+ base = PR_IntervalNow();
+
+ PR_CEnterMonitor(&sharedCM.o1);
+ LogNow("T1 waiting 3 seconds on o1", PR_SUCCESS);
+ rv = PR_CWait(&sharedCM.o1, PR_SecondsToInterval(3));
+ if (PR_SUCCESS == rv) LogNow("T1 resuming on o1", rv);
+ else LogNow("T1 wait on o1 failed", rv);
+ PR_CExitMonitor(&sharedCM.o1);
+
+ LogNow("T1 creating T2", PR_SUCCESS);
+ t2 = PR_CreateThread(
+ PR_USER_THREAD, T2CMon, &sharedCM, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+
+ LogNow("T1 creating T3", PR_SUCCESS);
+ t3 = PR_CreateThread(
+ PR_USER_THREAD, T3CMon, &sharedCM, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+
+ PR_CEnterMonitor(&sharedCM.o2);
+ LogNow("T1 waiting forever on o2", PR_SUCCESS);
+ rv = PR_CWait(&sharedCM.o2, PR_INTERVAL_NO_TIMEOUT);
+ if (PR_SUCCESS == rv) LogNow("T1 resuming on o2", rv);
+ else LogNow("T1 wait on o2 failed", rv);
+ PR_CExitMonitor(&sharedCM.o2);
+
+ (void)PR_JoinThread(t2);
+ (void)PR_JoinThread(t3);
+
+} /* T1CMon */
+
+static void PR_CALLBACK T2Mon(void *arg)
+{
+ PRStatus rv;
+ MonShared *shared = (MonShared*)arg;
+
+ PR_EnterMonitor(shared->o1);
+ LogNow("T2 waiting 5 seconds on o1", PR_SUCCESS);
+ rv = PR_Wait(shared->o1, PR_SecondsToInterval(5));
+ if (PR_SUCCESS == rv) LogNow("T2 resuming on o1", rv);
+ else LogNow("T2 wait failed on o1", rv);
+
+ rv = PR_Notify(shared->o1);
+ if (PR_SUCCESS == rv) LogNow("T2 notified o1", rv);
+ else LogNow("T2 notify on o1 failed", rv);
+
+ PR_ExitMonitor(shared->o1);
+} /* T2Mon */
+
+static void PR_CALLBACK T3Mon(void *arg)
+{
+ PRStatus rv;
+ MonShared *shared = (MonShared*)arg;
+
+ PR_EnterMonitor(shared->o2);
+ LogNow("T3 waiting 5 seconds on o2", PR_SUCCESS);
+ rv = PR_Wait(shared->o2, PR_SecondsToInterval(5));
+ if (PR_SUCCESS == rv) LogNow("T3 resuming on o2", rv);
+ else LogNow("T3 wait failed on o2", rv);
+ rv = PR_Notify(shared->o2);
+ LogNow("T3 notify on o2", rv);
+ PR_ExitMonitor(shared->o2);
+
+} /* T3Mon */
+
+static MonShared sharedM;
+static void T1Mon(void)
+{
+ PRStatus rv;
+ PRThread *t2, *t3;
+
+ PR_fprintf(err, "\n**********************************\n");
+ PR_fprintf(err, " MONITORS\n");
+ PR_fprintf(err, "**********************************\n");
+
+ sharedM.o1 = PR_NewMonitor();
+ sharedM.o2 = PR_NewMonitor();
+
+ base = PR_IntervalNow();
+
+ PR_EnterMonitor(sharedM.o1);
+ LogNow("T1 waiting 3 seconds on o1", PR_SUCCESS);
+ rv = PR_Wait(sharedM.o1, PR_SecondsToInterval(3));
+ if (PR_SUCCESS == rv) LogNow("T1 resuming on o1", rv);
+ else LogNow("T1 wait on o1 failed", rv);
+ PR_ExitMonitor(sharedM.o1);
+
+ LogNow("T1 creating T2", PR_SUCCESS);
+ t2 = PR_CreateThread(
+ PR_USER_THREAD, T2Mon, &sharedM, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+
+ LogNow("T1 creating T3", PR_SUCCESS);
+ t3 = PR_CreateThread(
+ PR_USER_THREAD, T3Mon, &sharedM, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+
+ PR_EnterMonitor(sharedM.o2);
+ LogNow("T1 waiting forever on o2", PR_SUCCESS);
+ rv = PR_Wait(sharedM.o2, PR_INTERVAL_NO_TIMEOUT);
+ if (PR_SUCCESS == rv) LogNow("T1 resuming on o2", rv);
+ else LogNow("T1 wait on o2 failed", rv);
+ PR_ExitMonitor(sharedM.o2);
+
+ (void)PR_JoinThread(t2);
+ (void)PR_JoinThread(t3);
+
+ PR_DestroyMonitor(sharedM.o1);
+ PR_DestroyMonitor(sharedM.o2);
+
+} /* T1Mon */
+
+static void PR_CALLBACK T2Lock(void *arg)
+{
+ PRStatus rv;
+ LockShared *shared = (LockShared*)arg;
+
+ PR_Lock(shared->o1);
+ LogNow("T2 waiting 5 seconds on o1", PR_SUCCESS);
+ rv = PR_WaitCondVar(shared->cv1, PR_SecondsToInterval(5));
+ if (PR_SUCCESS == rv) LogNow("T2 resuming on o1", rv);
+ else LogNow("T2 wait failed on o1", rv);
+
+ rv = PR_NotifyCondVar(shared->cv1);
+ if (PR_SUCCESS == rv) LogNow("T2 notified o1", rv);
+ else LogNow("T2 notify on o1 failed", rv);
+
+ PR_Unlock(shared->o1);
+} /* T2Lock */
+
+static void PR_CALLBACK T3Lock(void *arg)
+{
+ PRStatus rv;
+ LockShared *shared = (LockShared*)arg;
+
+ PR_Lock(shared->o2);
+ LogNow("T3 waiting 5 seconds on o2", PR_SUCCESS);
+ rv = PR_WaitCondVar(shared->cv2, PR_SecondsToInterval(5));
+ if (PR_SUCCESS == rv) LogNow("T3 resuming on o2", rv);
+ else LogNow("T3 wait failed on o2", rv);
+ rv = PR_NotifyCondVar(shared->cv2);
+ LogNow("T3 notify on o2", rv);
+ PR_Unlock(shared->o2);
+
+} /* T3Lock */
+
+/*
+** Make shared' a static variable for Win16
+*/
+static LockShared sharedL;
+
+static void T1Lock(void)
+{
+ PRStatus rv;
+ PRThread *t2, *t3;
+ sharedL.o1 = PR_NewLock();
+ sharedL.o2 = PR_NewLock();
+ sharedL.cv1 = PR_NewCondVar(sharedL.o1);
+ sharedL.cv2 = PR_NewCondVar(sharedL.o2);
+
+ PR_fprintf(err, "\n**********************************\n");
+ PR_fprintf(err, " LOCKS\n");
+ PR_fprintf(err, "**********************************\n");
+
+ base = PR_IntervalNow();
+
+ PR_Lock(sharedL.o1);
+ LogNow("T1 waiting 3 seconds on o1", PR_SUCCESS);
+ rv = PR_WaitCondVar(sharedL.cv1, PR_SecondsToInterval(3));
+ if (PR_SUCCESS == rv) LogNow("T1 resuming on o1", rv);
+ else LogNow("T1 wait on o1 failed", rv);
+ PR_Unlock(sharedL.o1);
+
+ LogNow("T1 creating T2", PR_SUCCESS);
+ t2 = PR_CreateThread(
+ PR_USER_THREAD, T2Lock, &sharedL, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+
+ LogNow("T1 creating T3", PR_SUCCESS);
+ t3 = PR_CreateThread(
+ PR_USER_THREAD, T3Lock, &sharedL, PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+
+ PR_Lock(sharedL.o2);
+ LogNow("T1 waiting forever on o2", PR_SUCCESS);
+ rv = PR_WaitCondVar(sharedL.cv2, PR_INTERVAL_NO_TIMEOUT);
+ if (PR_SUCCESS == rv) LogNow("T1 resuming on o2", rv);
+ else LogNow("T1 wait on o2 failed", rv);
+ PR_Unlock(sharedL.o2);
+
+ (void)PR_JoinThread(t2);
+ (void)PR_JoinThread(t3);
+
+ PR_DestroyLock(sharedL.o1);
+ PR_DestroyLock(sharedL.o2);
+ PR_DestroyCondVar(sharedL.cv1);
+ PR_DestroyCondVar(sharedL.cv2);
+} /* T1Lock */
+
+static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv )
+{
+ PLOptStatus os;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "dhlmc");
+ PRBool locks = PR_FALSE, monitors = PR_FALSE, cmonitors = PR_FALSE;
+
+ err = PR_GetSpecialFD(PR_StandardError);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 'd': /* debug mode (noop) */
+ break;
+ case 'l': /* locks */
+ locks = PR_TRUE;
+ break;
+ case 'm': /* monitors */
+ monitors = PR_TRUE;
+ break;
+ case 'c': /* cached monitors */
+ cmonitors = PR_TRUE;
+ break;
+ case 'h': /* needs guidance */
+ default:
+ Help();
+ return 2;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ ml = PR_NewLock();
+ if (locks) T1Lock();
+ if (monitors) T1Mon();
+ if (cmonitors) T1CMon();
+
+ PR_DestroyLock(ml);
+
+ PR_fprintf(err, "Done!\n");
+ return 0;
+} /* main */
+
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PRIntn rv;
+
+ PR_STDIO_INIT();
+ rv = PR_Initialize(RealMain, argc, argv, 0);
+ return rv;
+} /* main */
+/* xnotify.c */
diff --git a/pr/tests/yield.c b/pr/tests/yield.c
new file mode 100644
index 00000000..7f9d4548
--- /dev/null
+++ b/pr/tests/yield.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 <stdio.h>
+#include "prthread.h"
+#include "prinit.h"
+
+#define THREADS 10
+
+
+void
+threadmain(void *_id)
+{
+ int id = (int)_id;
+ int index;
+
+ printf("thread %d alive\n", id);
+ for (index=0; index<10; index++) {
+ printf("thread %d yielding\n", id);
+ PR_Sleep(0);
+ printf("thread %d awake\n", id);
+ }
+ printf("thread %d dead\n", id);
+
+}
+
+main()
+{
+ int index;
+ PRThread *a[THREADS];
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 5);
+ PR_STDIO_INIT();
+
+ for (index=0; index<THREADS; index++) {
+ a[index] = PR_CreateThread(PR_USER_THREAD,
+ threadmain,
+ (void *)index,
+ PR_PRIORITY_NORMAL,
+ index%2?PR_LOCAL_THREAD:PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+ }
+ for(index=0; index<THREADS; index++)
+ PR_JoinThread(a[index]);
+ printf("main dying\n");
+}
diff --git a/tools/Makefile b/tools/Makefile
new file mode 100644
index 00000000..8477d866
--- /dev/null
+++ b/tools/Makefile
@@ -0,0 +1,212 @@
+#
+# 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)
+OS_CFLAGS = $(OS_EXE_CFLAGS)
+endif
+
+
+DIRS =
+
+CSRCS = \
+ httpget.c \
+ tail.c \
+ $(NULL)
+
+ifeq ($(OS_ARCH), WINNT)
+PROG_SUFFIX = .exe
+else
+PROG_SUFFIX =
+endif
+
+PROGS = $(addprefix $(OBJDIR)/, $(CSRCS:.c=$(PROG_SUFFIX)))
+
+TARGETS = $(PROGS)
+
+INCLUDES = -I$(DIST)/include/private -I$(DIST)/include
+
+NSPR_VERSION = 21
+
+# Setting the variables LDOPTS and LIBPR. We first initialize
+# them to the default values, then adjust them for some platforms.
+LDOPTS = -L$(DIST)/lib
+LIBPR = -lnspr$(NSPR_VERSION)
+LIBPLC = -lplc$(NSPR_VERSION)
+
+ifeq ($(OS_ARCH), WINNT)
+ifeq ($(OS_TARGET), WIN16)
+ LIBPR = $(DIST)/lib/nspr$(NSPR_VERSION).lib
+ LIBPLC= $(DIST)/lib/plc$(NSPR_VERSION).lib
+else
+LDOPTS = -NOLOGO -DEBUG -DEBUGTYPE:CV -INCREMENTAL:NO
+LIBPR = $(DIST)/lib/libnspr$(NSPR_VERSION).$(LIB_SUFFIX)
+LIBPLC= $(DIST)/lib/libplc$(NSPR_VERSION).$(LIB_SUFFIX)
+endif
+endif
+
+ifneq ($(OS_ARCH), WINNT)
+PWD = $(shell pwd)
+endif
+
+ifeq ($(OS_ARCH), IRIX)
+LDOPTS += -rpath $(PWD)/$(DIST)/lib
+endif
+
+ifeq ($(OS_ARCH), OSF1)
+LDOPTS += -rpath $(PWD)/$(DIST)/lib -lpthread
+endif
+
+ifeq ($(OS_ARCH), HP-UX)
+LDOPTS += -Wl,+s,+b,$(PWD)/$(DIST)/lib
+endif
+
+# AIX
+ifeq ($(OS_ARCH),AIX)
+LDOPTS += -blibpath:.:$(PWD)/$(DIST)/lib:/usr/lib/threads:/usr/lpp/xlC/lib:/usr/lib:/lib
+LIBPR = -lnspr$(NSPR_VERSION)_shr
+LIBPLC = -lplc$(NSPR_VERSION)_shr
+endif
+
+# Solaris
+ifeq ($(OS_ARCH), SunOS)
+ifneq ($(OS_RELEASE), 4.1.3_U1)
+ifdef NS_USE_GCC
+LDOPTS += -Xlinker -R -Xlinker $(PWD)/$(DIST)/lib
+else
+LDOPTS += -R $(PWD)/$(DIST)/lib
+endif
+endif
+
+# SunOS 5.4 and 5.5 need to link with -lthread or -lpthread,
+# even though we already linked with these system libraries
+# when we built libnspr.so.
+ifeq ($(OS_RELEASE), 5.4)
+EXTRA_LIBS = -lthread
+endif
+
+ifeq ($(OS_RELEASE), 5.5)
+ifdef USE_PTHREADS
+EXTRA_LIBS = -lpthread
+else
+EXTRA_LIBS = -lthread
+endif
+endif
+endif # SunOS
+
+ifeq ($(OS_ARCH), NCR)
+# XXX: We see some strange problems when we link with libnspr.so.
+# So for now we use static libraries on NCR. The shared library
+# stuff below is commented out.
+LIBPR = $(DIST)/lib/libnspr$(NSPR_VERSION).a
+LIBPLC = $(DIST)/lib/libplc$(NSPR_VERSION).a
+EXTRA_LIBS = -lsocket -lnsl -ldl
+
+# NCR needs to link against -lsocket -lnsl (and -lc, which is linked
+# implicitly by $(CC)) again even though we already linked with these
+# system libraries when we built libnspr.so.
+#EXTRA_LIBS = -lsocket -lnsl
+# This hardcodes in the executable programs the directory to find
+# libnspr.so etc. at program startup. Equivalent to the -R or -rpath
+# option for ld on other platforms.
+#export LD_RUN_PATH = $(PWD)/$(DIST)/lib
+endif
+
+ifeq ($(OS_ARCH), SCOOS)
+# SCO Unix needs to link against -lsocket again even though we
+# already linked with these system libraries when we built libnspr.so.
+EXTRA_LIBS = -lsocket
+# This hardcodes in the executable programs the directory to find
+# libnspr.so etc. at program startup. Equivalent to the -R or -rpath
+# option for ld on other platforms.
+export LD_RUN_PATH = $(PWD)/$(DIST)/lib
+endif
+
+#####################################################
+#
+# The rules
+#
+#####################################################
+
+include $(MOD_DEPTH)/config/rules.mk
+
+AIX_PRE_4_2 = 0
+ifeq ($(OS_ARCH),AIX)
+ifneq ($(OS_RELEASE),4.2)
+ifneq ($(USE_PTHREADS), 1)
+#AIX_PRE_4_2 = 1
+endif
+endif
+endif
+
+ifeq ($(AIX_PRE_4_2),1)
+
+# AIX releases prior to 4.2 need a special two-step linking hack
+# in order to both override the system select() and be able to
+# get at the original system select().
+#
+# We use a pattern rule in ns/nspr20/config/rules.mk to generate
+# the .$(OBJ_SUFFIX) file from the .c source file, then do the
+# two-step linking hack below.
+
+$(OBJDIR)/%: $(OBJDIR)/%.$(OBJ_SUFFIX)
+ @$(MAKE_OBJDIR)
+ rm -f $@ $(AIX_TMP)
+ $(CC) $(AIX_LINK_OPTS) -o $(AIX_TMP) $< $(DIST)/lib/libnspr$(NSPR_VERSION).a
+ $(CC) -o $@ $(AIX_TMP) $(AIX_WRAP)
+ rm -f $(AIX_TMP)
+
+else
+
+# All platforms that are not AIX pre-4.2.
+
+$(OBJDIR)/%$(PROG_SUFFIX): $(OBJDIR)/%.$(OBJ_SUFFIX)
+ @$(MAKE_OBJDIR)
+ifeq ($(OS_ARCH), WINNT)
+ifeq ($(OS_TARGET),WIN16)
+ echo system windows >w16link
+ echo option map >>w16link
+ echo option stack=10K >>w16link
+ echo option heapsize=32K >>w16link
+ echo debug $(DEBUGTYPE) all >>w16link
+ echo name $@ >>w16link
+ echo file >>w16link
+ echo $< >>w16link
+ echo library >>w16link
+ echo $(LIBPR), >>w16link
+ echo $(LIBPLC), >>w16link
+ echo winsock.lib >>w16link
+ wlink @w16link.
+else
+ link $(LDOPTS) $< $(LIBPR) $(LIBPLC) wsock32.lib -out:$@
+endif
+else
+ $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPR) $(LIBPLC) $(EXTRA_LIBS) -o $@
+endif
+
+endif
+
+export:: $(TARGETS)
+install:: export
+clean::
+ rm -f $(TARGETS)
+
diff --git a/tools/httpget.c b/tools/httpget.c
new file mode 100644
index 00000000..5c5602b4
--- /dev/null
+++ b/tools/httpget.c
@@ -0,0 +1,446 @@
+/* -*- 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.
+ */
+
+/*
+ * Author: Wan-Teh Chang
+ *
+ * Given an HTTP URL, httpget uses the GET method to fetch the file.
+ * The fetched file is written to stdout by default, or can be
+ * saved in an output file.
+ *
+ * This is a single-threaded program.
+ */
+
+#include "prio.h"
+#include "prnetdb.h"
+#include "prlog.h"
+#include "prerror.h"
+#include "prprf.h"
+#include "prinit.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h> /* for atoi */
+
+#define FCOPY_BUFFER_SIZE (16 * 1024)
+#define INPUT_BUFFER_SIZE 1024
+#define LINE_SIZE 512
+#define HOST_SIZE 256
+#define PORT_SIZE 32
+#define PATH_SIZE 512
+
+/*
+ * A buffer for storing the excess input data for ReadLine.
+ * The data in the buffer starts from (including) the element pointed to
+ * by inputHead, and ends just before (not including) the element pointed
+ * to by inputTail. The buffer is empty if inputHead == inputTail.
+ */
+
+static char inputBuf[INPUT_BUFFER_SIZE];
+/*
+ * inputBufEnd points just past the end of inputBuf
+ */
+static char *inputBufEnd = inputBuf + sizeof(inputBuf);
+static char *inputHead = inputBuf;
+static char *inputTail = inputBuf;
+
+static PRBool endOfStream = PR_FALSE;
+
+/*
+ * ReadLine --
+ *
+ * Read in a line of text, terminated by CRLF or LF, from fd into buf.
+ * The terminating CRLF or LF is included (always as '\n'). The text
+ * in buf is terminated by a null byte. The excess bytes are stored in
+ * inputBuf for use in the next ReadLine call or FetchFile call.
+ * Returns the number of bytes in buf. 0 means end of stream. Returns
+ * -1 if read fails.
+ */
+
+PRInt32 ReadLine(PRFileDesc *fd, char *buf, PRUint32 bufSize)
+{
+ char *dst = buf;
+ char *bufEnd = buf + bufSize; /* just past the end of buf */
+ PRBool lineFound = PR_FALSE;
+ char *crPtr = NULL; /* points to the CR ('\r') character */
+ PRInt32 nRead;
+
+loop:
+ PR_ASSERT(inputBuf <= inputHead && inputHead <= inputTail
+ && inputTail <= inputBufEnd);
+ while (lineFound == PR_FALSE && inputHead != inputTail
+ && dst < bufEnd - 1) {
+ if (*inputHead == '\r') {
+ crPtr = dst;
+ } else if (*inputHead == '\n') {
+ lineFound = PR_TRUE;
+ if (crPtr == dst - 1) {
+ dst--;
+ }
+ }
+ *(dst++) = *(inputHead++);
+ }
+ if (lineFound == PR_TRUE || dst == bufEnd - 1 || endOfStream == PR_TRUE) {
+ *dst = '\0';
+ return dst - buf;
+ }
+
+ /*
+ * The input buffer should be empty now
+ */
+ PR_ASSERT(inputHead == inputTail);
+
+ nRead = PR_Read(fd, inputBuf, sizeof(inputBuf));
+ if (nRead == -1) {
+ *dst = '\0';
+ return -1;
+ } else if (nRead == 0) {
+ endOfStream = PR_TRUE;
+ *dst = '\0';
+ return dst - buf;
+ }
+ inputHead = inputBuf;
+ inputTail = inputBuf + nRead;
+ goto loop;
+}
+
+PRInt32 DrainInputBuffer(char *buf, PRUint32 bufSize)
+{
+ PRInt32 nBytes = inputTail - inputHead;
+
+ if (nBytes == 0) {
+ if (endOfStream) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ if ((PRInt32) bufSize < nBytes) {
+ nBytes = bufSize;
+ }
+ memcpy(buf, inputHead, nBytes);
+ inputHead += nBytes;
+ return nBytes;
+}
+
+PRStatus FetchFile(PRFileDesc *in, PRFileDesc *out)
+{
+ char buf[FCOPY_BUFFER_SIZE];
+ PRInt32 nBytes;
+
+ while ((nBytes = DrainInputBuffer(buf, sizeof(buf))) > 0) {
+ if (PR_Write(out, buf, nBytes) != nBytes) {
+ fprintf(stderr, "httpget: cannot write to file\n");
+ return PR_FAILURE;
+ }
+ }
+ if (nBytes < 0) {
+ /* Input buffer is empty and end of stream */
+ return PR_SUCCESS;
+ }
+ while ((nBytes = PR_Read(in, buf, sizeof(buf))) > 0) {
+ if (PR_Write(out, buf, nBytes) != nBytes) {
+ fprintf(stderr, "httpget: cannot write to file\n");
+ return PR_FAILURE;
+ }
+ }
+ if (nBytes < 0) {
+ fprintf(stderr, "httpget: cannot read from socket\n");
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+PRStatus FastFetchFile(PRFileDesc *in, PRFileDesc *out, PRUint32 size)
+{
+ PRInt32 nBytes;
+ PRFileMap *outfMap;
+ void *addr;
+ char *start;
+ PRUint32 rem;
+ PRUint32 bytesToRead;
+ PRStatus rv;
+ PRInt64 sz64;
+
+ LL_UI2L(sz64, size);
+ outfMap = PR_CreateFileMap(out, sz64, PR_PROT_READWRITE);
+ PR_ASSERT(outfMap);
+ addr = PR_MemMap(outfMap, LL_ZERO, size);
+ if (addr == (void *) -1) {
+ fprintf(stderr, "cannot memory-map file: (%d, %d)\n", PR_GetError(),
+ PR_GetOSError());
+
+ PR_CloseFileMap(outfMap);
+ return PR_FAILURE;
+ }
+ PR_ASSERT(addr != (void *) -1);
+ start = (char *) addr;
+ rem = size;
+ while ((nBytes = DrainInputBuffer(start, rem)) > 0) {
+ start += nBytes;
+ rem -= nBytes;
+ }
+ if (nBytes < 0) {
+ /* Input buffer is empty and end of stream */
+ return PR_SUCCESS;
+ }
+ bytesToRead = (rem < FCOPY_BUFFER_SIZE) ? rem : FCOPY_BUFFER_SIZE;
+ while (rem > 0 && (nBytes = PR_Read(in, start, bytesToRead)) > 0) {
+ start += nBytes;
+ rem -= nBytes;
+ bytesToRead = (rem < FCOPY_BUFFER_SIZE) ? rem : FCOPY_BUFFER_SIZE;
+ }
+ if (nBytes < 0) {
+ fprintf(stderr, "httpget: cannot read from socket\n");
+ return PR_FAILURE;
+ }
+ rv = PR_MemUnmap(addr, size);
+ PR_ASSERT(rv == PR_SUCCESS);
+ rv = PR_CloseFileMap(outfMap);
+ PR_ASSERT(rv == PR_SUCCESS);
+ return PR_SUCCESS;
+}
+
+PRStatus ParseURL(char *url, char *host, PRUint32 hostSize,
+ char *port, PRUint32 portSize, char *path, PRUint32 pathSize)
+{
+ char *start, *end;
+ char *dst;
+ char *hostEnd;
+ char *portEnd;
+ char *pathEnd;
+
+ if (strncmp(url, "http", 4)) {
+ fprintf(stderr, "httpget: the protocol must be http\n");
+ return PR_FAILURE;
+ }
+ if (strncmp(url + 4, "://", 3) || url[7] == '\0') {
+ fprintf(stderr, "httpget: malformed URL: %s\n", url);
+ return PR_FAILURE;
+ }
+
+ start = end = url + 7;
+ dst = host;
+ hostEnd = host + hostSize;
+ while (*end && *end != ':' && *end != '/') {
+ if (dst == hostEnd - 1) {
+ fprintf(stderr, "httpget: host name too long\n");
+ return PR_FAILURE;
+ }
+ *(dst++) = *(end++);
+ }
+ *dst = '\0';
+
+ if (*end == '\0') {
+ PR_snprintf(port, portSize, "%d", 80);
+ PR_snprintf(path, pathSize, "%s", "/");
+ return PR_SUCCESS;
+ }
+
+ if (*end == ':') {
+ end++;
+ dst = port;
+ portEnd = port + portSize;
+ while (*end && *end != '/') {
+ if (dst == portEnd - 1) {
+ fprintf(stderr, "httpget: port number too long\n");
+ return PR_FAILURE;
+ }
+ *(dst++) = *(end++);
+ }
+ *dst = '\0';
+ if (*end == '\0') {
+ PR_snprintf(path, pathSize, "%s", "/");
+ return PR_SUCCESS;
+ }
+ } else {
+ PR_snprintf(port, portSize, "%d", 80);
+ }
+
+ dst = path;
+ pathEnd = path + pathSize;
+ while (*end) {
+ if (dst == pathEnd - 1) {
+ fprintf(stderr, "httpget: file pathname too long\n");
+ return PR_FAILURE;
+ }
+ *(dst++) = *(end++);
+ }
+ *dst = '\0';
+ return PR_SUCCESS;
+}
+
+void PrintUsage(void) {
+ fprintf(stderr, "usage: httpget url\n"
+ " httpget -o outputfile url\n"
+ " httpget url -o outputfile\n");
+}
+
+int main(int argc, char **argv)
+{
+ PRHostEnt hostentry;
+ char buf[PR_NETDB_BUF_SIZE];
+ PRNetAddr addr;
+ PRFileDesc *socket = NULL, *file = NULL;
+ PRIntn cmdSize;
+ char host[HOST_SIZE];
+ char port[PORT_SIZE];
+ char path[PATH_SIZE];
+ char line[LINE_SIZE];
+ int exitStatus = 0;
+ PRBool endOfHeader = PR_FALSE;
+ char *url;
+ char *fileName = NULL;
+ PRUint32 fileSize;
+
+ if (argc != 2 && argc != 4) {
+ PrintUsage();
+ exit(1);
+ }
+
+ if (argc == 2) {
+ /*
+ * case 1: httpget url
+ */
+ url = argv[1];
+ } else {
+ if (strcmp(argv[1], "-o") == 0) {
+ /*
+ * case 2: httpget -o outputfile url
+ */
+ fileName = argv[2];
+ url = argv[3];
+ } else {
+ /*
+ * case 3: httpget url -o outputfile
+ */
+ url = argv[1];
+ if (strcmp(argv[2], "-o") != 0) {
+ PrintUsage();
+ exit(1);
+ }
+ fileName = argv[3];
+ }
+ }
+
+ if (ParseURL(url, host, sizeof(host), port, sizeof(port),
+ path, sizeof(path)) == PR_FAILURE) {
+ exit(1);
+ }
+
+ if (PR_GetHostByName(host, buf, sizeof(buf), &hostentry)
+ == PR_FAILURE) {
+ fprintf(stderr, "httpget: unknown host name: %s\n", host);
+ exit(1);
+ }
+
+ addr.inet.family = PR_AF_INET;
+ addr.inet.port = PR_htons((short) atoi(port));
+ addr.inet.ip = *((PRUint32 *) hostentry.h_addr_list[0]);
+
+ socket = PR_NewTCPSocket();
+ if (socket == NULL) {
+ fprintf(stderr, "httpget: cannot create new tcp socket\n");
+ exit(1);
+ }
+
+ if (PR_Connect(socket, &addr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
+ fprintf(stderr, "httpget: cannot connect to http server\n");
+ exitStatus = 1;
+ goto done;
+ }
+
+ if (fileName == NULL) {
+ file = PR_STDOUT;
+ } else {
+ file = PR_Open(fileName, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE,
+ 00777);
+ if (file == NULL) {
+ fprintf(stderr, "httpget: cannot open file %s: (%d, %d)\n",
+ fileName, PR_GetError(), PR_GetOSError());
+ exitStatus = 1;
+ goto done;
+ }
+ }
+
+ cmdSize = PR_snprintf(buf, sizeof(buf), "GET %s HTTP/1.0\r\n\r\n", path);
+ PR_ASSERT(cmdSize == (PRIntn) strlen("GET HTTP/1.0\r\n\r\n")
+ + (PRIntn) strlen(path));
+ if (PR_Write(socket, buf, cmdSize) != cmdSize) {
+ fprintf(stderr, "httpget: cannot write to http server\n");
+ exitStatus = 1;
+ goto done;
+ }
+
+ if (ReadLine(socket, line, sizeof(line)) <= 0) {
+ fprintf(stderr, "httpget: cannot read line from http server\n");
+ exitStatus = 1;
+ goto done;
+ }
+
+ /* HTTP response: 200 == OK */
+ if (strstr(line, "200") == NULL) {
+ fprintf(stderr, "httpget: %s\n", line);
+ exitStatus = 1;
+ goto done;
+ }
+
+ while (ReadLine(socket, line, sizeof(line)) > 0) {
+ if (line[0] == '\n') {
+ endOfHeader = PR_TRUE;
+ break;
+ }
+ if (strncmp(line, "Content-Length", 14) == 0
+ || strncmp(line, "Content-length", 14) == 0) {
+ char *p = line + 14;
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+ if (*p != ':') {
+ continue;
+ }
+ p++;
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+ fileSize = 0;
+ while ('0' <= *p && *p <= '9') {
+ fileSize = 10 * fileSize + (*p - '0');
+ p++;
+ }
+ }
+ }
+ if (endOfHeader == PR_FALSE) {
+ fprintf(stderr, "httpget: cannot read line from http server\n");
+ exitStatus = 1;
+ goto done;
+ }
+
+ if (fileName == NULL || fileSize == 0) {
+ FetchFile(socket, file);
+ } else {
+ FastFetchFile(socket, file, fileSize);
+ }
+
+done:
+ if (socket) PR_Close(socket);
+ if (file) PR_Close(file);
+ PR_Cleanup();
+ return exitStatus;
+}
diff --git a/tools/tail.c b/tools/tail.c
new file mode 100644
index 00000000..25ecb69e
--- /dev/null
+++ b/tools/tail.c
@@ -0,0 +1,147 @@
+/* -*- 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 "prio.h"
+#include "prprf.h"
+#include "prinit.h"
+#include "prthread.h"
+#include "prinrval.h"
+
+#include "plerror.h"
+#include "plgetopt.h"
+
+#include <stdlib.h>
+
+#define BUFFER_SIZE 500
+
+static PRFileDesc *out = NULL, *err = NULL;
+
+static void Help(void)
+{
+ PR_fprintf(err, "Usage: tail [-n <n>] [-f] [-h] <filename>\n");
+ PR_fprintf(err, "\t-t <n> Dally time in milliseconds\n");
+ PR_fprintf(err, "\t-n <n> Number of bytes before <eof>\n");
+ PR_fprintf(err, "\t-f Follow the <eof>\n");
+ PR_fprintf(err, "\t-h This message and nothing else\n");
+} /* Help */
+
+PRIntn main(PRIntn argc, char **argv)
+{
+ PRIntn rv = 0;
+ PLOptStatus os;
+ PRStatus status;
+ PRFileDesc *file;
+ PRFileInfo fileInfo;
+ PRIntervalTime dally;
+ char buffer[BUFFER_SIZE];
+ PRBool follow = PR_FALSE;
+ const char *filename = NULL;
+ PRUint32 position = 0, seek = 0, time = 0;
+ PLOptState *opt = PL_CreateOptState(argc, argv, "hfn:");
+
+ out = PR_GetSpecialFD(PR_StandardOutput);
+ err = PR_GetSpecialFD(PR_StandardError);
+
+ while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
+ {
+ if (PL_OPT_BAD == os) continue;
+ switch (opt->option)
+ {
+ case 0: /* it's the filename */
+ filename = opt->value;
+ break;
+ case 'n': /* bytes before end of file */
+ seek = atoi(opt->value);
+ break;
+ case 't': /* dally time */
+ time = atoi(opt->value);
+ break;
+ case 'f': /* follow the end of file */
+ follow = PR_TRUE;
+ break;
+ case 'h': /* user wants some guidance */
+ Help(); /* so give him an earful */
+ return 2; /* but not a lot else */
+ break;
+ default:
+ break;
+ }
+ }
+ PL_DestroyOptState(opt);
+
+ if (0 == time) time = 1000;
+ dally = PR_MillisecondsToInterval(time);
+
+ if (NULL == filename)
+ {
+ (void)PR_fprintf(out, "Input file not specified\n");
+ rv = 1; goto done;
+ }
+ file = PR_Open(filename, PR_RDONLY, 0);
+ if (NULL == file)
+ {
+ PL_FPrintError(err, "File cannot be opened for reading");
+ return 1;
+ }
+
+ status = PR_GetOpenFileInfo(file, &fileInfo);
+ if (PR_FAILURE == status)
+ {
+ PL_FPrintError(err, "Cannot acquire status of file");
+ rv = 1; goto done;
+ }
+ if (seek > 0)
+ {
+ if (seek > fileInfo.size) seek = 0;
+ position = PR_Seek(file, (fileInfo.size - seek), PR_SEEK_SET);
+ if (-1 == (PRInt32)position)
+ PL_FPrintError(err, "Cannot seek to starting position");
+ }
+
+ do
+ {
+ while (position < fileInfo.size)
+ {
+ PRInt32 read, bytes = fileInfo.size - position;
+ if (bytes > sizeof(buffer)) bytes = sizeof(buffer);
+ read = PR_Read(file, buffer, bytes);
+ if (read != bytes)
+ PL_FPrintError(err, "Cannot read to eof");
+ position += read;
+ PR_Write(out, buffer, read);
+ }
+
+ if (follow)
+ {
+ PR_Sleep(dally);
+ status = PR_GetOpenFileInfo(file, &fileInfo);
+ if (PR_FAILURE == status)
+ {
+ PL_FPrintError(err, "Cannot acquire status of file");
+ rv = 1; goto done;
+ }
+ }
+ } while (follow);
+
+done:
+ PR_Close(file);
+
+ return rv;
+} /* main */
+
+/* tail.c */