summaryrefslogtreecommitdiffstats
path: root/nsprpub/pr/src/misc
diff options
context:
space:
mode:
Diffstat (limited to 'nsprpub/pr/src/misc')
-rw-r--r--nsprpub/pr/src/misc/Makefile.in80
-rw-r--r--nsprpub/pr/src/misc/compile-et.pl108
-rw-r--r--nsprpub/pr/src/misc/dtoa.c4610
-rw-r--r--nsprpub/pr/src/misc/pralarm.c263
-rw-r--r--nsprpub/pr/src/misc/pratom.c384
-rw-r--r--nsprpub/pr/src/misc/praton.c217
-rw-r--r--nsprpub/pr/src/misc/prcountr.c483
-rw-r--r--nsprpub/pr/src/misc/prdtoa.c3697
-rw-r--r--nsprpub/pr/src/misc/prenv.c170
-rw-r--r--nsprpub/pr/src/misc/prerr.c97
-rw-r--r--nsprpub/pr/src/misc/prerr.et108
-rw-r--r--nsprpub/pr/src/misc/prerr.properties85
-rw-r--r--nsprpub/pr/src/misc/prerror.c78
-rw-r--r--nsprpub/pr/src/misc/prerrortable.c209
-rw-r--r--nsprpub/pr/src/misc/prinit.c839
-rw-r--r--nsprpub/pr/src/misc/prinrval.c122
-rw-r--r--nsprpub/pr/src/misc/pripc.c100
-rw-r--r--nsprpub/pr/src/misc/pripcsem.c102
-rw-r--r--nsprpub/pr/src/misc/prlog2.c27
-rw-r--r--nsprpub/pr/src/misc/prlong.c254
-rw-r--r--nsprpub/pr/src/misc/prnetdb.c2482
-rw-r--r--nsprpub/pr/src/misc/prolock.c56
-rw-r--r--nsprpub/pr/src/misc/prrng.c44
-rw-r--r--nsprpub/pr/src/misc/prsystem.c369
-rw-r--r--nsprpub/pr/src/misc/prthinfo.c211
-rw-r--r--nsprpub/pr/src/misc/prtime.c2150
-rw-r--r--nsprpub/pr/src/misc/prtpool.c1236
-rw-r--r--nsprpub/pr/src/misc/prtrace.c901
28 files changed, 19482 insertions, 0 deletions
diff --git a/nsprpub/pr/src/misc/Makefile.in b/nsprpub/pr/src/misc/Makefile.in
new file mode 100644
index 0000000000..3d87da2091
--- /dev/null
+++ b/nsprpub/pr/src/misc/Makefile.in
@@ -0,0 +1,80 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#! gmake
+
+MOD_DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(MOD_DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/config.mk
+
+CSRCS = \
+ pralarm.c \
+ pratom.c \
+ prcountr.c \
+ prdtoa.c \
+ prenv.c \
+ prerr.c \
+ prerror.c \
+ prerrortable.c \
+ prinit.c \
+ prinrval.c \
+ pripc.c \
+ prlog2.c \
+ prlong.c \
+ prnetdb.c \
+ praton.c \
+ prolock.c \
+ prrng.c \
+ prsystem.c \
+ prtime.c \
+ prthinfo.c \
+ prtpool.c \
+ prtrace.c \
+ $(NULL)
+
+ifndef USE_PTHREADS
+CSRCS += \
+ pripcsem.c \
+ $(NULL)
+endif
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private
+
+DEFINES += -D_NSPR_BUILD_
+
+RELEASE_BINS = $(srcdir)/compile-et.pl $(srcdir)/prerr.properties
+
+include $(topsrcdir)/config/rules.mk
+
+# Prevent floating point errors caused by MSVC 6.0 Processor Pack
+# optimizations (bug 207421). This disables optimizations that
+# could change the precision of floating-point calculations for
+# this single compilation unit.
+ifeq ($(NS_USE_GCC)_$(OS_ARCH),_WINNT)
+$(OBJDIR)/prdtoa.$(OBJ_SUFFIX): prdtoa.c
+ @$(MAKE_OBJDIR)
+ifeq (,$(filter-out 1100 1200 1300 1310,$(MSC_VER)))
+ $(CC) -Fo$@ -c $(CFLAGS) -Op $(call pr_abspath,$<)
+else
+ $(CC) -Fo$@ -c $(CFLAGS) -fp:precise $(call pr_abspath,$<)
+endif
+endif
+
+#
+# Generate prerr.h, prerr.c, and prerr.properties from prerr.et.
+#
+build_prerr:
+ cd $(srcdir); $(PERL) compile-et.pl prerr.et
+
+export:: $(TARGETS)
+
+
diff --git a/nsprpub/pr/src/misc/compile-et.pl b/nsprpub/pr/src/misc/compile-et.pl
new file mode 100644
index 0000000000..50855298a4
--- /dev/null
+++ b/nsprpub/pr/src/misc/compile-et.pl
@@ -0,0 +1,108 @@
+#!/usr/bin/perl
+
+# usage: compile-et input.et
+
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+sub header
+{
+ local($filename, $comment) = @_;
+
+<<EOF
+$comment
+$comment $filename
+$comment This file is automatically generated; please do not edit it.
+EOF
+}
+
+sub table_base
+{
+ local($name) = @_;
+ local($base) = 0;
+
+ for ($i = 0; $i < length($name); $i++) {
+ $base *= 64;
+ $base += index("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_", substr($name, $i, 1)) + 1;
+ }
+ $base -= 0x1000000 if ($base > 0x7fffff);
+ $base*256;
+}
+
+sub code {
+ local($macro, $text) = @_;
+ $code = $table_base + $table_item_count;
+
+ print H "\n";
+ print H "/* ", $text, " */\n";
+ printf H "#define %-40s (%dL)\n", $macro, $code;
+
+ print C "\t{\"", $macro, "\", \"", $text, "\"},\n";
+
+ print PROPERTIES $macro, "=", $text, "\n";
+
+ $table_item_count++;
+}
+
+
+$filename = $ARGV[0];
+open(INPUT, "< $filename") || die "Can't read $filename: $!\n";
+
+$base = "$filename";
+$base =~ s/\.et$//;
+$base =~ s#.*/##;
+
+open(H, "> ${base}.h") || die "Can't write ${base}.h\n";
+open(C, "> ${base}.c") || die "Can't write ${base}.c\n";
+open(PROPERTIES, "> ${base}.properties") || die "Can't write ${base}.properties\n";
+
+print H "/*\n", &header("${base}.h", " *"), " */\n";
+print C "/*\n", &header("${base}.c", " *"), " */\n";
+print PROPERTIES &header("${base}.properties", "#");
+
+$skipone = 0;
+
+while ($_ = <INPUT>) {
+ next if /^#/;
+
+ if (/^[ \t]*(error_table|et)[ \t]+([a-zA-Z][a-zA-Z0-9_]+) *(-?[0-9]*)/) {
+ $table_name = $2;
+ if ($3) {
+ $table_base = $3;
+ }
+ else {
+ $table_base = &table_base($table_name);
+ }
+ $table_item_count = 0;
+
+ print C "#include \"prerror.h\"\n";
+ print C "static const struct PRErrorMessage text[] = {\n";
+ }
+ elsif (/^[ \t]*(error_code|ec)[ \t]+([A-Z_0-9]+),[ \t]*$/) {
+ $skipone = 1;
+ $macro = $2;
+ }
+ elsif (/^[ \t]*(error_code|ec)[ \t]+([A-Z_0-9]+),[ \t]*"(.*)"[ \t]*$/) {
+ &code($2, $3);
+ }
+ elsif ($skipone && /^[ \t]*"(.*)"[ \t]*$/) {
+ &code($macro, $1);
+ }
+}
+
+print H "\n";
+print H "extern void ", $table_name, "_InitializePRErrorTable","(void);\n";
+printf H "#define ERROR_TABLE_BASE_%s (%dL)\n", $table_name, $table_base;
+
+print C "\t{0, 0}\n";
+print C "};\n\n";
+printf C "static const struct PRErrorTable et = { text, \"%s\", %dL, %d };\n",
+ $base, $table_base, $table_item_count;
+print C "\n";
+print C "void ", $table_name, "_InitializePRErrorTable", "(void) {\n";
+print C " PR_ErrorInstallTable(&et);\n";
+print C "}\n";
+
+0;
diff --git a/nsprpub/pr/src/misc/dtoa.c b/nsprpub/pr/src/misc/dtoa.c
new file mode 100644
index 0000000000..43883aba26
--- /dev/null
+++ b/nsprpub/pr/src/misc/dtoa.c
@@ -0,0 +1,4610 @@
+/****************************************************************
+ *
+ * The author of this software is David M. Gay.
+ *
+ * Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
+ *
+ * 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 LUCENT 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 (dmg at acm dot org,
+ * with " at " changed at "@" and " dot " changed to "."). */
+
+/* On a machine with IEEE extended-precision registers, it is
+ * necessary to specify double-precision (53-bit) rounding precision
+ * before invoking strtod or dtoa. If the machine uses (the equivalent
+ * of) Intel 80x87 arithmetic, the call
+ * _control87(PC_53, MCW_PC);
+ * does this with many compilers. Whether this or another call is
+ * appropriate depends on the compiler; for this to work, it may be
+ * necessary to #include "float.h" or another system-dependent header
+ * file.
+ */
+
+/* strtod for IEEE-, VAX-, and IBM-arithmetic machines.
+ *
+ * This strtod returns a nearest machine number to the input decimal
+ * string (or sets errno to ERANGE). With IEEE arithmetic, ties are
+ * broken by the IEEE round-even rule. Otherwise ties are broken by
+ * biased rounding (add half and chop).
+ *
+ * Inspired loosely by William D. Clinger's paper "How to Read Floating
+ * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101].
+ *
+ * Modifications:
+ *
+ * 1. We only require IEEE, IBM, or VAX double-precision
+ * arithmetic (not IEEE double-extended).
+ * 2. We get by with floating-point arithmetic in a case that
+ * Clinger missed -- when we're computing d * 10^n
+ * for a small integer d and the integer n is not too
+ * much larger than 22 (the maximum integer k for which
+ * we can represent 10^k exactly), we may be able to
+ * compute (d*10^k) * 10^(e-k) with just one roundoff.
+ * 3. Rather than a bit-at-a-time adjustment of the binary
+ * result in the hard case, we use floating-point
+ * arithmetic to determine the adjustment to within
+ * one bit; only in really hard cases do we need to
+ * compute a second residual.
+ * 4. Because of 3., we don't need a large table of powers of 10
+ * for ten-to-e (just some small tables, e.g. of 10^k
+ * for 0 <= k <= 22).
+ */
+
+/*
+ * #define IEEE_8087 for IEEE-arithmetic machines where the least
+ * significant byte has the lowest address.
+ * #define IEEE_MC68k for IEEE-arithmetic machines where the most
+ * significant byte has the lowest address.
+ * #define Long int on machines with 32-bit ints and 64-bit longs.
+ * #define IBM for IBM mainframe-style floating-point arithmetic.
+ * #define VAX for VAX-style floating-point arithmetic (D_floating).
+ * #define No_leftright to omit left-right logic in fast floating-point
+ * computation of dtoa. This will cause dtoa modes 4 and 5 to be
+ * treated the same as modes 2 and 3 for some inputs.
+ * #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3
+ * and strtod and dtoa should round accordingly. Unless Trust_FLT_ROUNDS
+ * is also #defined, fegetround() will be queried for the rounding mode.
+ * Note that both FLT_ROUNDS and fegetround() are specified by the C99
+ * standard (and are specified to be consistent, with fesetround()
+ * affecting the value of FLT_ROUNDS), but that some (Linux) systems
+ * do not work correctly in this regard, so using fegetround() is more
+ * portable than using FLT_ROUNDS directly.
+ * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3
+ * and Honor_FLT_ROUNDS is not #defined.
+ * #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 and arithmetic
+ * that rounds toward +Infinity.
+ * #define ROUND_BIASED_without_Round_Up for IEEE-format with biased
+ * rounding when the underlying floating-point arithmetic uses
+ * unbiased rounding. This prevent using ordinary floating-point
+ * arithmetic when the result could be computed with one rounding error.
+ * #define Inaccurate_Divide for IEEE-format with correctly rounded
+ * products but inaccurate quotients, e.g., for Intel i860.
+ * #define NO_LONG_LONG on machines that do not have a "long long"
+ * integer type (of >= 64 bits). On such machines, you can
+ * #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. If long long is available and the name is
+ * something other than "long long", #define Llong to be the name,
+ * and if "unsigned Llong" does not work as an unsigned version of
+ * Llong, #define #ULLong to be the corresponding unsigned type.
+ * #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. Similarly, if you
+ * want something other than the system's free() to be called to
+ * recycle memory acquired from MALLOC, #define FREE to be the
+ * name of the alternate routine. (FREE or free is only called in
+ * pathological cases, e.g., in a dtoa call after a dtoa return in
+ * mode 3 with thousands of digits requested.)
+ * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making
+ * memory allocations from a private pool of memory when possible.
+ * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes,
+ * unless #defined to be a different length. This default length
+ * suffices to get rid of MALLOC calls except for unusual cases,
+ * such as decimal-to-binary conversion of a very long string of
+ * digits. The longest string dtoa can return is about 751 bytes
+ * long. For conversions by strtod of strings of 800 digits and
+ * all dtoa conversions in single-threaded executions with 8-byte
+ * pointers, PRIVATE_MEM >= 7400 appears to suffice; with 4-byte
+ * pointers, PRIVATE_MEM >= 7112 appears adequate.
+ * #define NO_INFNAN_CHECK if you do not wish to have INFNAN_CHECK
+ * #defined automatically on IEEE systems. On such systems,
+ * when INFNAN_CHECK is #defined, strtod checks
+ * for Infinity and NaN (case insensitively). On some systems
+ * (e.g., some HP systems), it may be necessary to #define NAN_WORD0
+ * appropriately -- to the most significant word of a quiet NaN.
+ * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.)
+ * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined,
+ * strtod also accepts (case insensitively) strings of the form
+ * NaN(x), where x is a string of hexadecimal digits and spaces;
+ * if there is only one string of hexadecimal digits, it is taken
+ * for the 52 fraction bits of the resulting NaN; if there are two
+ * or more strings of hex digits, the first is for the high 20 bits,
+ * the second and subsequent for the low 32 bits, with intervening
+ * white space ignored; but if this results in none of the 52
+ * fraction bits being on (an IEEE Infinity symbol), then NAN_WORD0
+ * and NAN_WORD1 are used instead.
+ * #define MULTIPLE_THREADS if the system offers preemptively scheduled
+ * multiple threads. In this case, you must provide (or suitably
+ * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed
+ * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed
+ * in pow5mult, ensures lazy evaluation of only one copy of high
+ * powers of 5; omitting this lock would introduce a small
+ * probability of wasting memory, but would otherwise be harmless.)
+ * You must also invoke freedtoa(s) to free the value s returned by
+ * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined.
+ * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that
+ * avoids underflows on inputs whose result does not underflow.
+ * If you #define NO_IEEE_Scale on a machine that uses IEEE-format
+ * floating-point numbers and flushes underflows to zero rather
+ * than implementing gradual underflow, then you must also #define
+ * Sudden_Underflow.
+ * #define USE_LOCALE to use the current locale's decimal_point value.
+ * #define SET_INEXACT if IEEE arithmetic is being used and extra
+ * computation should be done to set the inexact flag when the
+ * result is inexact and avoid setting inexact when the result
+ * is exact. In this case, dtoa.c must be compiled in
+ * an environment, perhaps provided by #include "dtoa.c" in a
+ * suitable wrapper, that defines two functions,
+ * int get_inexact(void);
+ * void clear_inexact(void);
+ * such that get_inexact() returns a nonzero value if the
+ * inexact bit is already set, and clear_inexact() sets the
+ * inexact bit to 0. When SET_INEXACT is #defined, strtod
+ * also does extra computations to set the underflow and overflow
+ * flags when appropriate (i.e., when the result is tiny and
+ * inexact or when it is a numeric value rounded to +-infinity).
+ * #define NO_ERRNO if strtod should not assign errno = ERANGE when
+ * the result overflows to +-Infinity or underflows to 0.
+ * #define NO_HEX_FP to omit recognition of hexadecimal floating-point
+ * values by strtod.
+ * #define NO_STRTOD_BIGCOMP (on IEEE-arithmetic systems only for now)
+ * to disable logic for "fast" testing of very long input strings
+ * to strtod. This testing proceeds by initially truncating the
+ * input string, then if necessary comparing the whole string with
+ * a decimal expansion to decide close cases. This logic is only
+ * used for input more than STRTOD_DIGLIM digits long (default 40).
+ */
+
+#ifndef Long
+#define Long long
+#endif
+#ifndef ULong
+typedef unsigned Long ULong;
+#endif
+
+#ifdef DEBUG
+#include "stdio.h"
+#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);}
+#endif
+
+#include "stdlib.h"
+#include "string.h"
+
+#ifdef USE_LOCALE
+#include "locale.h"
+#endif
+
+#ifdef Honor_FLT_ROUNDS
+#ifndef Trust_FLT_ROUNDS
+#include <fenv.h>
+#endif
+#endif
+
+#ifdef MALLOC
+#ifdef KR_headers
+extern char *MALLOC();
+#else
+extern void *MALLOC(size_t);
+#endif
+#else
+#define MALLOC malloc
+#endif
+
+#ifndef Omit_Private_Memory
+#ifndef PRIVATE_MEM
+#define PRIVATE_MEM 2304
+#endif
+#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double))
+static double private_mem[PRIVATE_mem], *pmem_next = private_mem;
+#endif
+
+#undef IEEE_Arith
+#undef Avoid_Underflow
+#ifdef IEEE_MC68k
+#define IEEE_Arith
+#endif
+#ifdef IEEE_8087
+#define IEEE_Arith
+#endif
+
+#ifdef IEEE_Arith
+#ifndef NO_INFNAN_CHECK
+#undef INFNAN_CHECK
+#define INFNAN_CHECK
+#endif
+#else
+#undef INFNAN_CHECK
+#define NO_STRTOD_BIGCOMP
+#endif
+
+#include "errno.h"
+
+#ifdef Bad_float_h
+
+#ifdef IEEE_Arith
+#define DBL_DIG 15
+#define DBL_MAX_10_EXP 308
+#define DBL_MAX_EXP 1024
+#define FLT_RADIX 2
+#endif /*IEEE_Arith*/
+
+#ifdef IBM
+#define DBL_DIG 16
+#define DBL_MAX_10_EXP 75
+#define DBL_MAX_EXP 63
+#define FLT_RADIX 16
+#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 DBL_MAX 1.7014118346046923e+38
+#endif
+
+#ifndef LONG_MAX
+#define LONG_MAX 2147483647
+#endif
+
+#else /* ifndef Bad_float_h */
+#include "float.h"
+#endif /* Bad_float_h */
+
+#ifndef __MATH_H__
+#include "math.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef CONST
+#ifdef KR_headers
+#define CONST /* blank */
+#else
+#define CONST const
+#endif
+#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
+
+typedef union {
+ double d;
+ ULong L[2];
+} U;
+
+#ifdef IEEE_8087
+#define word0(x) (x)->L[1]
+#define word1(x) (x)->L[0]
+#else
+#define word0(x) (x)->L[0]
+#define word1(x) (x)->L[1]
+#endif
+#define dval(x) (x)->d
+
+#ifndef STRTOD_DIGLIM
+#define STRTOD_DIGLIM 40
+#endif
+
+#ifdef DIGLIM_DEBUG
+extern int strtod_diglim;
+#else
+#define strtod_diglim STRTOD_DIGLIM
+#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) */
+
+#ifdef IEEE_Arith
+#define Exp_shift 20
+#define Exp_shift1 20
+#define Exp_msk1 0x100000
+#define Exp_msk11 0x100000
+#define Exp_mask 0x7ff00000
+#define P 53
+#define Nbits 53
+#define Bias 1023
+#define Emax 1023
+#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
+#ifndef NO_IEEE_Scale
+#define Avoid_Underflow
+#ifdef Flush_Denorm /* debugging option */
+#undef Sudden_Underflow
+#endif
+#endif
+
+#ifndef Flt_Rounds
+#ifdef FLT_ROUNDS
+#define Flt_Rounds FLT_ROUNDS
+#else
+#define Flt_Rounds 1
+#endif
+#endif /*Flt_Rounds*/
+
+#ifdef Honor_FLT_ROUNDS
+#undef Check_FLT_ROUNDS
+#define Check_FLT_ROUNDS
+#else
+#define Rounding Flt_Rounds
+#endif
+
+#else /* ifndef IEEE_Arith */
+#undef Check_FLT_ROUNDS
+#undef Honor_FLT_ROUNDS
+#undef SET_INEXACT
+#undef Sudden_Underflow
+#define Sudden_Underflow
+#ifdef IBM
+#undef Flt_Rounds
+#define Flt_Rounds 0
+#define Exp_shift 24
+#define Exp_shift1 24
+#define Exp_msk1 0x1000000
+#define Exp_msk11 0x1000000
+#define Exp_mask 0x7f000000
+#define P 14
+#define Nbits 56
+#define Bias 65
+#define Emax 248
+#define Emin (-260)
+#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 */
+#undef Flt_Rounds
+#define Flt_Rounds 1
+#define Exp_shift 23
+#define Exp_shift1 7
+#define Exp_msk1 0x80
+#define Exp_msk11 0x800000
+#define Exp_mask 0x7f80
+#define P 56
+#define Nbits 56
+#define Bias 129
+#define Emax 126
+#define Emin (-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 /* IBM, VAX */
+#endif /* IEEE_Arith */
+
+#ifndef IEEE_Arith
+#define ROUND_BIASED
+#else
+#ifdef ROUND_BIASED_without_Round_Up
+#undef ROUND_BIASED
+#define ROUND_BIASED
+#endif
+#endif
+
+#ifdef RND_PRODQUOT
+#define rounded_product(a,b) a = rnd_prod(a, b)
+#define rounded_quotient(a,b) a = rnd_quot(a, b)
+#ifdef KR_headers
+extern double rnd_prod(), rnd_quot();
+#else
+extern double rnd_prod(double, double), rnd_quot(double, double);
+#endif
+#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 Pack_32
+#define Pack_32
+#endif
+
+typedef struct BCinfo BCinfo;
+struct
+ BCinfo {
+ int dp0, dp1, dplen, dsign, e0, inexact, nd, nd0, rounding, scale, uflchk;
+};
+
+#ifdef KR_headers
+#define FFFFFFFF ((((unsigned long)0xffff)<<16)|(unsigned long)0xffff)
+#else
+#define FFFFFFFF 0xffffffffUL
+#endif
+
+#ifdef NO_LONG_LONG
+#undef ULLong
+#ifdef Just_16
+#undef Pack_32
+/* 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.
+ */
+#endif
+#else /* long long available */
+#ifndef Llong
+#define Llong long long
+#endif
+#ifndef ULLong
+#define ULLong unsigned Llong
+#endif
+#endif /* NO_LONG_LONG */
+
+#ifndef MULTIPLE_THREADS
+#define ACQUIRE_DTOA_LOCK(n) /*nothing*/
+#define FREE_DTOA_LOCK(n) /*nothing*/
+#endif
+
+#define Kmax 7
+
+#ifdef __cplusplus
+extern "C" double strtod(const char *s00, char **se);
+extern "C" char *dtoa(double d, int mode, int ndigits,
+ int *decpt, int *sign, char **rve);
+#endif
+
+struct
+ Bigint {
+ struct Bigint *next;
+ int k, maxwds, sign, wds;
+ ULong x[1];
+};
+
+typedef struct Bigint Bigint;
+
+static Bigint *freelist[Kmax+1];
+
+static Bigint *
+Balloc
+#ifdef KR_headers
+(k) int k;
+#else
+(int k)
+#endif
+{
+ int x;
+ Bigint *rv;
+#ifndef Omit_Private_Memory
+ unsigned int len;
+#endif
+
+ ACQUIRE_DTOA_LOCK(0);
+ /* The k > Kmax case does not need ACQUIRE_DTOA_LOCK(0), */
+ /* but this case seems very unlikely. */
+ if (k <= Kmax && (rv = freelist[k])) {
+ freelist[k] = rv->next;
+ }
+ else {
+ x = 1 << k;
+#ifdef Omit_Private_Memory
+ rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong));
+#else
+ len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1)
+ /sizeof(double);
+ if (k <= Kmax && pmem_next - private_mem + len <= PRIVATE_mem) {
+ rv = (Bigint*)pmem_next;
+ pmem_next += len;
+ }
+ else {
+ rv = (Bigint*)MALLOC(len*sizeof(double));
+ }
+#endif
+ rv->k = k;
+ rv->maxwds = x;
+ }
+ FREE_DTOA_LOCK(0);
+ rv->sign = rv->wds = 0;
+ return rv;
+}
+
+static void
+Bfree
+#ifdef KR_headers
+(v) Bigint *v;
+#else
+(Bigint *v)
+#endif
+{
+ if (v) {
+ if (v->k > Kmax)
+#ifdef FREE
+ FREE((void*)v);
+#else
+ free((void*)v);
+#endif
+ else {
+ ACQUIRE_DTOA_LOCK(0);
+ v->next = freelist[v->k];
+ freelist[v->k] = v;
+ FREE_DTOA_LOCK(0);
+ }
+ }
+}
+
+#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \
+y->wds*sizeof(Long) + 2*sizeof(int))
+
+static Bigint *
+multadd
+#ifdef KR_headers
+(b, m, a) Bigint *b; int m, a;
+#else
+(Bigint *b, int m, int a) /* multiply by m and add a */
+#endif
+{
+ int i, wds;
+#ifdef ULLong
+ ULong *x;
+ ULLong carry, y;
+#else
+ ULong carry, *x, y;
+#ifdef Pack_32
+ ULong xi, z;
+#endif
+#endif
+ Bigint *b1;
+
+ wds = b->wds;
+ x = b->x;
+ i = 0;
+ carry = a;
+ do {
+#ifdef ULLong
+ y = *x * (ULLong)m + carry;
+ carry = y >> 32;
+ *x++ = y & FFFFFFFF;
+#else
+#ifdef Pack_32
+ xi = *x;
+ y = (xi & 0xffff) * m + carry;
+ z = (xi >> 16) * m + (y >> 16);
+ carry = z >> 16;
+ *x++ = (z << 16) + (y & 0xffff);
+#else
+ y = *x * m + carry;
+ carry = y >> 16;
+ *x++ = y & 0xffff;
+#endif
+#endif
+ }
+ while(++i < wds);
+ if (carry) {
+ if (wds >= b->maxwds) {
+ b1 = Balloc(b->k+1);
+ Bcopy(b1, b);
+ Bfree(b);
+ b = b1;
+ }
+ b->x[wds++] = carry;
+ b->wds = wds;
+ }
+ return b;
+}
+
+static Bigint *
+s2b
+#ifdef KR_headers
+(s, nd0, nd, y9, dplen) CONST char *s; int nd0, nd, dplen; ULong y9;
+#else
+(const char *s, int nd0, int nd, ULong y9, int dplen)
+#endif
+{
+ Bigint *b;
+ int 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 += dplen;
+ }
+ else {
+ s += dplen + 9;
+ }
+ for(; i < nd; i++) {
+ b = multadd(b, 10, *s++ - '0');
+ }
+ return b;
+}
+
+static int
+hi0bits
+#ifdef KR_headers
+(x) ULong x;
+#else
+(ULong x)
+#endif
+{
+ int 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 int
+lo0bits
+#ifdef KR_headers
+(y) ULong *y;
+#else
+(ULong *y)
+#endif
+{
+ int k;
+ ULong 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) {
+ return 32;
+ }
+ }
+ *y = x;
+ return k;
+}
+
+static Bigint *
+i2b
+#ifdef KR_headers
+(i) int i;
+#else
+(int i)
+#endif
+{
+ Bigint *b;
+
+ b = Balloc(1);
+ b->x[0] = i;
+ b->wds = 1;
+ return b;
+}
+
+static Bigint *
+mult
+#ifdef KR_headers
+(a, b) Bigint *a, *b;
+#else
+(Bigint *a, Bigint *b)
+#endif
+{
+ Bigint *c;
+ int k, wa, wb, wc;
+ ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0;
+ ULong y;
+#ifdef ULLong
+ ULLong carry, z;
+#else
+ ULong carry, z;
+#ifdef Pack_32
+ ULong z2;
+#endif
+#endif
+
+ if (a->wds < b->wds) {
+ c = a;
+ a = b;
+ b = c;
+ }
+ k = a->k;
+ wa = a->wds;
+ wb = b->wds;
+ wc = wa + wb;
+ if (wc > a->maxwds) {
+ k++;
+ }
+ c = Balloc(k);
+ for(x = c->x, xa = x + wc; x < xa; x++) {
+ *x = 0;
+ }
+ xa = a->x;
+ xae = xa + wa;
+ xb = b->x;
+ xbe = xb + wb;
+ xc0 = c->x;
+#ifdef ULLong
+ for(; xb < xbe; xc0++) {
+ if ((y = *xb++)) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ do {
+ z = *x++ * (ULLong)y + *xc + carry;
+ carry = z >> 32;
+ *xc++ = z & FFFFFFFF;
+ }
+ while(x < xae);
+ *xc = carry;
+ }
+ }
+#else
+#ifdef Pack_32
+ for(; xb < xbe; xb++, xc0++) {
+ if (y = *xb & 0xffff) {
+ 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) {
+ 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
+#endif
+ for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ;
+ c->wds = wc;
+ return c;
+}
+
+static Bigint *p5s;
+
+static Bigint *
+pow5mult
+#ifdef KR_headers
+(b, k) Bigint *b; int k;
+#else
+(Bigint *b, int k)
+#endif
+{
+ Bigint *b1, *p5, *p51;
+ int i;
+ static int p05[3] = { 5, 25, 125 };
+
+ if ((i = k & 3)) {
+ b = multadd(b, p05[i-1], 0);
+ }
+
+ if (!(k >>= 2)) {
+ return b;
+ }
+ if (!(p5 = p5s)) {
+ /* first time */
+#ifdef MULTIPLE_THREADS
+ ACQUIRE_DTOA_LOCK(1);
+ if (!(p5 = p5s)) {
+ p5 = p5s = i2b(625);
+ p5->next = 0;
+ }
+ FREE_DTOA_LOCK(1);
+#else
+ p5 = p5s = i2b(625);
+ p5->next = 0;
+#endif
+ }
+ for(;;) {
+ if (k & 1) {
+ b1 = mult(b, p5);
+ Bfree(b);
+ b = b1;
+ }
+ if (!(k >>= 1)) {
+ break;
+ }
+ if (!(p51 = p5->next)) {
+#ifdef MULTIPLE_THREADS
+ ACQUIRE_DTOA_LOCK(1);
+ if (!(p51 = p5->next)) {
+ p51 = p5->next = mult(p5,p5);
+ p51->next = 0;
+ }
+ FREE_DTOA_LOCK(1);
+#else
+ p51 = p5->next = mult(p5,p5);
+ p51->next = 0;
+#endif
+ }
+ p5 = p51;
+ }
+ return b;
+}
+
+static Bigint *
+lshift
+#ifdef KR_headers
+(b, k) Bigint *b; int k;
+#else
+(Bigint *b, int k)
+#endif
+{
+ int i, k1, n, n1;
+ Bigint *b1;
+ ULong *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)) {
+ ++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) {
+ ++n1;
+ }
+ }
+#endif
+ else do {
+ *x1++ = *x++;
+ }
+ while(x < xe);
+ b1->wds = n1 - 1;
+ Bfree(b);
+ return b1;
+}
+
+static int
+cmp
+#ifdef KR_headers
+(a, b) Bigint *a, *b;
+#else
+(Bigint *a, Bigint *b)
+#endif
+{
+ ULong *xa, *xa0, *xb, *xb0;
+ int i, j;
+
+ i = a->wds;
+ j = b->wds;
+#ifdef DEBUG
+ 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
+#ifdef KR_headers
+(a, b) Bigint *a, *b;
+#else
+(Bigint *a, Bigint *b)
+#endif
+{
+ Bigint *c;
+ int i, wa, wb;
+ ULong *xa, *xae, *xb, *xbe, *xc;
+#ifdef ULLong
+ ULLong borrow, y;
+#else
+ ULong borrow, y;
+#ifdef Pack_32
+ ULong z;
+#endif
+#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 ULLong
+ do {
+ y = (ULLong)*xa++ - *xb++ - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *xc++ = y & FFFFFFFF;
+ }
+ while(xb < xbe);
+ while(xa < xae) {
+ y = *xa++ - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *xc++ = y & FFFFFFFF;
+ }
+#else
+#ifdef Pack_32
+ do {
+ y = (*xa & 0xffff) - (*xb & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*xa++ >> 16) - (*xb++ >> 16) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(xc, z, y);
+ }
+ while(xb < xbe);
+ while(xa < xae) {
+ y = (*xa & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*xa++ >> 16) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(xc, z, y);
+ }
+#else
+ do {
+ y = *xa++ - *xb++ - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *xc++ = y & 0xffff;
+ }
+ while(xb < xbe);
+ while(xa < xae) {
+ y = *xa++ - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *xc++ = y & 0xffff;
+ }
+#endif
+#endif
+ while(!*--xc) {
+ wa--;
+ }
+ c->wds = wa;
+ return c;
+}
+
+static double
+ulp
+#ifdef KR_headers
+(x) U *x;
+#else
+(U *x)
+#endif
+{
+ Long L;
+ U u;
+
+ L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1;
+#ifndef Avoid_Underflow
+#ifndef Sudden_Underflow
+ if (L > 0) {
+#endif
+#endif
+#ifdef IBM
+ L |= Exp_msk1 >> 4;
+#endif
+ word0(&u) = L;
+ word1(&u) = 0;
+#ifndef Avoid_Underflow
+#ifndef Sudden_Underflow
+ }
+ else {
+ L = -L >> Exp_shift;
+ if (L < Exp_shift) {
+ word0(&u) = 0x80000 >> L;
+ word1(&u) = 0;
+ }
+ else {
+ word0(&u) = 0;
+ L -= Exp_shift;
+ word1(&u) = L >= 31 ? 1 : 1 << 31 - L;
+ }
+ }
+#endif
+#endif
+ return dval(&u);
+}
+
+static double
+b2d
+#ifdef KR_headers
+(a, e) Bigint *a; int *e;
+#else
+(Bigint *a, int *e)
+#endif
+{
+ ULong *xa, *xa0, w, y, z;
+ int k;
+ U d;
+#ifdef VAX
+ ULong d0, d1;
+#else
+#define d0 word0(&d)
+#define d1 word1(&d)
+#endif
+
+ xa0 = a->x;
+ xa = xa0 + a->wds;
+ y = *--xa;
+#ifdef DEBUG
+ 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 dval(&d);
+}
+
+static Bigint *
+d2b
+#ifdef KR_headers
+(d, e, bits) U *d; int *e, *bits;
+#else
+(U *d, int *e, int *bits)
+#endif
+{
+ Bigint *b;
+ int de, k;
+ ULong *x, y, z;
+#ifndef Sudden_Underflow
+ int i;
+#endif
+#ifdef VAX
+ ULong 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 = (int)(d0 >> Exp_shift);
+#ifndef IBM
+ z |= Exp_msk11;
+#endif
+#else
+ if ((de = (int)(d0 >> Exp_shift))) {
+ z |= Exp_msk1;
+ }
+#endif
+#ifdef Pack_32
+ if ((y = d1)) {
+ if ((k = lo0bits(&y))) {
+ x[0] = y | z << (32 - k);
+ z >>= k;
+ }
+ else {
+ x[0] = y;
+ }
+#ifndef Sudden_Underflow
+ i =
+#endif
+ b->wds = (x[1] = z) ? 2 : 1;
+ }
+ else {
+ k = lo0bits(&z);
+ x[0] = z;
+#ifndef Sudden_Underflow
+ i =
+#endif
+ b->wds = 1;
+ k += 32;
+ }
+#else
+ if (y = d1) {
+ if (k = lo0bits(&y))
+ 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
+ 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
+{
+ U da, db;
+ int k, ka, kb;
+
+ dval(&da) = b2d(a, &ka);
+ dval(&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) {
+ dval(&da) *= 1 << k;
+ }
+ }
+ else {
+ k = -k;
+ word0(&db) += (k >> 2)*Exp_msk1;
+ if (k &= 3) {
+ dval(&db) *= 1 << k;
+ }
+ }
+#else
+ if (k > 0) {
+ word0(&da) += k*Exp_msk1;
+ }
+ else {
+ k = -k;
+ word0(&db) += k*Exp_msk1;
+ }
+#endif
+ return dval(&da) / dval(&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,
+#ifdef Avoid_Underflow
+ 9007199254740992.*9007199254740992.e-256
+ /* = 2^106 * 1e-256 */
+#else
+ 1e-256
+#endif
+ };
+/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */
+/* flag unnecessarily. It leads to a song and dance at the end of strtod. */
+#define Scale_Bit 0x10
+#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
+
+#undef Need_Hexdig
+#ifdef INFNAN_CHECK
+#ifndef No_Hex_NaN
+#define Need_Hexdig
+#endif
+#endif
+
+#ifndef Need_Hexdig
+#ifndef NO_HEX_FP
+#define Need_Hexdig
+#endif
+#endif
+
+#ifdef Need_Hexdig /*{*/
+static unsigned char hexdig[256];
+
+static void
+#ifdef KR_headers
+htinit(h, s, inc) unsigned char *h; unsigned char *s; int inc;
+#else
+htinit(unsigned char *h, unsigned char *s, int inc)
+#endif
+{
+ int i, j;
+ for(i = 0; (j = s[i]) !=0; i++) {
+ h[j] = i + inc;
+ }
+}
+
+static void
+#ifdef KR_headers
+hexdig_init()
+#else
+hexdig_init(void)
+#endif
+{
+#define USC (unsigned char *)
+ htinit(hexdig, USC "0123456789", 0x10);
+ htinit(hexdig, USC "abcdef", 0x10 + 10);
+ htinit(hexdig, USC "ABCDEF", 0x10 + 10);
+}
+#endif /* } Need_Hexdig */
+
+#ifdef INFNAN_CHECK
+
+#ifndef NAN_WORD0
+#define NAN_WORD0 0x7ff80000
+#endif
+
+#ifndef NAN_WORD1
+#define NAN_WORD1 0
+#endif
+
+static int
+match
+#ifdef KR_headers
+(sp, t) char **sp, *t;
+#else
+(const char **sp, const char *t)
+#endif
+{
+ int c, d;
+ CONST char *s = *sp;
+
+ while((d = *t++)) {
+ if ((c = *++s) >= 'A' && c <= 'Z') {
+ c += 'a' - 'A';
+ }
+ if (c != d) {
+ return 0;
+ }
+ }
+ *sp = s + 1;
+ return 1;
+}
+
+#ifndef No_Hex_NaN
+static void
+hexnan
+#ifdef KR_headers
+(rvp, sp) U *rvp; CONST char **sp;
+#else
+(U *rvp, const char **sp)
+#endif
+{
+ ULong c, x[2];
+ CONST char *s;
+ int c1, havedig, udx0, xshift;
+
+ if (!hexdig['0']) {
+ hexdig_init();
+ }
+ x[0] = x[1] = 0;
+ havedig = xshift = 0;
+ udx0 = 1;
+ s = *sp;
+ /* allow optional initial 0x or 0X */
+ while((c = *(CONST unsigned char*)(s+1)) && c <= ' ') {
+ ++s;
+ }
+ if (s[1] == '0' && (s[2] == 'x' || s[2] == 'X')) {
+ s += 2;
+ }
+ while((c = *(CONST unsigned char*)++s)) {
+ if ((c1 = hexdig[c])) {
+ c = c1 & 0xf;
+ }
+ else if (c <= ' ') {
+ if (udx0 && havedig) {
+ udx0 = 0;
+ xshift = 1;
+ }
+ continue;
+ }
+#ifdef GDTOA_NON_PEDANTIC_NANCHECK
+ else if (/*(*/ c == ')' && havedig) {
+ *sp = s + 1;
+ break;
+ }
+ else {
+ return; /* invalid form: don't change *sp */
+ }
+#else
+ else {
+ do {
+ if (/*(*/ c == ')') {
+ *sp = s + 1;
+ break;
+ }
+ } while((c = *++s));
+ break;
+ }
+#endif
+ havedig = 1;
+ if (xshift) {
+ xshift = 0;
+ x[0] = x[1];
+ x[1] = 0;
+ }
+ if (udx0) {
+ x[0] = (x[0] << 4) | (x[1] >> 28);
+ }
+ x[1] = (x[1] << 4) | c;
+ }
+ if ((x[0] &= 0xfffff) || x[1]) {
+ word0(rvp) = Exp_mask | x[0];
+ word1(rvp) = x[1];
+ }
+}
+#endif /*No_Hex_NaN*/
+#endif /* INFNAN_CHECK */
+
+#ifdef Pack_32
+#define ULbits 32
+#define kshift 5
+#define kmask 31
+#else
+#define ULbits 16
+#define kshift 4
+#define kmask 15
+#endif
+
+#if !defined(NO_HEX_FP) || defined(Honor_FLT_ROUNDS) /*{*/
+static Bigint *
+#ifdef KR_headers
+increment(b) Bigint *b;
+#else
+increment(Bigint *b)
+#endif
+{
+ ULong *x, *xe;
+ Bigint *b1;
+
+ x = b->x;
+ xe = x + b->wds;
+ do {
+ if (*x < (ULong)0xffffffffL) {
+ ++*x;
+ return b;
+ }
+ *x++ = 0;
+ } while(x < xe);
+ {
+ if (b->wds >= b->maxwds) {
+ b1 = Balloc(b->k+1);
+ Bcopy(b1,b);
+ Bfree(b);
+ b = b1;
+ }
+ b->x[b->wds++] = 1;
+ }
+ return b;
+}
+
+#endif /*}*/
+
+#ifndef NO_HEX_FP /*{*/
+
+static void
+#ifdef KR_headers
+rshift(b, k) Bigint *b; int k;
+#else
+rshift(Bigint *b, int k)
+#endif
+{
+ ULong *x, *x1, *xe, y;
+ int n;
+
+ x = x1 = b->x;
+ n = k >> kshift;
+ if (n < b->wds) {
+ xe = x + b->wds;
+ x += n;
+ if (k &= kmask) {
+ n = 32 - k;
+ y = *x++ >> k;
+ while(x < xe) {
+ *x1++ = (y | (*x << n)) & 0xffffffff;
+ y = *x++ >> k;
+ }
+ if ((*x1 = y) !=0) {
+ x1++;
+ }
+ }
+ else
+ while(x < xe) {
+ *x1++ = *x++;
+ }
+ }
+ if ((b->wds = x1 - b->x) == 0) {
+ b->x[0] = 0;
+ }
+}
+
+static ULong
+#ifdef KR_headers
+any_on(b, k) Bigint *b; int k;
+#else
+any_on(Bigint *b, int k)
+#endif
+{
+ int n, nwds;
+ ULong *x, *x0, x1, x2;
+
+ x = b->x;
+ nwds = b->wds;
+ n = k >> kshift;
+ if (n > nwds) {
+ n = nwds;
+ }
+ else if (n < nwds && (k &= kmask)) {
+ x1 = x2 = x[n];
+ x1 >>= k;
+ x1 <<= k;
+ if (x1 != x2) {
+ return 1;
+ }
+ }
+ x0 = x;
+ x += n;
+ while(x > x0)
+ if (*--x) {
+ return 1;
+ }
+ return 0;
+}
+
+enum { /* rounding values: same as FLT_ROUNDS */
+ Round_zero = 0,
+ Round_near = 1,
+ Round_up = 2,
+ Round_down = 3
+};
+
+void
+#ifdef KR_headers
+gethex(sp, rvp, rounding, sign)
+CONST char **sp; U *rvp; int rounding, sign;
+#else
+gethex( CONST char **sp, U *rvp, int rounding, int sign)
+#endif
+{
+ Bigint *b;
+ CONST unsigned char *decpt, *s0, *s, *s1;
+ Long e, e1;
+ ULong L, lostbits, *x;
+ int big, denorm, esign, havedig, k, n, nbits, up, zret;
+#ifdef IBM
+ int j;
+#endif
+ enum {
+#ifdef IEEE_Arith /*{{*/
+ emax = 0x7fe - Bias - P + 1,
+ emin = Emin - P + 1
+#else /*}{*/
+ emin = Emin - P,
+#ifdef VAX
+ emax = 0x7ff - Bias - P + 1
+#endif
+#ifdef IBM
+ emax = 0x7f - Bias - P
+#endif
+#endif /*}}*/
+ };
+#ifdef USE_LOCALE
+ int i;
+#ifdef NO_LOCALE_CACHE
+ const unsigned char *decimalpoint = (unsigned char*)
+ localeconv()->decimal_point;
+#else
+ const unsigned char *decimalpoint;
+ static unsigned char *decimalpoint_cache;
+ if (!(s0 = decimalpoint_cache)) {
+ s0 = (unsigned char*)localeconv()->decimal_point;
+ if ((decimalpoint_cache = (unsigned char*)
+ MALLOC(strlen((CONST char*)s0) + 1))) {
+ strcpy((char*)decimalpoint_cache, (CONST char*)s0);
+ s0 = decimalpoint_cache;
+ }
+ }
+ decimalpoint = s0;
+#endif
+#endif
+
+ if (!hexdig['0']) {
+ hexdig_init();
+ }
+ havedig = 0;
+ s0 = *(CONST unsigned char **)sp + 2;
+ while(s0[havedig] == '0') {
+ havedig++;
+ }
+ s0 += havedig;
+ s = s0;
+ decpt = 0;
+ zret = 0;
+ e = 0;
+ if (hexdig[*s]) {
+ havedig++;
+ }
+ else {
+ zret = 1;
+#ifdef USE_LOCALE
+ for(i = 0; decimalpoint[i]; ++i) {
+ if (s[i] != decimalpoint[i]) {
+ goto pcheck;
+ }
+ }
+ decpt = s += i;
+#else
+ if (*s != '.') {
+ goto pcheck;
+ }
+ decpt = ++s;
+#endif
+ if (!hexdig[*s]) {
+ goto pcheck;
+ }
+ while(*s == '0') {
+ s++;
+ }
+ if (hexdig[*s]) {
+ zret = 0;
+ }
+ havedig = 1;
+ s0 = s;
+ }
+ while(hexdig[*s]) {
+ s++;
+ }
+#ifdef USE_LOCALE
+ if (*s == *decimalpoint && !decpt) {
+ for(i = 1; decimalpoint[i]; ++i) {
+ if (s[i] != decimalpoint[i]) {
+ goto pcheck;
+ }
+ }
+ decpt = s += i;
+#else
+ if (*s == '.' && !decpt) {
+ decpt = ++s;
+#endif
+ while(hexdig[*s]) {
+ s++;
+ }
+ }/*}*/
+ if (decpt) {
+ e = -(((Long)(s-decpt)) << 2);
+ }
+pcheck:
+ s1 = s;
+ big = esign = 0;
+ switch(*s) {
+ case 'p':
+ case 'P':
+ switch(*++s) {
+ case '-':
+ esign = 1;
+ /* no break */
+ case '+':
+ s++;
+ }
+ if ((n = hexdig[*s]) == 0 || n > 0x19) {
+ s = s1;
+ break;
+ }
+ e1 = n - 0x10;
+ while((n = hexdig[*++s]) !=0 && n <= 0x19) {
+ if (e1 & 0xf8000000) {
+ big = 1;
+ }
+ e1 = 10*e1 + n - 0x10;
+ }
+ if (esign) {
+ e1 = -e1;
+ }
+ e += e1;
+ }
+ *sp = (char*)s;
+ if (!havedig) {
+ *sp = (char*)s0 - 1;
+ }
+ if (zret) {
+ goto retz1;
+ }
+ if (big) {
+ if (esign) {
+#ifdef IEEE_Arith
+ switch(rounding) {
+ case Round_up:
+ if (sign) {
+ break;
+ }
+ goto ret_tiny;
+ case Round_down:
+ if (!sign) {
+ break;
+ }
+ goto ret_tiny;
+ }
+#endif
+ goto retz;
+#ifdef IEEE_Arith
+ret_tiny:
+#ifndef NO_ERRNO
+ errno = ERANGE;
+#endif
+ word0(rvp) = 0;
+ word1(rvp) = 1;
+ return;
+#endif /* IEEE_Arith */
+ }
+ switch(rounding) {
+ case Round_near:
+ goto ovfl1;
+ case Round_up:
+ if (!sign) {
+ goto ovfl1;
+ }
+ goto ret_big;
+ case Round_down:
+ if (sign) {
+ goto ovfl1;
+ }
+ goto ret_big;
+ }
+ret_big:
+ word0(rvp) = Big0;
+ word1(rvp) = Big1;
+ return;
+ }
+ n = s1 - s0 - 1;
+ for(k = 0; n > (1 << (kshift-2)) - 1; n >>= 1) {
+ k++;
+ }
+ b = Balloc(k);
+ x = b->x;
+ n = 0;
+ L = 0;
+#ifdef USE_LOCALE
+ for(i = 0; decimalpoint[i+1]; ++i);
+#endif
+ while(s1 > s0) {
+#ifdef USE_LOCALE
+ if (*--s1 == decimalpoint[i]) {
+ s1 -= i;
+ continue;
+ }
+#else
+ if (*--s1 == '.') {
+ continue;
+ }
+#endif
+ if (n == ULbits) {
+ *x++ = L;
+ L = 0;
+ n = 0;
+ }
+ L |= (hexdig[*s1] & 0x0f) << n;
+ n += 4;
+ }
+ *x++ = L;
+ b->wds = n = x - b->x;
+ n = ULbits*n - hi0bits(L);
+ nbits = Nbits;
+ lostbits = 0;
+ x = b->x;
+ if (n > nbits) {
+ n -= nbits;
+ if (any_on(b,n)) {
+ lostbits = 1;
+ k = n - 1;
+ if (x[k>>kshift] & 1 << (k & kmask)) {
+ lostbits = 2;
+ if (k > 0 && any_on(b,k)) {
+ lostbits = 3;
+ }
+ }
+ }
+ rshift(b, n);
+ e += n;
+ }
+ else if (n < nbits) {
+ n = nbits - n;
+ b = lshift(b, n);
+ e -= n;
+ x = b->x;
+ }
+ if (e > Emax) {
+ovfl:
+ Bfree(b);
+ovfl1:
+#ifndef NO_ERRNO
+ errno = ERANGE;
+#endif
+ word0(rvp) = Exp_mask;
+ word1(rvp) = 0;
+ return;
+ }
+ denorm = 0;
+ if (e < emin) {
+ denorm = 1;
+ n = emin - e;
+ if (n >= nbits) {
+#ifdef IEEE_Arith /*{*/
+ switch (rounding) {
+ case Round_near:
+ if (n == nbits && (n < 2 || any_on(b,n-1))) {
+ goto ret_tiny;
+ }
+ break;
+ case Round_up:
+ if (!sign) {
+ goto ret_tiny;
+ }
+ break;
+ case Round_down:
+ if (sign) {
+ goto ret_tiny;
+ }
+ }
+#endif /* } IEEE_Arith */
+ Bfree(b);
+retz:
+#ifndef NO_ERRNO
+ errno = ERANGE;
+#endif
+retz1:
+ rvp->d = 0.;
+ return;
+ }
+ k = n - 1;
+ if (lostbits) {
+ lostbits = 1;
+ }
+ else if (k > 0) {
+ lostbits = any_on(b,k);
+ }
+ if (x[k>>kshift] & 1 << (k & kmask)) {
+ lostbits |= 2;
+ }
+ nbits -= n;
+ rshift(b,n);
+ e = emin;
+ }
+ if (lostbits) {
+ up = 0;
+ switch(rounding) {
+ case Round_zero:
+ break;
+ case Round_near:
+ if (lostbits & 2
+ && (lostbits & 1) | (x[0] & 1)) {
+ up = 1;
+ }
+ break;
+ case Round_up:
+ up = 1 - sign;
+ break;
+ case Round_down:
+ up = sign;
+ }
+ if (up) {
+ k = b->wds;
+ b = increment(b);
+ x = b->x;
+ if (denorm) {
+#if 0
+ if (nbits == Nbits - 1
+ && x[nbits >> kshift] & 1 << (nbits & kmask)) {
+ denorm = 0; /* not currently used */
+ }
+#endif
+ }
+ else if (b->wds > k
+ || ((n = nbits & kmask) !=0
+ && hi0bits(x[k-1]) < 32-n)) {
+ rshift(b,1);
+ if (++e > Emax) {
+ goto ovfl;
+ }
+ }
+ }
+ }
+#ifdef IEEE_Arith
+ if (denorm) {
+ word0(rvp) = b->wds > 1 ? b->x[1] & ~0x100000 : 0;
+ }
+ else {
+ word0(rvp) = (b->x[1] & ~0x100000) | ((e + 0x3ff + 52) << 20);
+ }
+ word1(rvp) = b->x[0];
+#endif
+#ifdef IBM
+ if ((j = e & 3)) {
+ k = b->x[0] & ((1 << j) - 1);
+ rshift(b,j);
+ if (k) {
+ switch(rounding) {
+ case Round_up:
+ if (!sign) {
+ increment(b);
+ }
+ break;
+ case Round_down:
+ if (sign) {
+ increment(b);
+ }
+ break;
+ case Round_near:
+ j = 1 << (j-1);
+ if (k & j && ((k & (j-1)) | lostbits)) {
+ increment(b);
+ }
+ }
+ }
+ }
+ e >>= 2;
+ word0(rvp) = b->x[1] | ((e + 65 + 13) << 24);
+ word1(rvp) = b->x[0];
+#endif
+#ifdef VAX
+ /* The next two lines ignore swap of low- and high-order 2 bytes. */
+ /* word0(rvp) = (b->x[1] & ~0x800000) | ((e + 129 + 55) << 23); */
+ /* word1(rvp) = b->x[0]; */
+ word0(rvp) = ((b->x[1] & ~0x800000) >> 16) | ((e + 129 + 55) << 7) | (b->x[1] << 16);
+ word1(rvp) = (b->x[0] >> 16) | (b->x[0] << 16);
+#endif
+ Bfree(b);
+}
+#endif /*!NO_HEX_FP}*/
+
+static int
+#ifdef KR_headers
+dshift(b, p2) Bigint *b; int p2;
+#else
+dshift(Bigint *b, int p2)
+#endif
+{
+ int rv = hi0bits(b->x[b->wds-1]) - 4;
+ if (p2 > 0) {
+ rv -= p2;
+ }
+ return rv & kmask;
+}
+
+static int
+quorem
+#ifdef KR_headers
+(b, S) Bigint *b, *S;
+#else
+(Bigint *b, Bigint *S)
+#endif
+{
+ int n;
+ ULong *bx, *bxe, q, *sx, *sxe;
+#ifdef ULLong
+ ULLong borrow, carry, y, ys;
+#else
+ ULong borrow, carry, y, ys;
+#ifdef Pack_32
+ ULong si, z, zs;
+#endif
+#endif
+
+ n = S->wds;
+#ifdef DEBUG
+ /*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
+#ifdef NO_STRTOD_BIGCOMP
+ /*debug*/ if (q > 9)
+#else
+ /* An oversized q is possible when quorem is called from bigcomp and */
+ /* the input is near, e.g., twice the smallest denormalized number. */
+ /*debug*/ if (q > 15)
+#endif
+ /*debug*/ Bug("oversized quotient in quorem");
+#endif
+ if (q) {
+ borrow = 0;
+ carry = 0;
+ do {
+#ifdef ULLong
+ ys = *sx++ * (ULLong)q + carry;
+ carry = ys >> 32;
+ y = *bx - (ys & FFFFFFFF) - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *bx++ = y & FFFFFFFF;
+#else
+#ifdef Pack_32
+ si = *sx++;
+ ys = (si & 0xffff) * q + carry;
+ zs = (si >> 16) * q + (ys >> 16);
+ carry = zs >> 16;
+ y = (*bx & 0xffff) - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*bx >> 16) - (zs & 0xffff) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(bx, z, y);
+#else
+ ys = *sx++ * q + carry;
+ carry = ys >> 16;
+ y = *bx - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *bx++ = y & 0xffff;
+#endif
+#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 ULLong
+ ys = *sx++ + carry;
+ carry = ys >> 32;
+ y = *bx - (ys & FFFFFFFF) - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *bx++ = y & FFFFFFFF;
+#else
+#ifdef Pack_32
+ si = *sx++;
+ ys = (si & 0xffff) + carry;
+ zs = (si >> 16) + (ys >> 16);
+ carry = zs >> 16;
+ y = (*bx & 0xffff) - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*bx >> 16) - (zs & 0xffff) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(bx, z, y);
+#else
+ ys = *sx++ + carry;
+ carry = ys >> 16;
+ y = *bx - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *bx++ = y & 0xffff;
+#endif
+#endif
+ }
+ while(sx <= sxe);
+ bx = b->x;
+ bxe = bx + n;
+ if (!*bxe) {
+ while(--bxe > bx && !*bxe) {
+ --n;
+ }
+ b->wds = n;
+ }
+ }
+ return q;
+}
+
+#if defined(Avoid_Underflow) || !defined(NO_STRTOD_BIGCOMP) /*{*/
+static double
+sulp
+#ifdef KR_headers
+(x, bc) U *x; BCinfo *bc;
+#else
+(U *x, BCinfo *bc)
+#endif
+{
+ U u;
+ double rv;
+ int i;
+
+ rv = ulp(x);
+ if (!bc->scale || (i = 2*P + 1 - ((word0(x) & Exp_mask) >> Exp_shift)) <= 0) {
+ return rv; /* Is there an example where i <= 0 ? */
+ }
+ word0(&u) = Exp_1 + (i << Exp_shift);
+ word1(&u) = 0;
+ return rv * u.d;
+}
+#endif /*}*/
+
+#ifndef NO_STRTOD_BIGCOMP
+static void
+bigcomp
+#ifdef KR_headers
+(rv, s0, bc)
+U *rv; CONST char *s0; BCinfo *bc;
+#else
+(U *rv, const char *s0, BCinfo *bc)
+#endif
+{
+ Bigint *b, *d;
+ int b2, bbits, d2, dd, dig, dsign, i, j, nd, nd0, p2, p5, speccase;
+
+ dsign = bc->dsign;
+ nd = bc->nd;
+ nd0 = bc->nd0;
+ p5 = nd + bc->e0 - 1;
+ speccase = 0;
+#ifndef Sudden_Underflow
+ if (rv->d == 0.) { /* special case: value near underflow-to-zero */
+ /* threshold was rounded to zero */
+ b = i2b(1);
+ p2 = Emin - P + 1;
+ bbits = 1;
+#ifdef Avoid_Underflow
+ word0(rv) = (P+2) << Exp_shift;
+#else
+ word1(rv) = 1;
+#endif
+ i = 0;
+#ifdef Honor_FLT_ROUNDS
+ if (bc->rounding == 1)
+#endif
+ {
+ speccase = 1;
+ --p2;
+ dsign = 0;
+ goto have_i;
+ }
+ }
+ else
+#endif
+ b = d2b(rv, &p2, &bbits);
+#ifdef Avoid_Underflow
+ p2 -= bc->scale;
+#endif
+ /* floor(log2(rv)) == bbits - 1 + p2 */
+ /* Check for denormal case. */
+ i = P - bbits;
+ if (i > (j = P - Emin - 1 + p2)) {
+#ifdef Sudden_Underflow
+ Bfree(b);
+ b = i2b(1);
+ p2 = Emin;
+ i = P - 1;
+#ifdef Avoid_Underflow
+ word0(rv) = (1 + bc->scale) << Exp_shift;
+#else
+ word0(rv) = Exp_msk1;
+#endif
+ word1(rv) = 0;
+#else
+ i = j;
+#endif
+ }
+#ifdef Honor_FLT_ROUNDS
+ if (bc->rounding != 1) {
+ if (i > 0) {
+ b = lshift(b, i);
+ }
+ if (dsign) {
+ b = increment(b);
+ }
+ }
+ else
+#endif
+ {
+ b = lshift(b, ++i);
+ b->x[0] |= 1;
+ }
+#ifndef Sudden_Underflow
+have_i:
+#endif
+ p2 -= p5 + i;
+ d = i2b(1);
+ /* Arrange for convenient computation of quotients:
+ * shift left if necessary so divisor has 4 leading 0 bits.
+ */
+ if (p5 > 0) {
+ d = pow5mult(d, p5);
+ }
+ else if (p5 < 0) {
+ b = pow5mult(b, -p5);
+ }
+ if (p2 > 0) {
+ b2 = p2;
+ d2 = 0;
+ }
+ else {
+ b2 = 0;
+ d2 = -p2;
+ }
+ i = dshift(d, d2);
+ if ((b2 += i) > 0) {
+ b = lshift(b, b2);
+ }
+ if ((d2 += i) > 0) {
+ d = lshift(d, d2);
+ }
+
+ /* Now b/d = exactly half-way between the two floating-point values */
+ /* on either side of the input string. Compute first digit of b/d. */
+
+ if (!(dig = quorem(b,d))) {
+ b = multadd(b, 10, 0); /* very unlikely */
+ dig = quorem(b,d);
+ }
+
+ /* Compare b/d with s0 */
+
+ for(i = 0; i < nd0; ) {
+ if ((dd = s0[i++] - '0' - dig)) {
+ goto ret;
+ }
+ if (!b->x[0] && b->wds == 1) {
+ if (i < nd) {
+ dd = 1;
+ }
+ goto ret;
+ }
+ b = multadd(b, 10, 0);
+ dig = quorem(b,d);
+ }
+ for(j = bc->dp1; i++ < nd;) {
+ if ((dd = s0[j++] - '0' - dig)) {
+ goto ret;
+ }
+ if (!b->x[0] && b->wds == 1) {
+ if (i < nd) {
+ dd = 1;
+ }
+ goto ret;
+ }
+ b = multadd(b, 10, 0);
+ dig = quorem(b,d);
+ }
+ if (b->x[0] || b->wds > 1 || dig > 0) {
+ dd = -1;
+ }
+ret:
+ Bfree(b);
+ Bfree(d);
+#ifdef Honor_FLT_ROUNDS
+ if (bc->rounding != 1) {
+ if (dd < 0) {
+ if (bc->rounding == 0) {
+ if (!dsign) {
+ goto retlow1;
+ }
+ }
+ else if (dsign) {
+ goto rethi1;
+ }
+ }
+ else if (dd > 0) {
+ if (bc->rounding == 0) {
+ if (dsign) {
+ goto rethi1;
+ }
+ goto ret1;
+ }
+ if (!dsign) {
+ goto rethi1;
+ }
+ dval(rv) += 2.*sulp(rv,bc);
+ }
+ else {
+ bc->inexact = 0;
+ if (dsign) {
+ goto rethi1;
+ }
+ }
+ }
+ else
+#endif
+ if (speccase) {
+ if (dd <= 0) {
+ rv->d = 0.;
+ }
+ }
+ else if (dd < 0) {
+ if (!dsign) /* does not happen for round-near */
+retlow1:
+ dval(rv) -= sulp(rv,bc);
+ }
+ else if (dd > 0) {
+ if (dsign) {
+rethi1:
+ dval(rv) += sulp(rv,bc);
+ }
+ }
+ else {
+ /* Exact half-way case: apply round-even rule. */
+ if ((j = ((word0(rv) & Exp_mask) >> Exp_shift) - bc->scale) <= 0) {
+ i = 1 - j;
+ if (i <= 31) {
+ if (word1(rv) & (0x1 << i)) {
+ goto odd;
+ }
+ }
+ else if (word0(rv) & (0x1 << (i-32))) {
+ goto odd;
+ }
+ }
+ else if (word1(rv) & 1) {
+odd:
+ if (dsign) {
+ goto rethi1;
+ }
+ goto retlow1;
+ }
+ }
+
+#ifdef Honor_FLT_ROUNDS
+ret1:
+#endif
+ return;
+}
+#endif /* NO_STRTOD_BIGCOMP */
+
+double
+strtod
+#ifdef KR_headers
+(s00, se) CONST char *s00; char **se;
+#else
+(const char *s00, char **se)
+#endif
+{
+ int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, e, e1;
+ int esign, i, j, k, nd, nd0, nf, nz, nz0, nz1, sign;
+ CONST char *s, *s0, *s1;
+ double aadj, aadj1;
+ Long L;
+ U aadj2, adj, rv, rv0;
+ ULong y, z;
+ BCinfo bc;
+ Bigint *bb, *bb1, *bd, *bd0, *bs, *delta;
+#ifdef Avoid_Underflow
+ ULong Lsb, Lsb1;
+#endif
+#ifdef SET_INEXACT
+ int oldinexact;
+#endif
+#ifndef NO_STRTOD_BIGCOMP
+ int req_bigcomp = 0;
+#endif
+#ifdef Honor_FLT_ROUNDS /*{*/
+#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */
+ bc.rounding = Flt_Rounds;
+#else /*}{*/
+ bc.rounding = 1;
+ switch(fegetround()) {
+ case FE_TOWARDZERO: bc.rounding = 0; break;
+ case FE_UPWARD: bc.rounding = 2; break;
+ case FE_DOWNWARD: bc.rounding = 3;
+ }
+#endif /*}}*/
+#endif /*}*/
+#ifdef USE_LOCALE
+ CONST char *s2;
+#endif
+
+ sign = nz0 = nz1 = nz = bc.dplen = bc.uflchk = 0;
+ dval(&rv) = 0.;
+ for(s = s00;; s++) switch(*s) {
+ case '-':
+ sign = 1;
+ /* no break */
+ case '+':
+ if (*++s) {
+ goto break2;
+ }
+ /* no break */
+ case 0:
+ goto ret0;
+ case '\t':
+ case '\n':
+ case '\v':
+ case '\f':
+ case '\r':
+ case ' ':
+ continue;
+ default:
+ goto break2;
+ }
+break2:
+ if (*s == '0') {
+#ifndef NO_HEX_FP /*{*/
+ switch(s[1]) {
+ case 'x':
+ case 'X':
+#ifdef Honor_FLT_ROUNDS
+ gethex(&s, &rv, bc.rounding, sign);
+#else
+ gethex(&s, &rv, 1, sign);
+#endif
+ goto ret;
+ }
+#endif /*}*/
+ 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;
+ bc.dp0 = bc.dp1 = s - s0;
+ for(s1 = s; s1 > s0 && *--s1 == '0'; ) {
+ ++nz1;
+ }
+#ifdef USE_LOCALE
+ s1 = localeconv()->decimal_point;
+ if (c == *s1) {
+ c = '.';
+ if (*++s1) {
+ s2 = s;
+ for(;;) {
+ if (*++s2 != *s1) {
+ c = 0;
+ break;
+ }
+ if (!*++s1) {
+ s = s2;
+ break;
+ }
+ }
+ }
+ }
+#endif
+ if (c == '.') {
+ c = *++s;
+ bc.dp1 = s - s0;
+ bc.dplen = bc.dp1 - bc.dp0;
+ if (!nd) {
+ for(; c == '0'; c = *++s) {
+ nz++;
+ }
+ if (c > '0' && c <= '9') {
+ bc.dp0 = s0 - s;
+ bc.dp1 = bc.dp0 + bc.dplen;
+ 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 = nz1 = 0;
+ }
+ }
+ }
+dig_done:
+ e = 0;
+ if (c == 'e' || c == 'E') {
+ if (!nd && !nz && !nz0) {
+ goto ret0;
+ }
+ 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 = (int)L;
+ }
+ if (esign) {
+ e = -e;
+ }
+ }
+ else {
+ e = 0;
+ }
+ }
+ else {
+ s = s00;
+ }
+ }
+ if (!nd) {
+ if (!nz && !nz0) {
+#ifdef INFNAN_CHECK
+ /* Check for Nan and Infinity */
+ if (!bc.dplen)
+ switch(c) {
+ case 'i':
+ case 'I':
+ if (match(&s,"nf")) {
+ --s;
+ if (!match(&s,"inity")) {
+ ++s;
+ }
+ word0(&rv) = 0x7ff00000;
+ word1(&rv) = 0;
+ goto ret;
+ }
+ break;
+ case 'n':
+ case 'N':
+ if (match(&s, "an")) {
+ word0(&rv) = NAN_WORD0;
+ word1(&rv) = NAN_WORD1;
+#ifndef No_Hex_NaN
+ if (*s == '(') { /*)*/
+ hexnan(&rv, &s);
+ }
+#endif
+ goto ret;
+ }
+ }
+#endif /* INFNAN_CHECK */
+ret0:
+ s = s00;
+ sign = 0;
+ }
+ goto ret;
+ }
+ bc.e0 = 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;
+ dval(&rv) = y;
+ if (k > 9) {
+#ifdef SET_INEXACT
+ if (k > DBL_DIG) {
+ oldinexact = get_inexact();
+ }
+#endif
+ dval(&rv) = tens[k - 9] * dval(&rv) + z;
+ }
+ bd0 = 0;
+ if (nd <= DBL_DIG
+#ifndef RND_PRODQUOT
+#ifndef Honor_FLT_ROUNDS
+ && Flt_Rounds == 1
+#endif
+#endif
+ ) {
+ if (!e) {
+ goto ret;
+ }
+#ifndef ROUND_BIASED_without_Round_Up
+ if (e > 0) {
+ if (e <= Ten_pmax) {
+#ifdef VAX
+ goto vax_ovfl_check;
+#else
+#ifdef Honor_FLT_ROUNDS
+ /* round correctly FLT_ROUNDS = 2 or 3 */
+ if (sign) {
+ rv.d = -rv.d;
+ sign = 0;
+ }
+#endif
+ /* rv = */ rounded_product(dval(&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.
+ */
+#ifdef Honor_FLT_ROUNDS
+ /* round correctly FLT_ROUNDS = 2 or 3 */
+ if (sign) {
+ rv.d = -rv.d;
+ sign = 0;
+ }
+#endif
+ e -= i;
+ dval(&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(dval(&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(dval(&rv), tens[e]);
+#endif
+ goto ret;
+ }
+ }
+#ifndef Inaccurate_Divide
+ else if (e >= -Ten_pmax) {
+#ifdef Honor_FLT_ROUNDS
+ /* round correctly FLT_ROUNDS = 2 or 3 */
+ if (sign) {
+ rv.d = -rv.d;
+ sign = 0;
+ }
+#endif
+ /* rv = */ rounded_quotient(dval(&rv), tens[-e]);
+ goto ret;
+ }
+#endif
+#endif /* ROUND_BIASED_without_Round_Up */
+ }
+ e1 += nd - k;
+
+#ifdef IEEE_Arith
+#ifdef SET_INEXACT
+ bc.inexact = 1;
+ if (k <= DBL_DIG) {
+ oldinexact = get_inexact();
+ }
+#endif
+#ifdef Avoid_Underflow
+ bc.scale = 0;
+#endif
+#ifdef Honor_FLT_ROUNDS
+ if (bc.rounding >= 2) {
+ if (sign) {
+ bc.rounding = bc.rounding == 2 ? 0 : 2;
+ }
+ else if (bc.rounding != 2) {
+ bc.rounding = 0;
+ }
+ }
+#endif
+#endif /*IEEE_Arith*/
+
+ /* Get starting approximation = rv * 10**e1 */
+
+ if (e1 > 0) {
+ if ((i = e1 & 15)) {
+ dval(&rv) *= tens[i];
+ }
+ if (e1 &= ~15) {
+ if (e1 > DBL_MAX_10_EXP) {
+ovfl:
+ /* Can't trust HUGE_VAL */
+#ifdef IEEE_Arith
+#ifdef Honor_FLT_ROUNDS
+ switch(bc.rounding) {
+ case 0: /* toward 0 */
+ case 3: /* toward -infinity */
+ word0(&rv) = Big0;
+ word1(&rv) = Big1;
+ break;
+ default:
+ word0(&rv) = Exp_mask;
+ word1(&rv) = 0;
+ }
+#else /*Honor_FLT_ROUNDS*/
+ word0(&rv) = Exp_mask;
+ word1(&rv) = 0;
+#endif /*Honor_FLT_ROUNDS*/
+#ifdef SET_INEXACT
+ /* set overflow bit */
+ dval(&rv0) = 1e300;
+ dval(&rv0) *= dval(&rv0);
+#endif
+#else /*IEEE_Arith*/
+ word0(&rv) = Big0;
+ word1(&rv) = Big1;
+#endif /*IEEE_Arith*/
+range_err:
+ if (bd0) {
+ Bfree(bb);
+ Bfree(bd);
+ Bfree(bs);
+ Bfree(bd0);
+ Bfree(delta);
+ }
+#ifndef NO_ERRNO
+ errno = ERANGE;
+#endif
+ goto ret;
+ }
+ e1 >>= 4;
+ for(j = 0; e1 > 1; j++, e1 >>= 1)
+ if (e1 & 1) {
+ dval(&rv) *= bigtens[j];
+ }
+ /* The last multiplication could overflow. */
+ word0(&rv) -= P*Exp_msk1;
+ dval(&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)) {
+ dval(&rv) /= tens[i];
+ }
+ if (e1 >>= 4) {
+ if (e1 >= 1 << n_bigtens) {
+ goto undfl;
+ }
+#ifdef Avoid_Underflow
+ if (e1 & Scale_Bit) {
+ bc.scale = 2*P;
+ }
+ for(j = 0; e1 > 0; j++, e1 >>= 1)
+ if (e1 & 1) {
+ dval(&rv) *= tinytens[j];
+ }
+ if (bc.scale && (j = 2*P + 1 - ((word0(&rv) & Exp_mask)
+ >> Exp_shift)) > 0) {
+ /* scaled rv is denormal; clear j low bits */
+ if (j >= 32) {
+ if (j > 54) {
+ goto undfl;
+ }
+ word1(&rv) = 0;
+ if (j >= 53) {
+ word0(&rv) = (P+2)*Exp_msk1;
+ }
+ else {
+ word0(&rv) &= 0xffffffff << (j-32);
+ }
+ }
+ else {
+ word1(&rv) &= 0xffffffff << j;
+ }
+ }
+#else
+ for(j = 0; e1 > 1; j++, e1 >>= 1)
+ if (e1 & 1) {
+ dval(&rv) *= tinytens[j];
+ }
+ /* The last multiplication could underflow. */
+ dval(&rv0) = dval(&rv);
+ dval(&rv) *= tinytens[j];
+ if (!dval(&rv)) {
+ dval(&rv) = 2.*dval(&rv0);
+ dval(&rv) *= tinytens[j];
+#endif
+ if (!dval(&rv)) {
+undfl:
+ dval(&rv) = 0.;
+ goto range_err;
+ }
+#ifndef Avoid_Underflow
+ word0(&rv) = Tiny0;
+ word1(&rv) = Tiny1;
+ /* The refinement below will clean
+ * this approximation up.
+ */
+ }
+#endif
+ }
+}
+
+/* Now the hard part -- adjusting rv to the correct value.*/
+
+/* Put digits into bd: true value = bd * 10^e */
+
+bc.nd = nd - nz1;
+#ifndef NO_STRTOD_BIGCOMP
+bc.nd0 = nd0; /* Only needed if nd > strtod_diglim, but done here */
+/* to silence an erroneous warning about bc.nd0 */
+/* possibly not being initialized. */
+if (nd > strtod_diglim) {
+ /* ASSERT(strtod_diglim >= 18); 18 == one more than the */
+ /* minimum number of decimal digits to distinguish double values */
+ /* in IEEE arithmetic. */
+ i = j = 18;
+ if (i > nd0) {
+ j += bc.dplen;
+ }
+ for(;;) {
+ if (--j < bc.dp1 && j >= bc.dp0) {
+ j = bc.dp0 - 1;
+ }
+ if (s0[j] != '0') {
+ break;
+ }
+ --i;
+ }
+ e += nd - i;
+ nd = i;
+ if (nd0 > nd) {
+ nd0 = nd;
+ }
+ if (nd < 9) { /* must recompute y */
+ y = 0;
+ for(i = 0; i < nd0; ++i) {
+ y = 10*y + s0[i] - '0';
+ }
+ for(j = bc.dp1; i < nd; ++i) {
+ y = 10*y + s0[j++] - '0';
+ }
+ }
+}
+#endif
+bd0 = s2b(s0, nd0, nd, y, bc.dplen);
+
+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 Honor_FLT_ROUNDS
+ if (bc.rounding != 1) {
+ bs2++;
+ }
+#endif
+#ifdef Avoid_Underflow
+ Lsb = LSB;
+ Lsb1 = 0;
+ j = bbe - bc.scale;
+ i = j + bbbits - 1; /* logb(rv) */
+ j = P + 1 - bbbits;
+ if (i < Emin) { /* denormal */
+ i = Emin - i;
+ j -= i;
+ if (i < 32) {
+ Lsb <<= i;
+ }
+ else if (i < 52) {
+ Lsb1 = Lsb << (i-32);
+ }
+ else {
+ Lsb1 = Exp_mask;
+ }
+ }
+#else /*Avoid_Underflow*/
+#ifdef Sudden_Underflow
+#ifdef IBM
+ j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3);
+#else
+ j = P + 1 - bbbits;
+#endif
+#else /*Sudden_Underflow*/
+ j = bbe;
+ i = j + bbbits - 1; /* logb(rv) */
+ if (i < Emin) { /* denormal */
+ j += P - Emin;
+ }
+ else {
+ j = P + 1 - bbbits;
+ }
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow*/
+ bb2 += j;
+ bd2 += j;
+#ifdef Avoid_Underflow
+ bd2 += bc.scale;
+#endif
+ 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);
+ bc.dsign = delta->sign;
+ delta->sign = 0;
+ i = cmp(delta, bs);
+#ifndef NO_STRTOD_BIGCOMP /*{*/
+ if (bc.nd > nd && i <= 0) {
+ if (bc.dsign) {
+ /* Must use bigcomp(). */
+ req_bigcomp = 1;
+ break;
+ }
+#ifdef Honor_FLT_ROUNDS
+ if (bc.rounding != 1) {
+ if (i < 0) {
+ req_bigcomp = 1;
+ break;
+ }
+ }
+ else
+#endif
+ i = -1; /* Discarded digits make delta smaller. */
+ }
+#endif /*}*/
+#ifdef Honor_FLT_ROUNDS /*{*/
+ if (bc.rounding != 1) {
+ if (i < 0) {
+ /* Error is less than an ulp */
+ if (!delta->x[0] && delta->wds <= 1) {
+ /* exact */
+#ifdef SET_INEXACT
+ bc.inexact = 0;
+#endif
+ break;
+ }
+ if (bc.rounding) {
+ if (bc.dsign) {
+ adj.d = 1.;
+ goto apply_adj;
+ }
+ }
+ else if (!bc.dsign) {
+ adj.d = -1.;
+ if (!word1(&rv)
+ && !(word0(&rv) & Frac_mask)) {
+ y = word0(&rv) & Exp_mask;
+#ifdef Avoid_Underflow
+ if (!bc.scale || y > 2*P*Exp_msk1)
+#else
+ if (y)
+#endif
+ {
+ delta = lshift(delta,Log2P);
+ if (cmp(delta, bs) <= 0) {
+ adj.d = -0.5;
+ }
+ }
+ }
+apply_adj:
+#ifdef Avoid_Underflow /*{*/
+ if (bc.scale && (y = word0(&rv) & Exp_mask)
+ <= 2*P*Exp_msk1) {
+ word0(&adj) += (2*P+1)*Exp_msk1 - y;
+ }
+#else
+#ifdef Sudden_Underflow
+ if ((word0(&rv) & Exp_mask) <=
+ P*Exp_msk1) {
+ word0(&rv) += P*Exp_msk1;
+ dval(&rv) += adj.d*ulp(dval(&rv));
+ word0(&rv) -= P*Exp_msk1;
+ }
+ else
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow}*/
+ dval(&rv) += adj.d*ulp(&rv);
+ }
+ break;
+ }
+ adj.d = ratio(delta, bs);
+ if (adj.d < 1.) {
+ adj.d = 1.;
+ }
+ if (adj.d <= 0x7ffffffe) {
+ /* adj = rounding ? ceil(adj) : floor(adj); */
+ y = adj.d;
+ if (y != adj.d) {
+ if (!((bc.rounding>>1) ^ bc.dsign)) {
+ y++;
+ }
+ adj.d = y;
+ }
+ }
+#ifdef Avoid_Underflow /*{*/
+ if (bc.scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1) {
+ word0(&adj) += (2*P+1)*Exp_msk1 - y;
+ }
+#else
+#ifdef Sudden_Underflow
+ if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) {
+ word0(&rv) += P*Exp_msk1;
+ adj.d *= ulp(dval(&rv));
+ if (bc.dsign) {
+ dval(&rv) += adj.d;
+ }
+ else {
+ dval(&rv) -= adj.d;
+ }
+ word0(&rv) -= P*Exp_msk1;
+ goto cont;
+ }
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow}*/
+ adj.d *= ulp(&rv);
+ if (bc.dsign) {
+ if (word0(&rv) == Big0 && word1(&rv) == Big1) {
+ goto ovfl;
+ }
+ dval(&rv) += adj.d;
+ }
+ else {
+ dval(&rv) -= adj.d;
+ }
+ goto cont;
+ }
+#endif /*}Honor_FLT_ROUNDS*/
+
+ if (i < 0) {
+ /* Error is less than half an ulp -- check for
+ * special case of mantissa a power of two.
+ */
+ if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask
+#ifdef IEEE_Arith /*{*/
+#ifdef Avoid_Underflow
+ || (word0(&rv) & Exp_mask) <= (2*P+1)*Exp_msk1
+#else
+ || (word0(&rv) & Exp_mask) <= Exp_msk1
+#endif
+#endif /*}*/
+ ) {
+#ifdef SET_INEXACT
+ if (!delta->x[0] && delta->wds <= 1) {
+ bc.inexact = 0;
+ }
+#endif
+ break;
+ }
+ if (!delta->x[0] && delta->wds <= 1) {
+ /* exact result */
+#ifdef SET_INEXACT
+ bc.inexact = 0;
+#endif
+ break;
+ }
+ delta = lshift(delta,Log2P);
+ if (cmp(delta, bs) > 0) {
+ goto drop_down;
+ }
+ break;
+ }
+ if (i == 0) {
+ /* exactly half-way between */
+ if (bc.dsign) {
+ if ((word0(&rv) & Bndry_mask1) == Bndry_mask1
+ && word1(&rv) == (
+#ifdef Avoid_Underflow
+ (bc.scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1)
+ ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) :
+#endif
+ 0xffffffff)) {
+ /*boundary case -- increment exponent*/
+ if (word0(&rv) == Big0 && word1(&rv) == Big1) {
+ goto ovfl;
+ }
+ word0(&rv) = (word0(&rv) & Exp_mask)
+ + Exp_msk1
+#ifdef IBM
+ | Exp_msk1 >> 4
+#endif
+ ;
+ word1(&rv) = 0;
+#ifdef Avoid_Underflow
+ bc.dsign = 0;
+#endif
+ 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
+#ifdef Avoid_Underflow
+ if (L <= (bc.scale ? (2*P+1)*Exp_msk1 : Exp_msk1))
+#else
+ if (L <= Exp_msk1)
+#endif /*Avoid_Underflow*/
+#endif /*IBM*/
+ {
+ if (bc.nd >nd) {
+ bc.uflchk = 1;
+ break;
+ }
+ goto undfl;
+ }
+ L -= Exp_msk1;
+#else /*Sudden_Underflow}{*/
+#ifdef Avoid_Underflow
+ if (bc.scale) {
+ L = word0(&rv) & Exp_mask;
+ if (L <= (2*P+1)*Exp_msk1) {
+ if (L > (P+2)*Exp_msk1)
+ /* round even ==> */
+ /* accept rv */
+ {
+ break;
+ }
+ /* rv = smallest denormal */
+ if (bc.nd >nd) {
+ bc.uflchk = 1;
+ break;
+ }
+ goto undfl;
+ }
+ }
+#endif /*Avoid_Underflow*/
+ L = (word0(&rv) & Exp_mask) - Exp_msk1;
+#endif /*Sudden_Underflow}}*/
+ word0(&rv) = L | Bndry_mask1;
+ word1(&rv) = 0xffffffff;
+#ifdef IBM
+ goto cont;
+#else
+#ifndef NO_STRTOD_BIGCOMP
+ if (bc.nd > nd) {
+ goto cont;
+ }
+#endif
+ break;
+#endif
+ }
+#ifndef ROUND_BIASED
+#ifdef Avoid_Underflow
+ if (Lsb1) {
+ if (!(word0(&rv) & Lsb1)) {
+ break;
+ }
+ }
+ else if (!(word1(&rv) & Lsb)) {
+ break;
+ }
+#else
+ if (!(word1(&rv) & LSB)) {
+ break;
+ }
+#endif
+#endif
+ if (bc.dsign)
+#ifdef Avoid_Underflow
+ dval(&rv) += sulp(&rv, &bc);
+#else
+ dval(&rv) += ulp(&rv);
+#endif
+#ifndef ROUND_BIASED
+ else {
+#ifdef Avoid_Underflow
+ dval(&rv) -= sulp(&rv, &bc);
+#else
+ dval(&rv) -= ulp(&rv);
+#endif
+#ifndef Sudden_Underflow
+ if (!dval(&rv)) {
+ if (bc.nd >nd) {
+ bc.uflchk = 1;
+ break;
+ }
+ goto undfl;
+ }
+#endif
+ }
+#ifdef Avoid_Underflow
+ bc.dsign = 1 - bc.dsign;
+#endif
+#endif
+ break;
+ }
+ if ((aadj = ratio(delta, bs)) <= 2.) {
+ if (bc.dsign) {
+ aadj = aadj1 = 1.;
+ }
+ else if (word1(&rv) || word0(&rv) & Bndry_mask) {
+#ifndef Sudden_Underflow
+ if (word1(&rv) == Tiny1 && !word0(&rv)) {
+ if (bc.nd >nd) {
+ bc.uflchk = 1;
+ break;
+ }
+ 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 = bc.dsign ? aadj : -aadj;
+#ifdef Check_FLT_ROUNDS
+ switch(bc.rounding) {
+ 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 /*Check_FLT_ROUNDS*/
+ }
+ y = word0(&rv) & Exp_mask;
+
+ /* Check for overflow */
+
+ if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) {
+ dval(&rv0) = dval(&rv);
+ word0(&rv) -= P*Exp_msk1;
+ adj.d = aadj1 * ulp(&rv);
+ dval(&rv) += adj.d;
+ 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 Avoid_Underflow
+ if (bc.scale && y <= 2*P*Exp_msk1) {
+ if (aadj <= 0x7fffffff) {
+ if ((z = aadj) <= 0) {
+ z = 1;
+ }
+ aadj = z;
+ aadj1 = bc.dsign ? aadj : -aadj;
+ }
+ dval(&aadj2) = aadj1;
+ word0(&aadj2) += (2*P+1)*Exp_msk1 - y;
+ aadj1 = dval(&aadj2);
+ adj.d = aadj1 * ulp(&rv);
+ dval(&rv) += adj.d;
+ if (rv.d == 0.)
+#ifdef NO_STRTOD_BIGCOMP
+ goto undfl;
+#else
+ {
+ if (bc.nd > nd) {
+ bc.dsign = 1;
+ }
+ break;
+ }
+#endif
+ }
+ else {
+ adj.d = aadj1 * ulp(&rv);
+ dval(&rv) += adj.d;
+ }
+#else
+#ifdef Sudden_Underflow
+ if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) {
+ dval(&rv0) = dval(&rv);
+ word0(&rv) += P*Exp_msk1;
+ adj.d = aadj1 * ulp(&rv);
+ dval(&rv) += adj.d;
+#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) {
+ if (bc.nd >nd) {
+ bc.uflchk = 1;
+ break;
+ }
+ goto undfl;
+ }
+ word0(&rv) = Tiny0;
+ word1(&rv) = Tiny1;
+ goto cont;
+ }
+ else {
+ word0(&rv) -= P*Exp_msk1;
+ }
+ }
+ else {
+ adj.d = aadj1 * ulp(&rv);
+ dval(&rv) += adj.d;
+ }
+#else /*Sudden_Underflow*/
+ /* 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)(int)(aadj + 0.5);
+ if (!bc.dsign) {
+ aadj1 = -aadj1;
+ }
+ }
+ adj.d = aadj1 * ulp(&rv);
+ dval(&rv) += adj.d;
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow*/
+ }
+ z = word0(&rv) & Exp_mask;
+#ifndef SET_INEXACT
+ if (bc.nd == nd) {
+#ifdef Avoid_Underflow
+ if (!bc.scale)
+#endif
+ if (y == z) {
+ /* Can we stop now? */
+ L = (Long)aadj;
+ aadj -= L;
+ /* The tolerances below are conservative. */
+ if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask) {
+ if (aadj < .4999999 || aadj > .5000001) {
+ break;
+ }
+ }
+ else if (aadj < .4999999/FLT_RADIX) {
+ break;
+ }
+ }
+ }
+#endif
+cont:
+ Bfree(bb);
+ Bfree(bd);
+ Bfree(bs);
+ Bfree(delta);
+}
+Bfree(bb);
+Bfree(bd);
+Bfree(bs);
+Bfree(bd0);
+Bfree(delta);
+#ifndef NO_STRTOD_BIGCOMP
+if (req_bigcomp) {
+ bd0 = 0;
+ bc.e0 += nz1;
+ bigcomp(&rv, s0, &bc);
+ y = word0(&rv) & Exp_mask;
+ if (y == Exp_mask) {
+ goto ovfl;
+ }
+ if (y == 0 && rv.d == 0.) {
+ goto undfl;
+ }
+}
+#endif
+#ifdef SET_INEXACT
+if (bc.inexact) {
+ if (!oldinexact) {
+ word0(&rv0) = Exp_1 + (70 << Exp_shift);
+ word1(&rv0) = 0;
+ dval(&rv0) += 1.;
+ }
+}
+else if (!oldinexact) {
+ clear_inexact();
+}
+#endif
+#ifdef Avoid_Underflow
+if (bc.scale) {
+ word0(&rv0) = Exp_1 - 2*P*Exp_msk1;
+ word1(&rv0) = 0;
+ dval(&rv) *= dval(&rv0);
+#ifndef NO_ERRNO
+ /* try to avoid the bug of testing an 8087 register value */
+#ifdef IEEE_Arith
+ if (!(word0(&rv) & Exp_mask))
+#else
+ if (word0(&rv) == 0 && word1(&rv) == 0)
+#endif
+ errno = ERANGE;
+#endif
+}
+#endif /* Avoid_Underflow */
+#ifdef SET_INEXACT
+if (bc.inexact && !(word0(&rv) & Exp_mask)) {
+ /* set underflow bit */
+ dval(&rv0) = 1e-300;
+ dval(&rv0) *= dval(&rv0);
+}
+#endif
+ret:
+if (se) {
+ *se = (char *)s;
+}
+return sign ? -dval(&rv) : dval(&rv);
+}
+
+#ifndef MULTIPLE_THREADS
+static char *dtoa_result;
+#endif
+
+static char *
+#ifdef KR_headers
+rv_alloc(i) int i;
+#else
+rv_alloc(int i)
+#endif
+{
+ int j, k, *r;
+
+ j = sizeof(ULong);
+ for(k = 0;
+ sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= i;
+ j <<= 1) {
+ k++;
+ }
+ r = (int*)Balloc(k);
+ *r = k;
+ return
+#ifndef MULTIPLE_THREADS
+ dtoa_result =
+#endif
+ (char *)(r+1);
+}
+
+static char *
+#ifdef KR_headers
+nrv_alloc(s, rve, n) char *s, **rve; int n;
+#else
+nrv_alloc(const char *s, char **rve, int n)
+#endif
+{
+ char *rv, *t;
+
+ t = rv = rv_alloc(n);
+ while((*t = *s++)) {
+ t++;
+ }
+ if (rve) {
+ *rve = t;
+ }
+ return rv;
+}
+
+/* freedtoa(s) must be used to free values s returned by dtoa
+ * when MULTIPLE_THREADS is #defined. It should be used in all cases,
+ * but for consistency with earlier versions of dtoa, it is optional
+ * when MULTIPLE_THREADS is not defined.
+ */
+
+void
+#ifdef KR_headers
+freedtoa(s) char *s;
+#else
+freedtoa(char *s)
+#endif
+{
+ Bigint *b = (Bigint *)((int *)s - 1);
+ b->maxwds = 1 << (b->k = *(int*)b);
+ Bfree(b);
+#ifndef MULTIPLE_THREADS
+ if (s == dtoa_result) {
+ dtoa_result = 0;
+ }
+#endif
+}
+
+/* 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. 112-126].
+ *
+ * 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.
+ */
+
+char *
+dtoa
+#ifdef KR_headers
+(dd, mode, ndigits, decpt, sign, rve)
+double dd; int mode, ndigits, *decpt, *sign; char **rve;
+#else
+(double dd, int mode, int ndigits, int *decpt, int *sign, char **rve)
+#endif
+{
+ /* 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,5 ==> similar to 2 and 3, respectively, but (in
+ round-nearest mode) with the tests of mode 0 to
+ possibly return a shorter string that rounds to d.
+ With IEEE arithmetic and compilation with
+ -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same
+ as modes 2 and 3 when FLT_ROUNDS != 1.
+ 6-9 ==> Debugging modes similar to mode - 4: 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.
+ */
+
+ int 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
+ int denorm;
+ ULong x;
+#endif
+ Bigint *b, *b1, *delta, *mlo, *mhi, *S;
+ U d2, eps, u;
+ double ds;
+ char *s, *s0;
+#ifndef No_leftright
+#ifdef IEEE_Arith
+ U eps1;
+#endif
+#endif
+#ifdef SET_INEXACT
+ int inexact, oldinexact;
+#endif
+#ifdef Honor_FLT_ROUNDS /*{*/
+ int Rounding;
+#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */
+ Rounding = Flt_Rounds;
+#else /*}{*/
+ Rounding = 1;
+ switch(fegetround()) {
+ case FE_TOWARDZERO: Rounding = 0; break;
+ case FE_UPWARD: Rounding = 2; break;
+ case FE_DOWNWARD: Rounding = 3;
+ }
+#endif /*}}*/
+#endif /*}*/
+
+#ifndef MULTIPLE_THREADS
+ if (dtoa_result) {
+ freedtoa(dtoa_result);
+ dtoa_result = 0;
+ }
+#endif
+
+ u.d = dd;
+ if (word0(&u) & Sign_bit) {
+ /* set sign for everything, including 0's and NaNs */
+ *sign = 1;
+ word0(&u) &= ~Sign_bit; /* clear sign bit */
+ }
+ else {
+ *sign = 0;
+ }
+
+#if defined(IEEE_Arith) + defined(VAX)
+#ifdef IEEE_Arith
+ if ((word0(&u) & Exp_mask) == Exp_mask)
+#else
+ if (word0(&u) == 0x8000)
+#endif
+ {
+ /* Infinity or NaN */
+ *decpt = 9999;
+#ifdef IEEE_Arith
+ if (!word1(&u) && !(word0(&u) & 0xfffff)) {
+ return nrv_alloc("Infinity", rve, 8);
+ }
+#endif
+ return nrv_alloc("NaN", rve, 3);
+ }
+#endif
+#ifdef IBM
+ dval(&u) += 0; /* normalize */
+#endif
+ if (!dval(&u)) {
+ *decpt = 1;
+ return nrv_alloc("0", rve, 1);
+ }
+
+#ifdef SET_INEXACT
+ try_quick = oldinexact = get_inexact();
+ inexact = 1;
+#endif
+#ifdef Honor_FLT_ROUNDS
+ if (Rounding >= 2) {
+ if (*sign) {
+ Rounding = Rounding == 2 ? 0 : 2;
+ }
+ else if (Rounding != 2) {
+ Rounding = 0;
+ }
+ }
+#endif
+
+ b = d2b(&u, &be, &bbits);
+#ifdef Sudden_Underflow
+ i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1));
+#else
+ if ((i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1)))) {
+#endif
+ dval(&d2) = dval(&u);
+ word0(&d2) &= Frac_mask1;
+ word0(&d2) |= Exp_11;
+#ifdef IBM
+ if (j = 11 - hi0bits(word0(&d2) & Frac_mask)) {
+ dval(&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(&u) << (64 - i) | word1(&u) >> (i - 32)
+ : word1(&u) << (32 - i);
+ dval(&d2) = x;
+ word0(&d2) -= 31*Exp_msk1; /* adjust exponent */
+ i -= (Bias + (P-1) - 1) + 1;
+ denorm = 1;
+}
+#endif
+ds = (dval(&d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981;
+k = (int)ds;
+if (ds < 0. && ds != k) {
+ k--; /* want k = floor(ds) */
+}
+k_check = 1;
+if (k >= 0 && k <= Ten_pmax) {
+ if (dval(&u) < 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;
+}
+
+#ifndef SET_INEXACT
+#ifdef Check_FLT_ROUNDS
+try_quick = Rounding == 1;
+#else
+try_quick = 1;
+#endif
+#endif /*SET_INEXACT*/
+
+if (mode > 5) {
+ mode -= 4;
+ try_quick = 0;
+}
+leftright = 1;
+ilim = ilim1 = -1; /* Values for cases 0 and 1; done here to */
+/* silence erroneous "gcc -Wall" warning. */
+switch(mode) {
+case 0:
+case 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;
+ }
+}
+s = s0 = rv_alloc(i);
+
+#ifdef Honor_FLT_ROUNDS
+if (mode > 1 && Rounding != 1) {
+ leftright = 0;
+}
+#endif
+
+if (ilim >= 0 && ilim <= Quick_max && try_quick) {
+
+ /* Try to get by with floating-point arithmetic. */
+
+ i = 0;
+ dval(&d2) = dval(&u);
+ 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;
+ dval(&u) /= bigtens[n_bigtens-1];
+ ieps++;
+ }
+ for(; j; j >>= 1, i++)
+ if (j & 1) {
+ ieps++;
+ ds *= bigtens[i];
+ }
+ dval(&u) /= ds;
+ }
+ else if ((j1 = -k)) {
+ dval(&u) *= tens[j1 & 0xf];
+ for(j = j1 >> 4; j; j >>= 1, i++)
+ if (j & 1) {
+ ieps++;
+ dval(&u) *= bigtens[i];
+ }
+ }
+ if (k_check && dval(&u) < 1. && ilim > 0) {
+ if (ilim1 <= 0) {
+ goto fast_failed;
+ }
+ ilim = ilim1;
+ k--;
+ dval(&u) *= 10.;
+ ieps++;
+ }
+ dval(&eps) = ieps*dval(&u) + 7.;
+ word0(&eps) -= (P-1)*Exp_msk1;
+ if (ilim == 0) {
+ S = mhi = 0;
+ dval(&u) -= 5.;
+ if (dval(&u) > dval(&eps)) {
+ goto one_digit;
+ }
+ if (dval(&u) < -dval(&eps)) {
+ goto no_digits;
+ }
+ goto fast_failed;
+ }
+#ifndef No_leftright
+ if (leftright) {
+ /* Use Steele & White method of only
+ * generating digits needed.
+ */
+ dval(&eps) = 0.5/tens[ilim-1] - dval(&eps);
+#ifdef IEEE_Arith
+ if (k0 < 0 && j1 >= 307) {
+ eps1.d = 1.01e256; /* 1.01 allows roundoff in the next few lines */
+ word0(&eps1) -= Exp_msk1 * (Bias+P-1);
+ dval(&eps1) *= tens[j1 & 0xf];
+ for(i = 0, j = (j1-256) >> 4; j; j >>= 1, i++)
+ if (j & 1) {
+ dval(&eps1) *= bigtens[i];
+ }
+ if (eps.d < eps1.d) {
+ eps.d = eps1.d;
+ }
+ }
+#endif
+ for(i = 0;;) {
+ L = dval(&u);
+ dval(&u) -= L;
+ *s++ = '0' + (int)L;
+ if (1. - dval(&u) < dval(&eps)) {
+ goto bump_up;
+ }
+ if (dval(&u) < dval(&eps)) {
+ goto ret1;
+ }
+ if (++i >= ilim) {
+ break;
+ }
+ dval(&eps) *= 10.;
+ dval(&u) *= 10.;
+ }
+ }
+ else {
+#endif
+ /* Generate ilim digits, then fix them up. */
+ dval(&eps) *= tens[ilim-1];
+ for(i = 1;; i++, dval(&u) *= 10.) {
+ L = (Long)(dval(&u));
+ if (!(dval(&u) -= L)) {
+ ilim = i;
+ }
+ *s++ = '0' + (int)L;
+ if (i == ilim) {
+ if (dval(&u) > 0.5 + dval(&eps)) {
+ goto bump_up;
+ }
+ else if (dval(&u) < 0.5 - dval(&eps)) {
+ while(*--s == '0');
+ s++;
+ goto ret1;
+ }
+ break;
+ }
+ }
+#ifndef No_leftright
+ }
+#endif
+fast_failed:
+ s = s0;
+ dval(&u) = dval(&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 || dval(&u) <= 5*ds) {
+ goto no_digits;
+ }
+ goto one_digit;
+ }
+ for(i = 1;; i++, dval(&u) *= 10.) {
+ L = (Long)(dval(&u) / ds);
+ dval(&u) -= L*ds;
+#ifdef Check_FLT_ROUNDS
+ /* If FLT_ROUNDS == 2, L will usually be high by 1 */
+ if (dval(&u) < 0) {
+ L--;
+ dval(&u) += ds;
+ }
+#endif
+ *s++ = '0' + (int)L;
+ if (!dval(&u)) {
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ break;
+ }
+ if (i == ilim) {
+#ifdef Honor_FLT_ROUNDS
+ if (mode > 1)
+ switch(Rounding) {
+ case 0: goto ret1;
+ case 2: goto bump_up;
+ }
+#endif
+ dval(&u) += dval(&u);
+#ifdef ROUND_BIASED
+ if (dval(&u) >= ds)
+#else
+ if (dval(&u) > ds || (dval(&u) == ds && L & 1))
+#endif
+ {
+bump_up:
+ while(*--s == '9')
+ if (s == s0) {
+ k++;
+ *s = '0';
+ break;
+ }
+ ++*s++;
+ }
+ break;
+ }
+ }
+ goto ret1;
+}
+
+m2 = b2;
+m5 = b5;
+mhi = mlo = 0;
+if (leftright) {
+ 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
+ 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)) {
+ 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. */
+
+spec_case = 0;
+if ((mode < 2 || leftright)
+#ifdef Honor_FLT_ROUNDS
+ && Rounding == 1
+#endif
+ ) {
+ if (!word1(&u) && !(word0(&u) & Bndry_mask)
+#ifndef Sudden_Underflow
+ && word0(&u) & (Exp_mask & ~Exp_msk1)
+#endif
+ ) {
+ /* The special case */
+ b2 += Log2P;
+ s2 += Log2P;
+ spec_case = 1;
+ }
+}
+
+/* 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.
+ */
+i = dshift(S, s2);
+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 == 3 || mode == 5)) {
+ 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 != 1 && !(word1(&u) & 1)
+#ifdef Honor_FLT_ROUNDS
+ && Rounding >= 1
+#endif
+ ) {
+ if (dig == '9') {
+ goto round_9_up;
+ }
+ if (j > 0) {
+ dig++;
+ }
+#ifdef SET_INEXACT
+ else if (!b->x[0] && b->wds <= 1) {
+ inexact = 0;
+ }
+#endif
+ *s++ = dig;
+ goto ret;
+ }
+#endif
+ if (j < 0 || (j == 0 && mode != 1
+#ifndef ROUND_BIASED
+ && !(word1(&u) & 1)
+#endif
+ )) {
+ if (!b->x[0] && b->wds <= 1) {
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ goto accept_dig;
+ }
+#ifdef Honor_FLT_ROUNDS
+ if (mode > 1)
+ switch(Rounding) {
+ case 0: goto accept_dig;
+ case 2: goto keep_dig;
+ }
+#endif /*Honor_FLT_ROUNDS*/
+ if (j1 > 0) {
+ b = lshift(b, 1);
+ j1 = cmp(b, S);
+#ifdef ROUND_BIASED
+ if (j1 >= 0 /*)*/
+#else
+ if ((j1 > 0 || (j1 == 0 && dig & 1))
+#endif
+ && dig++ == '9')
+ goto round_9_up;
+ }
+accept_dig:
+ *s++ = dig;
+ goto ret;
+ }
+ if (j1 > 0) {
+#ifdef Honor_FLT_ROUNDS
+ if (!Rounding) {
+ goto accept_dig;
+ }
+#endif
+ if (dig == '9') { /* possible if i == 1 */
+round_9_up:
+ *s++ = '9';
+ goto roundoff;
+ }
+ *s++ = dig + 1;
+ goto ret;
+ }
+#ifdef Honor_FLT_ROUNDS
+keep_dig:
+#endif
+ *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 (!b->x[0] && b->wds <= 1) {
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ goto ret;
+ }
+ if (i >= ilim) {
+ break;
+ }
+ b = multadd(b, 10, 0);
+ }
+
+/* Round off last digit */
+
+#ifdef Honor_FLT_ROUNDS
+switch(Rounding) {
+case 0: goto trimzeros;
+case 2: goto roundoff;
+}
+#endif
+b = lshift(b, 1);
+j = cmp(b, S);
+#ifdef ROUND_BIASED
+if (j >= 0)
+#else
+if (j > 0 || (j == 0 && dig & 1))
+#endif
+{
+roundoff:
+ while(*--s == '9')
+ if (s == s0) {
+ k++;
+ *s++ = '1';
+ goto ret;
+ }
+ ++*s++;
+}
+else {
+#ifdef Honor_FLT_ROUNDS
+trimzeros:
+#endif
+ while(*--s == '0');
+ s++;
+}
+ret:
+Bfree(S);
+if (mhi) {
+ if (mlo && mlo != mhi) {
+ Bfree(mlo);
+ }
+ Bfree(mhi);
+}
+ret1:
+#ifdef SET_INEXACT
+if (inexact) {
+ if (!oldinexact) {
+ word0(&u) = Exp_1 + (70 << Exp_shift);
+ word1(&u) = 0;
+ dval(&u) += 1.;
+ }
+}
+else if (!oldinexact) {
+ clear_inexact();
+}
+#endif
+Bfree(b);
+*s = 0;
+*decpt = k + 1;
+if (rve) {
+ *rve = s;
+}
+return s0;
+}
+#ifdef __cplusplus
+}
+#endif
diff --git a/nsprpub/pr/src/misc/pralarm.c b/nsprpub/pr/src/misc/pralarm.c
new file mode 100644
index 0000000000..8130215c24
--- /dev/null
+++ b/nsprpub/pr/src/misc/pralarm.c
@@ -0,0 +1,263 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+/**********************************************************************/
+/******************************* PRALARM ******************************/
+/**********************************************************************/
+
+#include "obsolete/pralarm.h"
+
+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.next;
+ 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);
+ }
+ 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->lastNotify;
+ 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(void)
+{
+ 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/nsprpub/pr/src/misc/pratom.c b/nsprpub/pr/src/misc/pratom.c
new file mode 100644
index 0000000000..4f0e3da303
--- /dev/null
+++ b/nsprpub/pr/src/misc/pratom.c
@@ -0,0 +1,384 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** PR Atomic operations
+*/
+
+
+#include "pratom.h"
+#include "primpl.h"
+
+#include <string.h>
+
+/*
+ * The following is a fallback implementation that 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.
+ */
+
+#if !defined(_PR_HAVE_ATOMIC_OPS)
+
+#if defined(_PR_PTHREADS)
+/*
+ * PR_AtomicDecrement() is used in NSPR's thread-specific data
+ * destructor. Because thread-specific data destructors may be
+ * invoked after a PR_Cleanup() call, we need an implementation
+ * of the atomic routines that doesn't need NSPR to be initialized.
+ */
+
+/*
+ * We use a set of locks for all the emulated atomic operations.
+ * By hashing on the address of the integer to be locked the
+ * contention between multiple threads should be lessened.
+ *
+ * The number of atomic locks can be set by the environment variable
+ * NSPR_ATOMIC_HASH_LOCKS
+ */
+
+/*
+ * lock counts should be a power of 2
+ */
+#define DEFAULT_ATOMIC_LOCKS 16 /* should be in sync with the number of initializers
+ below */
+#define MAX_ATOMIC_LOCKS (4 * 1024)
+
+static pthread_mutex_t static_atomic_locks[DEFAULT_ATOMIC_LOCKS] = {
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER
+};
+
+#ifdef DEBUG
+static PRInt32 static_hash_lock_counts[DEFAULT_ATOMIC_LOCKS];
+static PRInt32 *hash_lock_counts = static_hash_lock_counts;
+#endif
+
+static PRUint32 num_atomic_locks = DEFAULT_ATOMIC_LOCKS;
+static pthread_mutex_t *atomic_locks = static_atomic_locks;
+static PRUint32 atomic_hash_mask = DEFAULT_ATOMIC_LOCKS - 1;
+
+#define _PR_HASH_FOR_LOCK(ptr) \
+ ((PRUint32) (((PRUptrdiff) (ptr) >> 2) ^ \
+ ((PRUptrdiff) (ptr) >> 8)) & \
+ atomic_hash_mask)
+
+void _PR_MD_INIT_ATOMIC()
+{
+ char *eval;
+ int index;
+
+
+ PR_ASSERT(PR_FloorLog2(MAX_ATOMIC_LOCKS) ==
+ PR_CeilingLog2(MAX_ATOMIC_LOCKS));
+
+ PR_ASSERT(PR_FloorLog2(DEFAULT_ATOMIC_LOCKS) ==
+ PR_CeilingLog2(DEFAULT_ATOMIC_LOCKS));
+
+ if (((eval = getenv("NSPR_ATOMIC_HASH_LOCKS")) != NULL) &&
+ ((num_atomic_locks = atoi(eval)) != DEFAULT_ATOMIC_LOCKS)) {
+
+ if (num_atomic_locks > MAX_ATOMIC_LOCKS) {
+ num_atomic_locks = MAX_ATOMIC_LOCKS;
+ }
+ else if (num_atomic_locks < 1) {
+ num_atomic_locks = 1;
+ }
+ else {
+ num_atomic_locks = PR_FloorLog2(num_atomic_locks);
+ num_atomic_locks = 1L << num_atomic_locks;
+ }
+ atomic_locks = (pthread_mutex_t *) PR_Malloc(sizeof(pthread_mutex_t) *
+ num_atomic_locks);
+ if (atomic_locks) {
+ for (index = 0; index < num_atomic_locks; index++) {
+ if (pthread_mutex_init(&atomic_locks[index], NULL)) {
+ PR_DELETE(atomic_locks);
+ atomic_locks = NULL;
+ break;
+ }
+ }
+ }
+#ifdef DEBUG
+ if (atomic_locks) {
+ hash_lock_counts = PR_CALLOC(num_atomic_locks * sizeof(PRInt32));
+ if (hash_lock_counts == NULL) {
+ PR_DELETE(atomic_locks);
+ atomic_locks = NULL;
+ }
+ }
+#endif
+ if (atomic_locks == NULL) {
+ /*
+ * Use statically allocated locks
+ */
+ atomic_locks = static_atomic_locks;
+ num_atomic_locks = DEFAULT_ATOMIC_LOCKS;
+#ifdef DEBUG
+ hash_lock_counts = static_hash_lock_counts;
+#endif
+ }
+ atomic_hash_mask = num_atomic_locks - 1;
+ }
+ PR_ASSERT(PR_FloorLog2(num_atomic_locks) ==
+ PR_CeilingLog2(num_atomic_locks));
+}
+
+PRInt32
+_PR_MD_ATOMIC_INCREMENT(PRInt32 *val)
+{
+ PRInt32 rv;
+ PRInt32 idx = _PR_HASH_FOR_LOCK(val);
+
+ pthread_mutex_lock(&atomic_locks[idx]);
+ rv = ++(*val);
+#ifdef DEBUG
+ hash_lock_counts[idx]++;
+#endif
+ pthread_mutex_unlock(&atomic_locks[idx]);
+ return rv;
+}
+
+PRInt32
+_PR_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val)
+{
+ PRInt32 rv;
+ PRInt32 idx = _PR_HASH_FOR_LOCK(ptr);
+
+ pthread_mutex_lock(&atomic_locks[idx]);
+ rv = ((*ptr) += val);
+#ifdef DEBUG
+ hash_lock_counts[idx]++;
+#endif
+ pthread_mutex_unlock(&atomic_locks[idx]);
+ return rv;
+}
+
+PRInt32
+_PR_MD_ATOMIC_DECREMENT(PRInt32 *val)
+{
+ PRInt32 rv;
+ PRInt32 idx = _PR_HASH_FOR_LOCK(val);
+
+ pthread_mutex_lock(&atomic_locks[idx]);
+ rv = --(*val);
+#ifdef DEBUG
+ hash_lock_counts[idx]++;
+#endif
+ pthread_mutex_unlock(&atomic_locks[idx]);
+ return rv;
+}
+
+PRInt32
+_PR_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
+{
+ PRInt32 rv;
+ PRInt32 idx = _PR_HASH_FOR_LOCK(val);
+
+ pthread_mutex_lock(&atomic_locks[idx]);
+ rv = *val;
+ *val = newval;
+#ifdef DEBUG
+ hash_lock_counts[idx]++;
+#endif
+ pthread_mutex_unlock(&atomic_locks[idx]);
+ return rv;
+}
+#else /* _PR_PTHREADS */
+/*
+ * We use a single lock for all the emulated atomic operations.
+ * The lock contention should be acceptable.
+ */
+static PRLock *atomic_lock = NULL;
+void _PR_MD_INIT_ATOMIC(void)
+{
+ if (atomic_lock == NULL) {
+ atomic_lock = PR_NewLock();
+ }
+}
+
+PRInt32
+_PR_MD_ATOMIC_INCREMENT(PRInt32 *val)
+{
+ PRInt32 rv;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ PR_Lock(atomic_lock);
+ rv = ++(*val);
+ PR_Unlock(atomic_lock);
+ return rv;
+}
+
+PRInt32
+_PR_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val)
+{
+ PRInt32 rv;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ PR_Lock(atomic_lock);
+ rv = ((*ptr) += val);
+ PR_Unlock(atomic_lock);
+ return rv;
+}
+
+PRInt32
+_PR_MD_ATOMIC_DECREMENT(PRInt32 *val)
+{
+ PRInt32 rv;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ PR_Lock(atomic_lock);
+ rv = --(*val);
+ PR_Unlock(atomic_lock);
+ return rv;
+}
+
+PRInt32
+_PR_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
+{
+ PRInt32 rv;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ PR_Lock(atomic_lock);
+ rv = *val;
+ *val = newval;
+ PR_Unlock(atomic_lock);
+ return rv;
+}
+#endif /* _PR_PTHREADS */
+
+#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/nsprpub/pr/src/misc/praton.c b/nsprpub/pr/src/misc/praton.c
new file mode 100644
index 0000000000..3e729f5879
--- /dev/null
+++ b/nsprpub/pr/src/misc/praton.c
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*******************************************************************************
+ * The following function pr_inet_aton is based on the BSD function inet_aton
+ * with some modifications. The license and copyright notices applying to this
+ * function appear below. Modifications are also according to the license below.
+ ******************************************************************************/
+
+#include "prnetdb.h"
+
+/*
+ * Copyright (c) 1983, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define XX 127
+static const unsigned char index_hex[256] = {
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,XX,XX, XX,XX,XX,XX,
+ XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+};
+
+static PRBool _isdigit(char c) {
+ return c >= '0' && c <= '9';
+}
+static PRBool _isxdigit(char c) {
+ return index_hex[(unsigned char) c] != XX;
+}
+static PRBool _isspace(char c) {
+ return c == ' ' || (c >= '\t' && c <= '\r');
+}
+#undef XX
+
+int
+pr_inet_aton(const char *cp, PRUint32 *addr)
+{
+ PRUint32 val;
+ int base, n;
+ char c;
+ PRUint8 parts[4];
+ PRUint8 *pp = parts;
+ int digit;
+
+ c = *cp;
+ for (;;) {
+ /*
+ * Collect number up to ``.''.
+ * Values are specified as for C:
+ * 0x=hex, 0=octal, isdigit=decimal.
+ */
+ if (!_isdigit(c)) {
+ return (0);
+ }
+ val = 0; base = 10; digit = 0;
+ if (c == '0') {
+ c = *++cp;
+ if (c == 'x' || c == 'X') {
+ base = 16, c = *++cp;
+ }
+ else {
+ base = 8;
+ digit = 1;
+ }
+ }
+ for (;;) {
+ if (_isdigit(c)) {
+ if (base == 8 && (c == '8' || c == '9')) {
+ return (0);
+ }
+ val = (val * base) + (c - '0');
+ c = *++cp;
+ digit = 1;
+ } else if (base == 16 && _isxdigit(c)) {
+ val = (val << 4) + index_hex[(unsigned char) c];
+ c = *++cp;
+ digit = 1;
+ } else {
+ break;
+ }
+ }
+ if (c == '.') {
+ /*
+ * Internet format:
+ * a.b.c.d
+ * a.b.c (with c treated as 16 bits)
+ * a.b (with b treated as 24 bits)
+ */
+ if (pp >= parts + 3 || val > 0xffU) {
+ return (0);
+ }
+ *pp++ = val;
+ c = *++cp;
+ } else {
+ break;
+ }
+ }
+ /*
+ * Check for trailing characters.
+ */
+ if (c != '\0' && !_isspace(c)) {
+ return (0);
+ }
+ /*
+ * Did we get a valid digit?
+ */
+ if (!digit) {
+ return (0);
+ }
+ /*
+ * Concoct the address according to
+ * the number of parts specified.
+ */
+ n = pp - parts + 1;
+ switch (n) {
+ case 1: /*%< a -- 32 bits */
+ break;
+
+ case 2: /*%< a.b -- 8.24 bits */
+ if (val > 0xffffffU) {
+ return (0);
+ }
+ val |= (unsigned int)parts[0] << 24;
+ break;
+
+ case 3: /*%< a.b.c -- 8.8.16 bits */
+ if (val > 0xffffU) {
+ return (0);
+ }
+ val |= ((unsigned int)parts[0] << 24) | ((unsigned int)parts[1] << 16);
+ break;
+
+ case 4: /*%< a.b.c.d -- 8.8.8.8 bits */
+ if (val > 0xffU) {
+ return (0);
+ }
+ val |= ((unsigned int)parts[0] << 24) |
+ ((unsigned int)parts[1] << 16) |
+ ((unsigned int)parts[2] << 8);
+ break;
+ }
+ *addr = PR_htonl(val);
+ return (1);
+}
+
diff --git a/nsprpub/pr/src/misc/prcountr.c b/nsprpub/pr/src/misc/prcountr.c
new file mode 100644
index 0000000000..29d7d4ccbe
--- /dev/null
+++ b/nsprpub/pr/src/misc/prcountr.c
@@ -0,0 +1,483 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** 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.
+**
+*/
+
+#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 *)&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 *)&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 = NULL;
+ 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() */
diff --git a/nsprpub/pr/src/misc/prdtoa.c b/nsprpub/pr/src/misc/prdtoa.c
new file mode 100644
index 0000000000..51b331bac8
--- /dev/null
+++ b/nsprpub/pr/src/misc/prdtoa.c
@@ -0,0 +1,3697 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This file is based on the third-party code dtoa.c. We minimize our
+ * modifications to third-party code to make it easy to merge new versions.
+ * The author of dtoa.c was not willing to add the parentheses suggested by
+ * GCC, so we suppress these warnings.
+ */
+#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)
+#pragma GCC diagnostic ignored "-Wparentheses"
+#endif
+
+#include "primpl.h"
+#include "prbit.h"
+
+#define MULTIPLE_THREADS
+#define ACQUIRE_DTOA_LOCK(n) PR_Lock(dtoa_lock[n])
+#define FREE_DTOA_LOCK(n) PR_Unlock(dtoa_lock[n])
+
+static PRLock *dtoa_lock[2];
+
+void _PR_InitDtoa(void)
+{
+ dtoa_lock[0] = PR_NewLock();
+ dtoa_lock[1] = PR_NewLock();
+}
+
+void _PR_CleanupDtoa(void)
+{
+ PR_DestroyLock(dtoa_lock[0]);
+ dtoa_lock[0] = NULL;
+ PR_DestroyLock(dtoa_lock[1]);
+ dtoa_lock[1] = NULL;
+
+ /* FIXME: deal with freelist and p5s. */
+}
+
+#if !defined(__ARM_EABI__) \
+ && (defined(__arm) || defined(__arm__) || defined(__arm26__) \
+ || defined(__arm32__))
+#define IEEE_ARM
+#elif defined(IS_LITTLE_ENDIAN)
+#define IEEE_8087
+#else
+#define IEEE_MC68k
+#endif
+
+#define Long PRInt32
+#define ULong PRUint32
+#define NO_LONG_LONG
+
+#define No_Hex_NaN
+
+/****************************************************************
+ *
+ * The author of this software is David M. Gay.
+ *
+ * Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
+ *
+ * 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 LUCENT 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 (dmg at acm dot org,
+ * with " at " changed at "@" and " dot " changed to "."). */
+
+/* On a machine with IEEE extended-precision registers, it is
+ * necessary to specify double-precision (53-bit) rounding precision
+ * before invoking strtod or dtoa. If the machine uses (the equivalent
+ * of) Intel 80x87 arithmetic, the call
+ * _control87(PC_53, MCW_PC);
+ * does this with many compilers. Whether this or another call is
+ * appropriate depends on the compiler; for this to work, it may be
+ * necessary to #include "float.h" or another system-dependent header
+ * file.
+ */
+
+/* strtod for IEEE-, VAX-, and IBM-arithmetic machines.
+ *
+ * This strtod returns a nearest machine number to the input decimal
+ * string (or sets errno to ERANGE). With IEEE arithmetic, ties are
+ * broken by the IEEE round-even rule. Otherwise ties are broken by
+ * biased rounding (add half and chop).
+ *
+ * Inspired loosely by William D. Clinger's paper "How to Read Floating
+ * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101].
+ *
+ * Modifications:
+ *
+ * 1. We only require IEEE, IBM, or VAX double-precision
+ * arithmetic (not IEEE double-extended).
+ * 2. We get by with floating-point arithmetic in a case that
+ * Clinger missed -- when we're computing d * 10^n
+ * for a small integer d and the integer n is not too
+ * much larger than 22 (the maximum integer k for which
+ * we can represent 10^k exactly), we may be able to
+ * compute (d*10^k) * 10^(e-k) with just one roundoff.
+ * 3. Rather than a bit-at-a-time adjustment of the binary
+ * result in the hard case, we use floating-point
+ * arithmetic to determine the adjustment to within
+ * one bit; only in really hard cases do we need to
+ * compute a second residual.
+ * 4. Because of 3., we don't need a large table of powers of 10
+ * for ten-to-e (just some small tables, e.g. of 10^k
+ * for 0 <= k <= 22).
+ */
+
+/*
+ * #define IEEE_8087 for IEEE-arithmetic machines where the least
+ * significant byte has the lowest address.
+ * #define IEEE_MC68k for IEEE-arithmetic machines where the most
+ * significant byte has the lowest address.
+ * #define IEEE_ARM for IEEE-arithmetic machines where the two words
+ * in a double are stored in big endian order but the two shorts
+ * in a word are still stored in little endian order.
+ * #define Long int on machines with 32-bit ints and 64-bit longs.
+ * #define IBM for IBM mainframe-style floating-point arithmetic.
+ * #define VAX for VAX-style floating-point arithmetic (D_floating).
+ * #define No_leftright to omit left-right logic in fast floating-point
+ * computation of dtoa.
+ * #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3
+ * and strtod and dtoa should round accordingly.
+ * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3
+ * and Honor_FLT_ROUNDS is not #defined.
+ * #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 NO_LONG_LONG on machines that do not have a "long long"
+ * integer type (of >= 64 bits). On such machines, you can
+ * #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. If long long is available and the name is
+ * something other than "long long", #define Llong to be the name,
+ * and if "unsigned Llong" does not work as an unsigned version of
+ * Llong, #define #ULLong to be the corresponding unsigned type.
+ * #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. Similarly, if you
+ * want something other than the system's free() to be called to
+ * recycle memory acquired from MALLOC, #define FREE to be the
+ * name of the alternate routine. (FREE or free is only called in
+ * pathological cases, e.g., in a dtoa call after a dtoa return in
+ * mode 3 with thousands of digits requested.)
+ * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making
+ * memory allocations from a private pool of memory when possible.
+ * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes,
+ * unless #defined to be a different length. This default length
+ * suffices to get rid of MALLOC calls except for unusual cases,
+ * such as decimal-to-binary conversion of a very long string of
+ * digits. The longest string dtoa can return is about 751 bytes
+ * long. For conversions by strtod of strings of 800 digits and
+ * all dtoa conversions in single-threaded executions with 8-byte
+ * pointers, PRIVATE_MEM >= 7400 appears to suffice; with 4-byte
+ * pointers, PRIVATE_MEM >= 7112 appears adequate.
+ * #define INFNAN_CHECK on IEEE systems to cause strtod to check for
+ * Infinity and NaN (case insensitively). On some systems (e.g.,
+ * some HP systems), it may be necessary to #define NAN_WORD0
+ * appropriately -- to the most significant word of a quiet NaN.
+ * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.)
+ * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined,
+ * strtod also accepts (case insensitively) strings of the form
+ * NaN(x), where x is a string of hexadecimal digits and spaces;
+ * if there is only one string of hexadecimal digits, it is taken
+ * for the 52 fraction bits of the resulting NaN; if there are two
+ * or more strings of hex digits, the first is for the high 20 bits,
+ * the second and subsequent for the low 32 bits, with intervening
+ * white space ignored; but if this results in none of the 52
+ * fraction bits being on (an IEEE Infinity symbol), then NAN_WORD0
+ * and NAN_WORD1 are used instead.
+ * #define MULTIPLE_THREADS if the system offers preemptively scheduled
+ * multiple threads. In this case, you must provide (or suitably
+ * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed
+ * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed
+ * in pow5mult, ensures lazy evaluation of only one copy of high
+ * powers of 5; omitting this lock would introduce a small
+ * probability of wasting memory, but would otherwise be harmless.)
+ * You must also invoke freedtoa(s) to free the value s returned by
+ * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined.
+ * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that
+ * avoids underflows on inputs whose result does not underflow.
+ * If you #define NO_IEEE_Scale on a machine that uses IEEE-format
+ * floating-point numbers and flushes underflows to zero rather
+ * than implementing gradual underflow, then you must also #define
+ * Sudden_Underflow.
+ * #define USE_LOCALE to use the current locale's decimal_point value.
+ * #define SET_INEXACT if IEEE arithmetic is being used and extra
+ * computation should be done to set the inexact flag when the
+ * result is inexact and avoid setting inexact when the result
+ * is exact. In this case, dtoa.c must be compiled in
+ * an environment, perhaps provided by #include "dtoa.c" in a
+ * suitable wrapper, that defines two functions,
+ * int get_inexact(void);
+ * void clear_inexact(void);
+ * such that get_inexact() returns a nonzero value if the
+ * inexact bit is already set, and clear_inexact() sets the
+ * inexact bit to 0. When SET_INEXACT is #defined, strtod
+ * also does extra computations to set the underflow and overflow
+ * flags when appropriate (i.e., when the result is tiny and
+ * inexact or when it is a numeric value rounded to +-infinity).
+ * #define NO_ERRNO if strtod should not assign errno = ERANGE when
+ * the result overflows to +-Infinity or underflows to 0.
+ */
+
+#ifndef Long
+#define Long long
+#endif
+#ifndef ULong
+typedef unsigned Long ULong;
+#endif
+
+#ifdef DEBUG
+#include "stdio.h"
+#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);}
+#endif
+
+#include "stdlib.h"
+#include "string.h"
+
+#ifdef USE_LOCALE
+#include "locale.h"
+#endif
+
+#ifdef MALLOC
+#ifdef KR_headers
+extern char *MALLOC();
+#else
+extern void *MALLOC(size_t);
+#endif
+#else
+#define MALLOC malloc
+#endif
+
+#ifndef Omit_Private_Memory
+#ifndef PRIVATE_MEM
+#define PRIVATE_MEM 2304
+#endif
+#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double))
+static double private_mem[PRIVATE_mem], *pmem_next = private_mem;
+#endif
+
+#undef IEEE_Arith
+#undef Avoid_Underflow
+#ifdef IEEE_MC68k
+#define IEEE_Arith
+#endif
+#ifdef IEEE_8087
+#define IEEE_Arith
+#endif
+#ifdef IEEE_ARM
+#define IEEE_Arith
+#endif
+
+#include "errno.h"
+
+#ifdef Bad_float_h
+
+#ifdef IEEE_Arith
+#define DBL_DIG 15
+#define DBL_MAX_10_EXP 308
+#define DBL_MAX_EXP 1024
+#define FLT_RADIX 2
+#endif /*IEEE_Arith*/
+
+#ifdef IBM
+#define DBL_DIG 16
+#define DBL_MAX_10_EXP 75
+#define DBL_MAX_EXP 63
+#define FLT_RADIX 16
+#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 DBL_MAX 1.7014118346046923e+38
+#endif
+
+#ifndef LONG_MAX
+#define LONG_MAX 2147483647
+#endif
+
+#else /* ifndef Bad_float_h */
+#include "float.h"
+#endif /* Bad_float_h */
+
+#ifndef __MATH_H__
+#include "math.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef CONST
+#ifdef KR_headers
+#define CONST /* blank */
+#else
+#define CONST const
+#endif
+#endif
+
+#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(IEEE_ARM) + defined(VAX) + defined(IBM) != 1
+Exactly one of IEEE_8087, IEEE_MC68k, IEEE_ARM, VAX, or IBM should be defined.
+#endif
+
+typedef union {
+ double d;
+ ULong L[2];
+} U;
+
+#define dval(x) (x).d
+#ifdef IEEE_8087
+#define word0(x) (x).L[1]
+#define word1(x) (x).L[0]
+#else
+#define word0(x) (x).L[0]
+#define word1(x) (x).L[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(IEEE_ARM) + 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) */
+
+#ifdef IEEE_Arith
+#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 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
+#ifndef NO_IEEE_Scale
+#define Avoid_Underflow
+#ifdef Flush_Denorm /* debugging option */
+#undef Sudden_Underflow
+#endif
+#endif
+
+#ifndef Flt_Rounds
+#ifdef FLT_ROUNDS
+#define Flt_Rounds FLT_ROUNDS
+#else
+#define Flt_Rounds 1
+#endif
+#endif /*Flt_Rounds*/
+
+#ifdef Honor_FLT_ROUNDS
+#define Rounding rounding
+#undef Check_FLT_ROUNDS
+#define Check_FLT_ROUNDS
+#else
+#define Rounding Flt_Rounds
+#endif
+
+#else /* ifndef IEEE_Arith */
+#undef Check_FLT_ROUNDS
+#undef Honor_FLT_ROUNDS
+#undef SET_INEXACT
+#undef Sudden_Underflow
+#define Sudden_Underflow
+#ifdef IBM
+#undef Flt_Rounds
+#define Flt_Rounds 0
+#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 */
+#undef Flt_Rounds
+#define Flt_Rounds 1
+#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 /* IBM, VAX */
+#endif /* IEEE_Arith */
+
+#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)
+#ifdef KR_headers
+extern double rnd_prod(), rnd_quot();
+#else
+extern double rnd_prod(double, double), rnd_quot(double, double);
+#endif
+#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 Pack_32
+#define Pack_32
+#endif
+
+#ifdef KR_headers
+#define FFFFFFFF ((((unsigned long)0xffff)<<16)|(unsigned long)0xffff)
+#else
+#define FFFFFFFF 0xffffffffUL
+#endif
+
+#ifdef NO_LONG_LONG
+#undef ULLong
+#ifdef Just_16
+#undef Pack_32
+/* 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.
+ */
+#endif
+#else /* long long available */
+#ifndef Llong
+#define Llong long long
+#endif
+#ifndef ULLong
+#define ULLong unsigned Llong
+#endif
+#endif /* NO_LONG_LONG */
+
+#ifndef MULTIPLE_THREADS
+#define ACQUIRE_DTOA_LOCK(n) /*nothing*/
+#define FREE_DTOA_LOCK(n) /*nothing*/
+#endif
+
+#define Kmax 7
+
+struct
+ Bigint {
+ struct Bigint *next;
+ int k, maxwds, sign, wds;
+ ULong x[1];
+};
+
+typedef struct Bigint Bigint;
+
+static Bigint *freelist[Kmax+1];
+
+static Bigint *
+Balloc
+#ifdef KR_headers
+(k) int k;
+#else
+(int k)
+#endif
+{
+ int x;
+ Bigint *rv;
+#ifndef Omit_Private_Memory
+ unsigned int len;
+#endif
+
+ ACQUIRE_DTOA_LOCK(0);
+ /* The k > Kmax case does not need ACQUIRE_DTOA_LOCK(0), */
+ /* but this case seems very unlikely. */
+ if (k <= Kmax && (rv = freelist[k])) {
+ freelist[k] = rv->next;
+ }
+ else {
+ x = 1 << k;
+#ifdef Omit_Private_Memory
+ rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong));
+#else
+ len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1)
+ /sizeof(double);
+ if (k <= Kmax && pmem_next - private_mem + len <= PRIVATE_mem) {
+ rv = (Bigint*)pmem_next;
+ pmem_next += len;
+ }
+ else {
+ rv = (Bigint*)MALLOC(len*sizeof(double));
+ }
+#endif
+ rv->k = k;
+ rv->maxwds = x;
+ }
+ FREE_DTOA_LOCK(0);
+ rv->sign = rv->wds = 0;
+ return rv;
+}
+
+static void
+Bfree
+#ifdef KR_headers
+(v) Bigint *v;
+#else
+(Bigint *v)
+#endif
+{
+ if (v) {
+ if (v->k > Kmax)
+#ifdef FREE
+ FREE((void*)v);
+#else
+ free((void*)v);
+#endif
+ else {
+ ACQUIRE_DTOA_LOCK(0);
+ v->next = freelist[v->k];
+ freelist[v->k] = v;
+ FREE_DTOA_LOCK(0);
+ }
+ }
+}
+
+#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \
+y->wds*sizeof(Long) + 2*sizeof(int))
+
+static Bigint *
+multadd
+#ifdef KR_headers
+(b, m, a) Bigint *b; int m, a;
+#else
+(Bigint *b, int m, int a) /* multiply by m and add a */
+#endif
+{
+ int i, wds;
+#ifdef ULLong
+ ULong *x;
+ ULLong carry, y;
+#else
+ ULong carry, *x, y;
+#ifdef Pack_32
+ ULong xi, z;
+#endif
+#endif
+ Bigint *b1;
+
+ wds = b->wds;
+ x = b->x;
+ i = 0;
+ carry = a;
+ do {
+#ifdef ULLong
+ y = *x * (ULLong)m + carry;
+ carry = y >> 32;
+ *x++ = y & FFFFFFFF;
+#else
+#ifdef Pack_32
+ xi = *x;
+ y = (xi & 0xffff) * m + carry;
+ z = (xi >> 16) * m + (y >> 16);
+ carry = z >> 16;
+ *x++ = (z << 16) + (y & 0xffff);
+#else
+ y = *x * m + carry;
+ carry = y >> 16;
+ *x++ = y & 0xffff;
+#endif
+#endif
+ }
+ while(++i < wds);
+ if (carry) {
+ if (wds >= b->maxwds) {
+ b1 = Balloc(b->k+1);
+ Bcopy(b1, b);
+ Bfree(b);
+ b = b1;
+ }
+ b->x[wds++] = carry;
+ b->wds = wds;
+ }
+ return b;
+}
+
+static Bigint *
+s2b
+#ifdef KR_headers
+(s, nd0, nd, y9) CONST char *s; int nd0, nd; ULong y9;
+#else
+(CONST char *s, int nd0, int nd, ULong y9)
+#endif
+{
+ Bigint *b;
+ int 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 int
+hi0bits
+#ifdef KR_headers
+(x) register ULong x;
+#else
+(register ULong x)
+#endif
+{
+#ifdef PR_HAVE_BUILTIN_BITSCAN32
+ return( (!x) ? 32 : pr_bitscan_clz32(x) );
+#else
+ register int 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;
+#endif /* PR_HAVE_BUILTIN_BITSCAN32 */
+}
+
+static int
+lo0bits
+#ifdef KR_headers
+(y) ULong *y;
+#else
+(ULong *y)
+#endif
+{
+#ifdef PR_HAVE_BUILTIN_BITSCAN32
+ int k;
+ ULong x = *y;
+
+ if (x>1) {
+ *y = ( x >> (k = pr_bitscan_ctz32(x)) );
+ }
+ else {
+ k = ((x ^ 1) << 5);
+ }
+#else
+ register int k;
+ register ULong 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) {
+ return 32;
+ }
+ }
+ *y = x;
+#endif /* PR_HAVE_BUILTIN_BITSCAN32 */
+ return k;
+}
+
+static Bigint *
+i2b
+#ifdef KR_headers
+(i) int i;
+#else
+(int i)
+#endif
+{
+ Bigint *b;
+
+ b = Balloc(1);
+ b->x[0] = i;
+ b->wds = 1;
+ return b;
+}
+
+static Bigint *
+mult
+#ifdef KR_headers
+(a, b) Bigint *a, *b;
+#else
+(Bigint *a, Bigint *b)
+#endif
+{
+ Bigint *c;
+ int k, wa, wb, wc;
+ ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0;
+ ULong y;
+#ifdef ULLong
+ ULLong carry, z;
+#else
+ ULong carry, z;
+#ifdef Pack_32
+ ULong z2;
+#endif
+#endif
+
+ if (a->wds < b->wds) {
+ c = a;
+ a = b;
+ b = c;
+ }
+ k = a->k;
+ wa = a->wds;
+ wb = b->wds;
+ wc = wa + wb;
+ if (wc > a->maxwds) {
+ k++;
+ }
+ c = Balloc(k);
+ for(x = c->x, xa = x + wc; x < xa; x++) {
+ *x = 0;
+ }
+ xa = a->x;
+ xae = xa + wa;
+ xb = b->x;
+ xbe = xb + wb;
+ xc0 = c->x;
+#ifdef ULLong
+ for(; xb < xbe; xc0++) {
+ if (y = *xb++) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ do {
+ z = *x++ * (ULLong)y + *xc + carry;
+ carry = z >> 32;
+ *xc++ = z & FFFFFFFF;
+ }
+ while(x < xae);
+ *xc = carry;
+ }
+ }
+#else
+#ifdef Pack_32
+ for(; xb < xbe; xb++, xc0++) {
+ if (y = *xb & 0xffff) {
+ 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) {
+ 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
+#endif
+ for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ;
+ c->wds = wc;
+ return c;
+}
+
+static Bigint *p5s;
+
+static Bigint *
+pow5mult
+#ifdef KR_headers
+(b, k) Bigint *b; int k;
+#else
+(Bigint *b, int k)
+#endif
+{
+ Bigint *b1, *p5, *p51;
+ int i;
+ static int p05[3] = { 5, 25, 125 };
+
+ if (i = k & 3) {
+ b = multadd(b, p05[i-1], 0);
+ }
+
+ if (!(k >>= 2)) {
+ return b;
+ }
+ if (!(p5 = p5s)) {
+ /* first time */
+#ifdef MULTIPLE_THREADS
+ ACQUIRE_DTOA_LOCK(1);
+ if (!(p5 = p5s)) {
+ p5 = p5s = i2b(625);
+ p5->next = 0;
+ }
+ FREE_DTOA_LOCK(1);
+#else
+ p5 = p5s = i2b(625);
+ p5->next = 0;
+#endif
+ }
+ for(;;) {
+ if (k & 1) {
+ b1 = mult(b, p5);
+ Bfree(b);
+ b = b1;
+ }
+ if (!(k >>= 1)) {
+ break;
+ }
+ if (!(p51 = p5->next)) {
+#ifdef MULTIPLE_THREADS
+ ACQUIRE_DTOA_LOCK(1);
+ if (!(p51 = p5->next)) {
+ p51 = p5->next = mult(p5,p5);
+ p51->next = 0;
+ }
+ FREE_DTOA_LOCK(1);
+#else
+ p51 = p5->next = mult(p5,p5);
+ p51->next = 0;
+#endif
+ }
+ p5 = p51;
+ }
+ return b;
+}
+
+static Bigint *
+lshift
+#ifdef KR_headers
+(b, k) Bigint *b; int k;
+#else
+(Bigint *b, int k)
+#endif
+{
+ int i, k1, n, n1;
+ Bigint *b1;
+ ULong *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) {
+ ++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) {
+ ++n1;
+ }
+ }
+#endif
+ else do {
+ *x1++ = *x++;
+ }
+ while(x < xe);
+ b1->wds = n1 - 1;
+ Bfree(b);
+ return b1;
+}
+
+static int
+cmp
+#ifdef KR_headers
+(a, b) Bigint *a, *b;
+#else
+(Bigint *a, Bigint *b)
+#endif
+{
+ ULong *xa, *xa0, *xb, *xb0;
+ int i, j;
+
+ i = a->wds;
+ j = b->wds;
+#ifdef DEBUG
+ 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
+#ifdef KR_headers
+(a, b) Bigint *a, *b;
+#else
+(Bigint *a, Bigint *b)
+#endif
+{
+ Bigint *c;
+ int i, wa, wb;
+ ULong *xa, *xae, *xb, *xbe, *xc;
+#ifdef ULLong
+ ULLong borrow, y;
+#else
+ ULong borrow, y;
+#ifdef Pack_32
+ ULong z;
+#endif
+#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 ULLong
+ do {
+ y = (ULLong)*xa++ - *xb++ - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *xc++ = y & FFFFFFFF;
+ }
+ while(xb < xbe);
+ while(xa < xae) {
+ y = *xa++ - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *xc++ = y & FFFFFFFF;
+ }
+#else
+#ifdef Pack_32
+ do {
+ y = (*xa & 0xffff) - (*xb & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*xa++ >> 16) - (*xb++ >> 16) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(xc, z, y);
+ }
+ while(xb < xbe);
+ while(xa < xae) {
+ y = (*xa & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*xa++ >> 16) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(xc, z, y);
+ }
+#else
+ do {
+ y = *xa++ - *xb++ - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *xc++ = y & 0xffff;
+ }
+ while(xb < xbe);
+ while(xa < xae) {
+ y = *xa++ - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *xc++ = y & 0xffff;
+ }
+#endif
+#endif
+ while(!*--xc) {
+ wa--;
+ }
+ c->wds = wa;
+ return c;
+}
+
+static double
+ulp
+#ifdef KR_headers
+(dx) double dx;
+#else
+(double dx)
+#endif
+{
+ register Long L;
+ U x, a;
+
+ dval(x) = dx;
+ L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1;
+#ifndef Avoid_Underflow
+#ifndef Sudden_Underflow
+ if (L > 0) {
+#endif
+#endif
+#ifdef IBM
+ L |= Exp_msk1 >> 4;
+#endif
+ word0(a) = L;
+ word1(a) = 0;
+#ifndef Avoid_Underflow
+#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
+#endif
+ return dval(a);
+}
+
+static double
+b2d
+#ifdef KR_headers
+(a, e) Bigint *a; int *e;
+#else
+(Bigint *a, int *e)
+#endif
+{
+ ULong *xa, *xa0, w, y, z;
+ int k;
+ U d;
+#ifdef VAX
+ ULong d0, d1;
+#else
+#define d0 word0(d)
+#define d1 word1(d)
+#endif
+
+ xa0 = a->x;
+ xa = xa0 + a->wds;
+ y = *--xa;
+#ifdef DEBUG
+ 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 dval(d);
+}
+
+static Bigint *
+d2b
+#ifdef KR_headers
+(dd, e, bits) double dd; int *e, *bits;
+#else
+(double dd, int *e, int *bits)
+#endif
+{
+ U d;
+ Bigint *b;
+ int de, k;
+ ULong *x, y, z;
+#ifndef Sudden_Underflow
+ int i;
+#endif
+#ifdef VAX
+ ULong d0, d1;
+#endif
+
+ dval(d) = dd;
+#ifdef VAX
+ 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 = (int)(d0 >> Exp_shift);
+#ifndef IBM
+ z |= Exp_msk11;
+#endif
+#else
+ if (de = (int)(d0 >> Exp_shift)) {
+ z |= Exp_msk1;
+ }
+#endif
+#ifdef Pack_32
+ if (y = d1) {
+ if (k = lo0bits(&y)) {
+ x[0] = y | z << 32 - k;
+ z >>= k;
+ }
+ else {
+ x[0] = y;
+ }
+#ifndef Sudden_Underflow
+ i =
+#endif
+ b->wds = (x[1] = z) ? 2 : 1;
+ }
+ else {
+ k = lo0bits(&z);
+ x[0] = z;
+#ifndef Sudden_Underflow
+ i =
+#endif
+ b->wds = 1;
+ k += 32;
+ }
+#else
+ if (y = d1) {
+ if (k = lo0bits(&y))
+ 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
+ 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
+{
+ U da, db;
+ int k, ka, kb;
+
+ dval(da) = b2d(a, &ka);
+ dval(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) {
+ dval(da) *= 1 << k;
+ }
+ }
+ else {
+ k = -k;
+ word0(db) += (k >> 2)*Exp_msk1;
+ if (k &= 3) {
+ dval(db) *= 1 << k;
+ }
+ }
+#else
+ if (k > 0) {
+ word0(da) += k*Exp_msk1;
+ }
+ else {
+ k = -k;
+ word0(db) += k*Exp_msk1;
+ }
+#endif
+ return dval(da) / dval(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,
+#ifdef Avoid_Underflow
+ 9007199254740992.*9007199254740992.e-256
+ /* = 2^106 * 1e-53 */
+#else
+ 1e-256
+#endif
+ };
+/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */
+/* flag unnecessarily. It leads to a song and dance at the end of strtod. */
+#define Scale_Bit 0x10
+#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
+
+#ifndef IEEE_Arith
+#undef INFNAN_CHECK
+#endif
+
+#ifdef INFNAN_CHECK
+
+#ifndef NAN_WORD0
+#define NAN_WORD0 0x7ff80000
+#endif
+
+#ifndef NAN_WORD1
+#define NAN_WORD1 0
+#endif
+
+static int
+match
+#ifdef KR_headers
+(sp, t) char **sp, *t;
+#else
+(CONST char **sp, char *t)
+#endif
+{
+ int c, d;
+ CONST char *s = *sp;
+
+ while(d = *t++) {
+ if ((c = *++s) >= 'A' && c <= 'Z') {
+ c += 'a' - 'A';
+ }
+ if (c != d) {
+ return 0;
+ }
+ }
+ *sp = s + 1;
+ return 1;
+}
+
+#ifndef No_Hex_NaN
+static void
+hexnan
+#ifdef KR_headers
+(rvp, sp) double *rvp; CONST char **sp;
+#else
+(double *rvp, CONST char **sp)
+#endif
+{
+ ULong c, x[2];
+ CONST char *s;
+ int havedig, udx0, xshift;
+
+ x[0] = x[1] = 0;
+ havedig = xshift = 0;
+ udx0 = 1;
+ s = *sp;
+ while(c = *(CONST unsigned char*)++s) {
+ if (c >= '0' && c <= '9') {
+ c -= '0';
+ }
+ else if (c >= 'a' && c <= 'f') {
+ c += 10 - 'a';
+ }
+ else if (c >= 'A' && c <= 'F') {
+ c += 10 - 'A';
+ }
+ else if (c <= ' ') {
+ if (udx0 && havedig) {
+ udx0 = 0;
+ xshift = 1;
+ }
+ continue;
+ }
+ else if (/*(*/ c == ')' && havedig) {
+ *sp = s + 1;
+ break;
+ }
+ else {
+ return; /* invalid form: don't change *sp */
+ }
+ havedig = 1;
+ if (xshift) {
+ xshift = 0;
+ x[0] = x[1];
+ x[1] = 0;
+ }
+ if (udx0) {
+ x[0] = (x[0] << 4) | (x[1] >> 28);
+ }
+ x[1] = (x[1] << 4) | c;
+ }
+ if ((x[0] &= 0xfffff) || x[1]) {
+ word0(*rvp) = Exp_mask | x[0];
+ word1(*rvp) = x[1];
+ }
+}
+#endif /*No_Hex_NaN*/
+#endif /* INFNAN_CHECK */
+
+PR_IMPLEMENT(double)
+PR_strtod
+#ifdef KR_headers
+(s00, se) CONST char *s00; char **se;
+#else
+(CONST char *s00, char **se)
+#endif
+{
+#ifdef Avoid_Underflow
+ int scale;
+#endif
+ int 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;
+ double aadj, aadj1, adj;
+ U aadj2, rv, rv0;
+ Long L;
+ ULong y, z;
+ Bigint *bb, *bb1, *bd, *bd0, *bs, *delta;
+#ifdef SET_INEXACT
+ int inexact, oldinexact;
+#endif
+#ifdef Honor_FLT_ROUNDS
+ int rounding;
+#endif
+#ifdef USE_LOCALE
+ CONST char *s2;
+#endif
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ sign = nz0 = nz = 0;
+ dval(rv) = 0.;
+ for(s = s00;; s++) switch(*s) {
+ case '-':
+ sign = 1;
+ /* no break */
+ case '+':
+ if (*++s) {
+ goto break2;
+ }
+ /* no break */
+ case 0:
+ goto ret0;
+ 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;
+#ifdef USE_LOCALE
+ s1 = localeconv()->decimal_point;
+ if (c == *s1) {
+ c = '.';
+ if (*++s1) {
+ s2 = s;
+ for(;;) {
+ if (*++s2 != *s1) {
+ c = 0;
+ break;
+ }
+ if (!*++s1) {
+ s = s2;
+ break;
+ }
+ }
+ }
+ }
+#endif
+ 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:
+ if (nd > 64 * 1024) {
+ goto ret0;
+ }
+ e = 0;
+ if (c == 'e' || c == 'E') {
+ if (!nd && !nz && !nz0) {
+ goto ret0;
+ }
+ 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 = (int)L;
+ }
+ if (esign) {
+ e = -e;
+ }
+ }
+ else {
+ e = 0;
+ }
+ }
+ else {
+ s = s00;
+ }
+ }
+ if (!nd) {
+ if (!nz && !nz0) {
+#ifdef INFNAN_CHECK
+ /* Check for Nan and Infinity */
+ switch(c) {
+ case 'i':
+ case 'I':
+ if (match(&s,"nf")) {
+ --s;
+ if (!match(&s,"inity")) {
+ ++s;
+ }
+ word0(rv) = 0x7ff00000;
+ word1(rv) = 0;
+ goto ret;
+ }
+ break;
+ case 'n':
+ case 'N':
+ if (match(&s, "an")) {
+ word0(rv) = NAN_WORD0;
+ word1(rv) = NAN_WORD1;
+#ifndef No_Hex_NaN
+ if (*s == '(') { /*)*/
+ hexnan(&rv, &s);
+ }
+#endif
+ goto ret;
+ }
+ }
+#endif /* INFNAN_CHECK */
+ret0:
+ s = s00;
+ sign = 0;
+ }
+ 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;
+ dval(rv) = y;
+ if (k > 9) {
+#ifdef SET_INEXACT
+ if (k > DBL_DIG) {
+ oldinexact = get_inexact();
+ }
+#endif
+ dval(rv) = tens[k - 9] * dval(rv) + z;
+ }
+ bd0 = 0;
+ if (nd <= DBL_DIG
+#ifndef RND_PRODQUOT
+#ifndef Honor_FLT_ROUNDS
+ && Flt_Rounds == 1
+#endif
+#endif
+ ) {
+ if (!e) {
+ goto ret;
+ }
+ if (e > 0) {
+ if (e <= Ten_pmax) {
+#ifdef VAX
+ goto vax_ovfl_check;
+#else
+#ifdef Honor_FLT_ROUNDS
+ /* round correctly FLT_ROUNDS = 2 or 3 */
+ if (sign) {
+ rv = -rv;
+ sign = 0;
+ }
+#endif
+ /* rv = */ rounded_product(dval(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.
+ */
+#ifdef Honor_FLT_ROUNDS
+ /* round correctly FLT_ROUNDS = 2 or 3 */
+ if (sign) {
+ rv = -rv;
+ sign = 0;
+ }
+#endif
+ e -= i;
+ dval(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(dval(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(dval(rv), tens[e]);
+#endif
+ goto ret;
+ }
+ }
+#ifndef Inaccurate_Divide
+ else if (e >= -Ten_pmax) {
+#ifdef Honor_FLT_ROUNDS
+ /* round correctly FLT_ROUNDS = 2 or 3 */
+ if (sign) {
+ rv = -rv;
+ sign = 0;
+ }
+#endif
+ /* rv = */ rounded_quotient(dval(rv), tens[-e]);
+ goto ret;
+ }
+#endif
+ }
+ e1 += nd - k;
+
+#ifdef IEEE_Arith
+#ifdef SET_INEXACT
+ inexact = 1;
+ if (k <= DBL_DIG) {
+ oldinexact = get_inexact();
+ }
+#endif
+#ifdef Avoid_Underflow
+ scale = 0;
+#endif
+#ifdef Honor_FLT_ROUNDS
+ if ((rounding = Flt_Rounds) >= 2) {
+ if (sign) {
+ rounding = rounding == 2 ? 0 : 2;
+ }
+ else if (rounding != 2) {
+ rounding = 0;
+ }
+ }
+#endif
+#endif /*IEEE_Arith*/
+
+ /* Get starting approximation = rv * 10**e1 */
+
+ if (e1 > 0) {
+ if (i = e1 & 15) {
+ dval(rv) *= tens[i];
+ }
+ if (e1 &= ~15) {
+ if (e1 > DBL_MAX_10_EXP) {
+ovfl:
+#ifndef NO_ERRNO
+ PR_SetError(PR_RANGE_ERROR, 0);
+#endif
+ /* Can't trust HUGE_VAL */
+#ifdef IEEE_Arith
+#ifdef Honor_FLT_ROUNDS
+ switch(rounding) {
+ case 0: /* toward 0 */
+ case 3: /* toward -infinity */
+ word0(rv) = Big0;
+ word1(rv) = Big1;
+ break;
+ default:
+ word0(rv) = Exp_mask;
+ word1(rv) = 0;
+ }
+#else /*Honor_FLT_ROUNDS*/
+ word0(rv) = Exp_mask;
+ word1(rv) = 0;
+#endif /*Honor_FLT_ROUNDS*/
+#ifdef SET_INEXACT
+ /* set overflow bit */
+ dval(rv0) = 1e300;
+ dval(rv0) *= dval(rv0);
+#endif
+#else /*IEEE_Arith*/
+ word0(rv) = Big0;
+ word1(rv) = Big1;
+#endif /*IEEE_Arith*/
+ if (bd0) {
+ goto retfree;
+ }
+ goto ret;
+ }
+ e1 >>= 4;
+ for(j = 0; e1 > 1; j++, e1 >>= 1)
+ if (e1 & 1) {
+ dval(rv) *= bigtens[j];
+ }
+ /* The last multiplication could overflow. */
+ word0(rv) -= P*Exp_msk1;
+ dval(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) {
+ dval(rv) /= tens[i];
+ }
+ if (e1 >>= 4) {
+ if (e1 >= 1 << n_bigtens) {
+ goto undfl;
+ }
+#ifdef Avoid_Underflow
+ if (e1 & Scale_Bit) {
+ scale = 2*P;
+ }
+ for(j = 0; e1 > 0; j++, e1 >>= 1)
+ if (e1 & 1) {
+ dval(rv) *= tinytens[j];
+ }
+ if (scale && (j = 2*P + 1 - ((word0(rv) & Exp_mask)
+ >> Exp_shift)) > 0) {
+ /* scaled rv is denormal; zap j low bits */
+ if (j >= 32) {
+ word1(rv) = 0;
+ if (j >= 53) {
+ word0(rv) = (P+2)*Exp_msk1;
+ }
+ else {
+ word0(rv) &= 0xffffffff << j-32;
+ }
+ }
+ else {
+ word1(rv) &= 0xffffffff << j;
+ }
+ }
+#else
+ for(j = 0; e1 > 1; j++, e1 >>= 1)
+ if (e1 & 1) {
+ dval(rv) *= tinytens[j];
+ }
+ /* The last multiplication could underflow. */
+ dval(rv0) = dval(rv);
+ dval(rv) *= tinytens[j];
+ if (!dval(rv)) {
+ dval(rv) = 2.*dval(rv0);
+ dval(rv) *= tinytens[j];
+#endif
+ if (!dval(rv)) {
+undfl:
+ dval(rv) = 0.;
+#ifndef NO_ERRNO
+ PR_SetError(PR_RANGE_ERROR, 0);
+#endif
+ if (bd0) {
+ goto retfree;
+ }
+ goto ret;
+ }
+#ifndef Avoid_Underflow
+ word0(rv) = Tiny0;
+ word1(rv) = Tiny1;
+ /* The refinement below will clean
+ * this approximation up.
+ */
+ }
+#endif
+ }
+}
+
+/* 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(dval(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 Honor_FLT_ROUNDS
+ if (rounding != 1) {
+ bs2++;
+ }
+#endif
+#ifdef Avoid_Underflow
+ j = bbe - scale;
+ i = j + bbbits - 1; /* logb(rv) */
+ if (i < Emin) { /* denormal */
+ j += P - Emin;
+ }
+ else {
+ j = P + 1 - bbbits;
+ }
+#else /*Avoid_Underflow*/
+#ifdef Sudden_Underflow
+#ifdef IBM
+ j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3);
+#else
+ j = P + 1 - bbbits;
+#endif
+#else /*Sudden_Underflow*/
+ j = bbe;
+ i = j + bbbits - 1; /* logb(rv) */
+ if (i < Emin) { /* denormal */
+ j += P - Emin;
+ }
+ else {
+ j = P + 1 - bbbits;
+ }
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow*/
+ bb2 += j;
+ bd2 += j;
+#ifdef Avoid_Underflow
+ bd2 += scale;
+#endif
+ 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);
+#ifdef Honor_FLT_ROUNDS
+ if (rounding != 1) {
+ if (i < 0) {
+ /* Error is less than an ulp */
+ if (!delta->x[0] && delta->wds <= 1) {
+ /* exact */
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ break;
+ }
+ if (rounding) {
+ if (dsign) {
+ adj = 1.;
+ goto apply_adj;
+ }
+ }
+ else if (!dsign) {
+ adj = -1.;
+ if (!word1(rv)
+ && !(word0(rv) & Frac_mask)) {
+ y = word0(rv) & Exp_mask;
+#ifdef Avoid_Underflow
+ if (!scale || y > 2*P*Exp_msk1)
+#else
+ if (y)
+#endif
+ {
+ delta = lshift(delta,Log2P);
+ if (cmp(delta, bs) <= 0) {
+ adj = -0.5;
+ }
+ }
+ }
+apply_adj:
+#ifdef Avoid_Underflow
+ if (scale && (y = word0(rv) & Exp_mask)
+ <= 2*P*Exp_msk1) {
+ word0(adj) += (2*P+1)*Exp_msk1 - y;
+ }
+#else
+#ifdef Sudden_Underflow
+ if ((word0(rv) & Exp_mask) <=
+ P*Exp_msk1) {
+ word0(rv) += P*Exp_msk1;
+ dval(rv) += adj*ulp(dval(rv));
+ word0(rv) -= P*Exp_msk1;
+ }
+ else
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow*/
+ dval(rv) += adj*ulp(dval(rv));
+ }
+ break;
+ }
+ adj = ratio(delta, bs);
+ if (adj < 1.) {
+ adj = 1.;
+ }
+ if (adj <= 0x7ffffffe) {
+ /* adj = rounding ? ceil(adj) : floor(adj); */
+ y = adj;
+ if (y != adj) {
+ if (!((rounding>>1) ^ dsign)) {
+ y++;
+ }
+ adj = y;
+ }
+ }
+#ifdef Avoid_Underflow
+ if (scale && (y = word0(rv) & Exp_mask) <= 2*P*Exp_msk1) {
+ word0(adj) += (2*P+1)*Exp_msk1 - y;
+ }
+#else
+#ifdef Sudden_Underflow
+ if ((word0(rv) & Exp_mask) <= P*Exp_msk1) {
+ word0(rv) += P*Exp_msk1;
+ adj *= ulp(dval(rv));
+ if (dsign) {
+ dval(rv) += adj;
+ }
+ else {
+ dval(rv) -= adj;
+ }
+ word0(rv) -= P*Exp_msk1;
+ goto cont;
+ }
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow*/
+ adj *= ulp(dval(rv));
+ if (dsign) {
+ dval(rv) += adj;
+ }
+ else {
+ dval(rv) -= adj;
+ }
+ goto cont;
+ }
+#endif /*Honor_FLT_ROUNDS*/
+
+ 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
+#ifdef IEEE_Arith
+#ifdef Avoid_Underflow
+ || (word0(rv) & Exp_mask) <= (2*P+1)*Exp_msk1
+#else
+ || (word0(rv) & Exp_mask) <= Exp_msk1
+#endif
+#endif
+ ) {
+#ifdef SET_INEXACT
+ if (!delta->x[0] && delta->wds <= 1) {
+ inexact = 0;
+ }
+#endif
+ break;
+ }
+ if (!delta->x[0] && delta->wds <= 1) {
+ /* exact result */
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ 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) == (
+#ifdef Avoid_Underflow
+ (scale && (y = word0(rv) & Exp_mask) <= 2*P*Exp_msk1)
+ ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) :
+#endif
+ 0xffffffff)) {
+ /*boundary case -- increment exponent*/
+ word0(rv) = (word0(rv) & Exp_mask)
+ + Exp_msk1
+#ifdef IBM
+ | Exp_msk1 >> 4
+#endif
+ ;
+ word1(rv) = 0;
+#ifdef Avoid_Underflow
+ dsign = 0;
+#endif
+ 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
+#ifdef Avoid_Underflow
+ if (L <= (scale ? (2*P+1)*Exp_msk1 : Exp_msk1))
+#else
+ if (L <= Exp_msk1)
+#endif /*Avoid_Underflow*/
+#endif /*IBM*/
+ goto undfl;
+ L -= Exp_msk1;
+#else /*Sudden_Underflow}{*/
+#ifdef Avoid_Underflow
+ if (scale) {
+ L = word0(rv) & Exp_mask;
+ if (L <= (2*P+1)*Exp_msk1) {
+ if (L > (P+2)*Exp_msk1)
+ /* round even ==> */
+ /* accept rv */
+ {
+ break;
+ }
+ /* rv = smallest denormal */
+ goto undfl;
+ }
+ }
+#endif /*Avoid_Underflow*/
+ L = (word0(rv) & Exp_mask) - Exp_msk1;
+#endif /*Sudden_Underflow}}*/
+ 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) {
+ dval(rv) += ulp(dval(rv));
+ }
+#ifndef ROUND_BIASED
+ else {
+ dval(rv) -= ulp(dval(rv));
+#ifndef Sudden_Underflow
+ if (!dval(rv)) {
+ goto undfl;
+ }
+#endif
+ }
+#ifdef Avoid_Underflow
+ dsign = 1 - dsign;
+#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(Rounding) {
+ 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 /*Check_FLT_ROUNDS*/
+ }
+ y = word0(rv) & Exp_mask;
+
+ /* Check for overflow */
+
+ if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) {
+ dval(rv0) = dval(rv);
+ word0(rv) -= P*Exp_msk1;
+ adj = aadj1 * ulp(dval(rv));
+ dval(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 Avoid_Underflow
+ if (scale && y <= 2*P*Exp_msk1) {
+ if (aadj <= 0x7fffffff) {
+ if ((z = aadj) <= 0) {
+ z = 1;
+ }
+ aadj = z;
+ aadj1 = dsign ? aadj : -aadj;
+ }
+ dval(aadj2) = aadj1;
+ word0(aadj2) += (2*P+1)*Exp_msk1 - y;
+ aadj1 = dval(aadj2);
+ }
+ adj = aadj1 * ulp(dval(rv));
+ dval(rv) += adj;
+#else
+#ifdef Sudden_Underflow
+ if ((word0(rv) & Exp_mask) <= P*Exp_msk1) {
+ dval(rv0) = dval(rv);
+ word0(rv) += P*Exp_msk1;
+ adj = aadj1 * ulp(dval(rv));
+ dval(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(dval(rv));
+ dval(rv) += adj;
+ }
+#else /*Sudden_Underflow*/
+ /* 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)(int)(aadj + 0.5);
+ if (!dsign) {
+ aadj1 = -aadj1;
+ }
+ }
+ adj = aadj1 * ulp(dval(rv));
+ dval(rv) += adj;
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow*/
+ }
+ z = word0(rv) & Exp_mask;
+#ifndef SET_INEXACT
+#ifdef Avoid_Underflow
+ if (!scale)
+#endif
+ 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;
+ }
+ }
+#endif
+cont:
+ Bfree(bb);
+ Bfree(bd);
+ Bfree(bs);
+ Bfree(delta);
+}
+#ifdef SET_INEXACT
+if (inexact) {
+ if (!oldinexact) {
+ word0(rv0) = Exp_1 + (70 << Exp_shift);
+ word1(rv0) = 0;
+ dval(rv0) += 1.;
+ }
+}
+else if (!oldinexact) {
+ clear_inexact();
+}
+#endif
+#ifdef Avoid_Underflow
+if (scale) {
+ word0(rv0) = Exp_1 - 2*P*Exp_msk1;
+ word1(rv0) = 0;
+ dval(rv) *= dval(rv0);
+#ifndef NO_ERRNO
+ /* try to avoid the bug of testing an 8087 register value */
+ if (word0(rv) == 0 && word1(rv) == 0) {
+ PR_SetError(PR_RANGE_ERROR, 0);
+ }
+#endif
+}
+#endif /* Avoid_Underflow */
+#ifdef SET_INEXACT
+if (inexact && !(word0(rv) & Exp_mask)) {
+ /* set underflow bit */
+ dval(rv0) = 1e-300;
+ dval(rv0) *= dval(rv0);
+}
+#endif
+retfree:
+Bfree(bb);
+Bfree(bd);
+Bfree(bs);
+Bfree(bd0);
+Bfree(delta);
+ret:
+if (se) {
+ *se = (char *)s;
+}
+return sign ? -dval(rv) : dval(rv);
+}
+
+static int
+quorem
+#ifdef KR_headers
+(b, S) Bigint *b, *S;
+#else
+(Bigint *b, Bigint *S)
+#endif
+{
+ int n;
+ ULong *bx, *bxe, q, *sx, *sxe;
+#ifdef ULLong
+ ULLong borrow, carry, y, ys;
+#else
+ ULong borrow, carry, y, ys;
+#ifdef Pack_32
+ ULong si, z, zs;
+#endif
+#endif
+
+ n = S->wds;
+#ifdef DEBUG
+ /*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
+ /*debug*/ if (q > 9)
+ /*debug*/{
+ Bug("oversized quotient in quorem");
+ }
+#endif
+ if (q) {
+ borrow = 0;
+ carry = 0;
+ do {
+#ifdef ULLong
+ ys = *sx++ * (ULLong)q + carry;
+ carry = ys >> 32;
+ y = *bx - (ys & FFFFFFFF) - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *bx++ = y & FFFFFFFF;
+#else
+#ifdef Pack_32
+ si = *sx++;
+ ys = (si & 0xffff) * q + carry;
+ zs = (si >> 16) * q + (ys >> 16);
+ carry = zs >> 16;
+ y = (*bx & 0xffff) - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*bx >> 16) - (zs & 0xffff) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(bx, z, y);
+#else
+ ys = *sx++ * q + carry;
+ carry = ys >> 16;
+ y = *bx - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *bx++ = y & 0xffff;
+#endif
+#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 ULLong
+ ys = *sx++ + carry;
+ carry = ys >> 32;
+ y = *bx - (ys & FFFFFFFF) - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *bx++ = y & FFFFFFFF;
+#else
+#ifdef Pack_32
+ si = *sx++;
+ ys = (si & 0xffff) + carry;
+ zs = (si >> 16) + (ys >> 16);
+ carry = zs >> 16;
+ y = (*bx & 0xffff) - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*bx >> 16) - (zs & 0xffff) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(bx, z, y);
+#else
+ ys = *sx++ + carry;
+ carry = ys >> 16;
+ y = *bx - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *bx++ = y & 0xffff;
+#endif
+#endif
+ }
+ while(sx <= sxe);
+ bx = b->x;
+ bxe = bx + n;
+ if (!*bxe) {
+ while(--bxe > bx && !*bxe) {
+ --n;
+ }
+ b->wds = n;
+ }
+ }
+ return q;
+}
+
+#ifndef MULTIPLE_THREADS
+static char *dtoa_result;
+#endif
+
+static char *
+#ifdef KR_headers
+rv_alloc(i) int i;
+#else
+rv_alloc(int i)
+#endif
+{
+ int j, k, *r;
+
+ j = sizeof(ULong);
+ for(k = 0;
+ sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= i;
+ j <<= 1) {
+ k++;
+ }
+ r = (int*)Balloc(k);
+ *r = k;
+ return
+#ifndef MULTIPLE_THREADS
+ dtoa_result =
+#endif
+ (char *)(r+1);
+}
+
+static char *
+#ifdef KR_headers
+nrv_alloc(s, rve, n) char *s, **rve; int n;
+#else
+nrv_alloc(char *s, char **rve, int n)
+#endif
+{
+ char *rv, *t;
+
+ t = rv = rv_alloc(n);
+ while(*t = *s++) {
+ t++;
+ }
+ if (rve) {
+ *rve = t;
+ }
+ return rv;
+}
+
+/* freedtoa(s) must be used to free values s returned by dtoa
+ * when MULTIPLE_THREADS is #defined. It should be used in all cases,
+ * but for consistency with earlier versions of dtoa, it is optional
+ * when MULTIPLE_THREADS is not defined.
+ */
+
+static void
+#ifdef KR_headers
+freedtoa(s) char *s;
+#else
+freedtoa(char *s)
+#endif
+{
+ Bigint *b = (Bigint *)((int *)s - 1);
+ b->maxwds = 1 << (b->k = *(int*)b);
+ Bfree(b);
+#ifndef MULTIPLE_THREADS
+ if (s == dtoa_result) {
+ dtoa_result = 0;
+ }
+#endif
+}
+
+/* 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. 112-126].
+ *
+ * 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.
+ */
+
+static char *
+dtoa
+#ifdef KR_headers
+(dd, mode, ndigits, decpt, sign, rve)
+double dd; int mode, ndigits, *decpt, *sign; char **rve;
+#else
+(double dd, int mode, int ndigits, int *decpt, int *sign, char **rve)
+#endif
+{
+ /* 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,5 ==> similar to 2 and 3, respectively, but (in
+ round-nearest mode) with the tests of mode 0 to
+ possibly return a shorter string that rounds to d.
+ With IEEE arithmetic and compilation with
+ -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same
+ as modes 2 and 3 when FLT_ROUNDS != 1.
+ 6-9 ==> Debugging modes similar to mode - 4: 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.
+ */
+
+ int 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
+ int denorm;
+ ULong x;
+#endif
+ Bigint *b, *b1, *delta, *mlo, *mhi, *S;
+ U d, d2, eps;
+ double ds;
+ char *s, *s0;
+#ifdef Honor_FLT_ROUNDS
+ int rounding;
+#endif
+#ifdef SET_INEXACT
+ int inexact, oldinexact;
+#endif
+
+#ifndef MULTIPLE_THREADS
+ if (dtoa_result) {
+ freedtoa(dtoa_result);
+ dtoa_result = 0;
+ }
+#endif
+
+ dval(d) = dd;
+ 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;
+#ifdef IEEE_Arith
+ if (!word1(d) && !(word0(d) & 0xfffff)) {
+ return nrv_alloc("Infinity", rve, 8);
+ }
+#endif
+ return nrv_alloc("NaN", rve, 3);
+ }
+#endif
+#ifdef IBM
+ dval(d) += 0; /* normalize */
+#endif
+ if (!dval(d)) {
+ *decpt = 1;
+ return nrv_alloc("0", rve, 1);
+ }
+
+#ifdef SET_INEXACT
+ try_quick = oldinexact = get_inexact();
+ inexact = 1;
+#endif
+#ifdef Honor_FLT_ROUNDS
+ if ((rounding = Flt_Rounds) >= 2) {
+ if (*sign) {
+ rounding = rounding == 2 ? 0 : 2;
+ }
+ else if (rounding != 2) {
+ rounding = 0;
+ }
+ }
+#endif
+
+ b = d2b(dval(d), &be, &bbits);
+#ifdef Sudden_Underflow
+ i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1));
+#else
+ if (i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1))) {
+#endif
+ dval(d2) = dval(d);
+ word0(d2) &= Frac_mask1;
+ word0(d2) |= Exp_11;
+#ifdef IBM
+ if (j = 11 - hi0bits(word0(d2) & Frac_mask)) {
+ dval(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;
+ dval(d2) = x;
+ word0(d2) -= 31*Exp_msk1; /* adjust exponent */
+ i -= (Bias + (P-1) - 1) + 1;
+ denorm = 1;
+}
+#endif
+ds = (dval(d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981;
+k = (int)ds;
+if (ds < 0. && ds != k) {
+ k--; /* want k = floor(ds) */
+}
+k_check = 1;
+if (k >= 0 && k <= Ten_pmax) {
+ if (dval(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;
+}
+
+#ifndef SET_INEXACT
+#ifdef Check_FLT_ROUNDS
+try_quick = Rounding == 1;
+#else
+try_quick = 1;
+#endif
+#endif /*SET_INEXACT*/
+
+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;
+ }
+}
+s = s0 = rv_alloc(i);
+
+#ifdef Honor_FLT_ROUNDS
+if (mode > 1 && rounding != 1) {
+ leftright = 0;
+}
+#endif
+
+if (ilim >= 0 && ilim <= Quick_max && try_quick) {
+
+ /* Try to get by with floating-point arithmetic. */
+
+ i = 0;
+ dval(d2) = dval(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;
+ dval(d) /= bigtens[n_bigtens-1];
+ ieps++;
+ }
+ for(; j; j >>= 1, i++)
+ if (j & 1) {
+ ieps++;
+ ds *= bigtens[i];
+ }
+ dval(d) /= ds;
+ }
+ else if (j1 = -k) {
+ dval(d) *= tens[j1 & 0xf];
+ for(j = j1 >> 4; j; j >>= 1, i++)
+ if (j & 1) {
+ ieps++;
+ dval(d) *= bigtens[i];
+ }
+ }
+ if (k_check && dval(d) < 1. && ilim > 0) {
+ if (ilim1 <= 0) {
+ goto fast_failed;
+ }
+ ilim = ilim1;
+ k--;
+ dval(d) *= 10.;
+ ieps++;
+ }
+ dval(eps) = ieps*dval(d) + 7.;
+ word0(eps) -= (P-1)*Exp_msk1;
+ if (ilim == 0) {
+ S = mhi = 0;
+ dval(d) -= 5.;
+ if (dval(d) > dval(eps)) {
+ goto one_digit;
+ }
+ if (dval(d) < -dval(eps)) {
+ goto no_digits;
+ }
+ goto fast_failed;
+ }
+#ifndef No_leftright
+ if (leftright) {
+ /* Use Steele & White method of only
+ * generating digits needed.
+ */
+ dval(eps) = 0.5/tens[ilim-1] - dval(eps);
+ for(i = 0;;) {
+ L = dval(d);
+ dval(d) -= L;
+ *s++ = '0' + (int)L;
+ if (dval(d) < dval(eps)) {
+ goto ret1;
+ }
+ if (1. - dval(d) < dval(eps)) {
+ goto bump_up;
+ }
+ if (++i >= ilim) {
+ break;
+ }
+ dval(eps) *= 10.;
+ dval(d) *= 10.;
+ }
+ }
+ else {
+#endif
+ /* Generate ilim digits, then fix them up. */
+ dval(eps) *= tens[ilim-1];
+ for(i = 1;; i++, dval(d) *= 10.) {
+ L = (Long)(dval(d));
+ if (!(dval(d) -= L)) {
+ ilim = i;
+ }
+ *s++ = '0' + (int)L;
+ if (i == ilim) {
+ if (dval(d) > 0.5 + dval(eps)) {
+ goto bump_up;
+ }
+ else if (dval(d) < 0.5 - dval(eps)) {
+ while(*--s == '0');
+ s++;
+ goto ret1;
+ }
+ break;
+ }
+ }
+#ifndef No_leftright
+ }
+#endif
+fast_failed:
+ s = s0;
+ dval(d) = dval(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 || dval(d) <= 5*ds) {
+ goto no_digits;
+ }
+ goto one_digit;
+ }
+ for(i = 1; i <= k+1; i++, dval(d) *= 10.) {
+ L = (Long)(dval(d) / ds);
+ dval(d) -= L*ds;
+#ifdef Check_FLT_ROUNDS
+ /* If FLT_ROUNDS == 2, L will usually be high by 1 */
+ if (dval(d) < 0) {
+ L--;
+ dval(d) += ds;
+ }
+#endif
+ *s++ = '0' + (int)L;
+ if (!dval(d)) {
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ break;
+ }
+ if (i == ilim) {
+#ifdef Honor_FLT_ROUNDS
+ if (mode > 1)
+ switch(rounding) {
+ case 0: goto ret1;
+ case 2: goto bump_up;
+ }
+#endif
+ dval(d) += dval(d);
+ if (dval(d) > ds || dval(d) == ds && L & 1) {
+bump_up:
+ while(*--s == '9')
+ if (s == s0) {
+ k++;
+ *s = '0';
+ break;
+ }
+ ++*s++;
+ }
+ break;
+ }
+ }
+ goto ret1;
+}
+
+m2 = b2;
+m5 = b5;
+mhi = mlo = 0;
+if (leftright) {
+ 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
+ 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) {
+ 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. */
+
+spec_case = 0;
+if ((mode < 2 || leftright)
+#ifdef Honor_FLT_ROUNDS
+ && rounding == 1
+#endif
+ ) {
+ if (!word1(d) && !(word0(d) & Bndry_mask)
+#ifndef Sudden_Underflow
+ && word0(d) & (Exp_mask & ~Exp_msk1)
+#endif
+ ) {
+ /* The special case */
+ b2 += Log2P;
+ s2 += Log2P;
+ spec_case = 1;
+ }
+}
+
+/* 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) {
+ 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 == 3 || mode == 5)) {
+ 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 != 1 && !(word1(d) & 1)
+#ifdef Honor_FLT_ROUNDS
+ && rounding >= 1
+#endif
+ ) {
+ if (dig == '9') {
+ goto round_9_up;
+ }
+ if (j > 0) {
+ dig++;
+ }
+#ifdef SET_INEXACT
+ else if (!b->x[0] && b->wds <= 1) {
+ inexact = 0;
+ }
+#endif
+ *s++ = dig;
+ goto ret;
+ }
+#endif
+ if (j < 0 || j == 0 && mode != 1
+#ifndef ROUND_BIASED
+ && !(word1(d) & 1)
+#endif
+ ) {
+ if (!b->x[0] && b->wds <= 1) {
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ goto accept_dig;
+ }
+#ifdef Honor_FLT_ROUNDS
+ if (mode > 1)
+ switch(rounding) {
+ case 0: goto accept_dig;
+ case 2: goto keep_dig;
+ }
+#endif /*Honor_FLT_ROUNDS*/
+ if (j1 > 0) {
+ b = lshift(b, 1);
+ j1 = cmp(b, S);
+ if ((j1 > 0 || j1 == 0 && dig & 1)
+ && dig++ == '9') {
+ goto round_9_up;
+ }
+ }
+accept_dig:
+ *s++ = dig;
+ goto ret;
+ }
+ if (j1 > 0) {
+#ifdef Honor_FLT_ROUNDS
+ if (!rounding) {
+ goto accept_dig;
+ }
+#endif
+ if (dig == '9') { /* possible if i == 1 */
+round_9_up:
+ *s++ = '9';
+ goto roundoff;
+ }
+ *s++ = dig + 1;
+ goto ret;
+ }
+#ifdef Honor_FLT_ROUNDS
+keep_dig:
+#endif
+ *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 (!b->x[0] && b->wds <= 1) {
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ goto ret;
+ }
+ if (i >= ilim) {
+ break;
+ }
+ b = multadd(b, 10, 0);
+ }
+
+/* Round off last digit */
+
+#ifdef Honor_FLT_ROUNDS
+switch(rounding) {
+case 0: goto trimzeros;
+case 2: goto roundoff;
+}
+#endif
+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 {
+#ifdef Honor_FLT_ROUNDS
+trimzeros:
+#endif
+ while(*--s == '0');
+ s++;
+}
+ret:
+Bfree(S);
+if (mhi) {
+ if (mlo && mlo != mhi) {
+ Bfree(mlo);
+ }
+ Bfree(mhi);
+}
+ret1:
+#ifdef SET_INEXACT
+if (inexact) {
+ if (!oldinexact) {
+ word0(d) = Exp_1 + (70 << Exp_shift);
+ word1(d) = 0;
+ dval(d) += 1.;
+ }
+}
+else if (!oldinexact) {
+ clear_inexact();
+}
+#endif
+Bfree(b);
+*s = 0;
+*decpt = k + 1;
+if (rve) {
+ *rve = s;
+}
+return s0;
+}
+#ifdef __cplusplus
+}
+#endif
+
+PR_IMPLEMENT(PRStatus)
+PR_dtoa(PRFloat64 d, PRIntn mode, PRIntn ndigits,
+ PRIntn *decpt, PRIntn *sign, char **rve, char *buf, PRSize bufsize)
+{
+ char *result;
+ PRSize resultlen;
+ PRStatus rv = PR_FAILURE;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (mode < 0 || mode > 3) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return rv;
+ }
+ result = dtoa(d, mode, ndigits, decpt, sign, rve);
+ if (!result) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return rv;
+ }
+ resultlen = strlen(result)+1;
+ if (bufsize < resultlen) {
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
+ } else {
+ memcpy(buf, result, resultlen);
+ if (rve) {
+ *rve = buf + (*rve - result);
+ }
+ rv = PR_SUCCESS;
+ }
+ freedtoa(result);
+ return rv;
+}
+
+/*
+** 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 dfval)
+{
+ PRIntn decpt, sign, numdigits;
+ char *num, *nump;
+ char *bufp = buf;
+ char *endnum;
+ U fval;
+
+ dval(fval) = dfval;
+ /* 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(dval(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/nsprpub/pr/src/misc/prenv.c b/nsprpub/pr/src/misc/prenv.c
new file mode 100644
index 0000000000..b057a1c89a
--- /dev/null
+++ b/nsprpub/pr/src/misc/prenv.c
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <string.h>
+#include <stdlib.h>
+#include "primpl.h"
+#include "prmem.h"
+
+#if defined(XP_UNIX)
+#include <unistd.h>
+#if defined(DARWIN)
+#if defined(HAVE_CRT_EXTERNS_H)
+#include <crt_externs.h>
+#endif /* HAVE_CRT_EXTERNS_H */
+#else /* DARWIN */
+PR_IMPORT_DATA(char **) environ;
+#endif /* DARWIN */
+#endif /* XP_UNIX */
+
+#if !defined(HAVE_SECURE_GETENV) && defined(HAVE___SECURE_GETENV)
+#define secure_getenv __secure_getenv
+#define HAVE_SECURE_GETENV 1
+#endif
+
+/* Lock used to lock the environment */
+#if defined(_PR_NO_PREEMPT)
+#define _PR_NEW_LOCK_ENV()
+#define _PR_DELETE_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_DELETE_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_DELETE_LOCK_ENV() \
+ { if (_pr_envLock) { PR_DestroyLock(_pr_envLock); _pr_envLock = NULL; } }
+#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(void)
+{
+ _PR_NEW_LOCK_ENV();
+}
+
+void _PR_CleanupEnv(void)
+{
+ _PR_DELETE_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;
+}
+
+PR_IMPLEMENT(char*) PR_GetEnvSecure(const char *var)
+{
+#ifdef HAVE_SECURE_GETENV
+ char *ev;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ _PR_LOCK_ENV();
+ ev = secure_getenv(var);
+ _PR_UNLOCK_ENV();
+
+ return ev;
+#else
+#ifdef XP_UNIX
+ /*
+ ** Fall back to checking uids and gids. This won't detect any other
+ ** privilege-granting mechanisms the platform may have. This also
+ ** can't detect the case where the process already called
+ ** setuid(geteuid()) and/or setgid(getegid()).
+ */
+ if (getuid() != geteuid() || getgid() != getegid()) {
+ return NULL;
+ }
+#endif /* XP_UNIX */
+ return PR_GetEnv(var);
+#endif /* HAVE_SECURE_GETENV */
+}
+
+PR_IMPLEMENT(PRStatus) PR_SetEnv(const char *string)
+{
+ PRIntn result;
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (!strchr(string, '=')) {
+ return(PR_FAILURE);
+ }
+
+ _PR_LOCK_ENV();
+ result = _PR_MD_PUT_ENV((char*)string);
+ _PR_UNLOCK_ENV();
+ return result ? PR_FAILURE : PR_SUCCESS;
+}
+
+#if defined(XP_UNIX) && (!defined(DARWIN) || defined(HAVE_CRT_EXTERNS_H))
+PR_IMPLEMENT(char **) PR_DuplicateEnvironment(void)
+{
+ char **the_environ, **result, **end, **src, **dst;
+
+ _PR_LOCK_ENV();
+#ifdef DARWIN
+ the_environ = *(_NSGetEnviron());
+#else
+ the_environ = environ;
+#endif
+
+ for (end = the_environ; *end != NULL; end++)
+ /* empty loop body */;
+
+ result = (char **)PR_Malloc(sizeof(char *) * (end - the_environ + 1));
+ if (result != NULL) {
+ for (src = the_environ, dst = result; src != end; src++, dst++) {
+ size_t len;
+
+ len = strlen(*src) + 1;
+ *dst = PR_Malloc(len);
+ if (*dst == NULL) {
+ /* Allocation failed. Must clean up the half-copied env. */
+ char **to_delete;
+
+ for (to_delete = result; to_delete != dst; to_delete++) {
+ PR_Free(*to_delete);
+ }
+ PR_Free(result);
+ result = NULL;
+ goto out;
+ }
+ memcpy(*dst, *src, len);
+ }
+ *dst = NULL;
+ }
+out:
+ _PR_UNLOCK_ENV();
+ return result;
+}
+#else
+/* This platform doesn't support raw access to the environ block. */
+PR_IMPLEMENT(char **) PR_DuplicateEnvironment(void)
+{
+ return NULL;
+}
+#endif
diff --git a/nsprpub/pr/src/misc/prerr.c b/nsprpub/pr/src/misc/prerr.c
new file mode 100644
index 0000000000..99cf39f8c2
--- /dev/null
+++ b/nsprpub/pr/src/misc/prerr.c
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ *
+ * prerr.c
+ * This file is automatically generated; please do not edit it.
+ */
+#include "prerror.h"
+static const struct PRErrorMessage text[] = {
+ {"PR_OUT_OF_MEMORY_ERROR", "Memory allocation attempt failed"},
+ {"PR_BAD_DESCRIPTOR_ERROR", "Invalid file descriptor"},
+ {"PR_WOULD_BLOCK_ERROR", "The operation would have blocked"},
+ {"PR_ACCESS_FAULT_ERROR", "Invalid memory address argument"},
+ {"PR_INVALID_METHOD_ERROR", "Invalid function for file type"},
+ {"PR_ILLEGAL_ACCESS_ERROR", "Invalid memory address argument"},
+ {"PR_UNKNOWN_ERROR", "Some unknown error has occurred"},
+ {"PR_PENDING_INTERRUPT_ERROR", "Operation interrupted by another thread"},
+ {"PR_NOT_IMPLEMENTED_ERROR", "function not implemented"},
+ {"PR_IO_ERROR", "I/O function error"},
+ {"PR_IO_TIMEOUT_ERROR", "I/O operation timed out"},
+ {"PR_IO_PENDING_ERROR", "I/O operation on busy file descriptor"},
+ {"PR_DIRECTORY_OPEN_ERROR", "The directory could not be opened"},
+ {"PR_INVALID_ARGUMENT_ERROR", "Invalid function argument"},
+ {"PR_ADDRESS_NOT_AVAILABLE_ERROR", "Network address not available (in use?)"},
+ {"PR_ADDRESS_NOT_SUPPORTED_ERROR", "Network address type not supported"},
+ {"PR_IS_CONNECTED_ERROR", "Already connected"},
+ {"PR_BAD_ADDRESS_ERROR", "Network address is invalid"},
+ {"PR_ADDRESS_IN_USE_ERROR", "Local Network address is in use"},
+ {"PR_CONNECT_REFUSED_ERROR", "Connection refused by peer"},
+ {"PR_NETWORK_UNREACHABLE_ERROR", "Network address is presently unreachable"},
+ {"PR_CONNECT_TIMEOUT_ERROR", "Connection attempt timed out"},
+ {"PR_NOT_CONNECTED_ERROR", "Network file descriptor is not connected"},
+ {"PR_LOAD_LIBRARY_ERROR", "Failure to load dynamic library"},
+ {"PR_UNLOAD_LIBRARY_ERROR", "Failure to unload dynamic library"},
+ {"PR_FIND_SYMBOL_ERROR", "Symbol not found in any of the loaded dynamic libraries"},
+ {"PR_INSUFFICIENT_RESOURCES_ERROR", "Insufficient system resources"},
+ {"PR_DIRECTORY_LOOKUP_ERROR", "A directory lookup on a network address has failed"},
+ {"PR_TPD_RANGE_ERROR", "Attempt to access a TPD key that is out of range"},
+ {"PR_PROC_DESC_TABLE_FULL_ERROR", "Process open FD table is full"},
+ {"PR_SYS_DESC_TABLE_FULL_ERROR", "System open FD table is full"},
+ {"PR_NOT_SOCKET_ERROR", "Network operation attempted on non-network file descriptor"},
+ {"PR_NOT_TCP_SOCKET_ERROR", "TCP-specific function attempted on a non-TCP file descriptor"},
+ {"PR_SOCKET_ADDRESS_IS_BOUND_ERROR", "TCP file descriptor is already bound"},
+ {"PR_NO_ACCESS_RIGHTS_ERROR", "Access Denied"},
+ {"PR_OPERATION_NOT_SUPPORTED_ERROR", "The requested operation is not supported by the platform"},
+ {"PR_PROTOCOL_NOT_SUPPORTED_ERROR", "The host operating system does not support the protocol requested"},
+ {"PR_REMOTE_FILE_ERROR", "Access to the remote file has been severed"},
+ {"PR_BUFFER_OVERFLOW_ERROR", "The value requested is too large to be stored in the data buffer provided"},
+ {"PR_CONNECT_RESET_ERROR", "TCP connection reset by peer"},
+ {"PR_RANGE_ERROR", "Unused"},
+ {"PR_DEADLOCK_ERROR", "The operation would have deadlocked"},
+ {"PR_FILE_IS_LOCKED_ERROR", "The file is already locked"},
+ {"PR_FILE_TOO_BIG_ERROR", "Write would result in file larger than the system allows"},
+ {"PR_NO_DEVICE_SPACE_ERROR", "The device for storing the file is full"},
+ {"PR_PIPE_ERROR", "Unused"},
+ {"PR_NO_SEEK_DEVICE_ERROR", "Unused"},
+ {"PR_IS_DIRECTORY_ERROR", "Cannot perform a normal file operation on a directory"},
+ {"PR_LOOP_ERROR", "Symbolic link loop"},
+ {"PR_NAME_TOO_LONG_ERROR", "File name is too long"},
+ {"PR_FILE_NOT_FOUND_ERROR", "File not found"},
+ {"PR_NOT_DIRECTORY_ERROR", "Cannot perform directory operation on a normal file"},
+ {"PR_READ_ONLY_FILESYSTEM_ERROR", "Cannot write to a read-only file system"},
+ {"PR_DIRECTORY_NOT_EMPTY_ERROR", "Cannot delete a directory that is not empty"},
+ {"PR_FILESYSTEM_MOUNTED_ERROR", "Cannot delete or rename a file object while the file system is busy"},
+ {"PR_NOT_SAME_DEVICE_ERROR", "Cannot rename a file to a file system on another device"},
+ {"PR_DIRECTORY_CORRUPTED_ERROR", "The directory object in the file system is corrupted"},
+ {"PR_FILE_EXISTS_ERROR", "Cannot create or rename a filename that already exists"},
+ {"PR_MAX_DIRECTORY_ENTRIES_ERROR", "Directory is full. No additional filenames may be added"},
+ {"PR_INVALID_DEVICE_STATE_ERROR", "The required device was in an invalid state"},
+ {"PR_DEVICE_IS_LOCKED_ERROR", "The device is locked"},
+ {"PR_NO_MORE_FILES_ERROR", "No more entries in the directory"},
+ {"PR_END_OF_FILE_ERROR", "Encountered end of file"},
+ {"PR_FILE_SEEK_ERROR", "Seek error"},
+ {"PR_FILE_IS_BUSY_ERROR", "The file is busy"},
+ {"PR_OPERATION_ABORTED_ERROR", "The I/O operation was aborted"},
+ {"PR_IN_PROGRESS_ERROR", "Operation is still in progress (probably a non-blocking connect)"},
+ {"PR_ALREADY_INITIATED_ERROR", "Operation has already been initiated (probably a non-blocking connect)"},
+ {"PR_GROUP_EMPTY_ERROR", "The wait group is empty"},
+ {"PR_INVALID_STATE_ERROR", "Object state improper for request"},
+ {"PR_NETWORK_DOWN_ERROR", "Network is down"},
+ {"PR_SOCKET_SHUTDOWN_ERROR", "Socket shutdown"},
+ {"PR_CONNECT_ABORTED_ERROR", "Connection aborted"},
+ {"PR_HOST_UNREACHABLE_ERROR", "Host is unreachable"},
+ {"PR_LIBRARY_NOT_LOADED_ERROR", "The library is not loaded"},
+ {"PR_CALL_ONCE_ERROR", "The one-time function was previously called and failed. Its error code is no longer available"},
+ {"PR_MAX_ERROR", "Placeholder for the end of the list"},
+ {0, 0}
+};
+
+static const struct PRErrorTable et = { text, "prerr", -6000L, 77 };
+
+void nspr_InitializePRErrorTable(void) {
+ PR_ErrorInstallTable(&et);
+}
diff --git a/nsprpub/pr/src/misc/prerr.et b/nsprpub/pr/src/misc/prerr.et
new file mode 100644
index 0000000000..fa81aafcf6
--- /dev/null
+++ b/nsprpub/pr/src/misc/prerr.et
@@ -0,0 +1,108 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+et nspr -6000
+
+ec PR_OUT_OF_MEMORY_ERROR, "Memory allocation attempt failed"
+ec PR_BAD_DESCRIPTOR_ERROR, "Invalid file descriptor"
+ec PR_WOULD_BLOCK_ERROR, "The operation would have blocked"
+ec PR_ACCESS_FAULT_ERROR, "Invalid memory address argument"
+ec PR_INVALID_METHOD_ERROR, "Invalid function for file type"
+ec PR_ILLEGAL_ACCESS_ERROR, "Invalid memory address argument"
+ec PR_UNKNOWN_ERROR, "Some unknown error has occurred"
+ec PR_PENDING_INTERRUPT_ERROR,"Operation interrupted by another thread"
+ec PR_NOT_IMPLEMENTED_ERROR, "function not implemented"
+ec PR_IO_ERROR, "I/O function error"
+ec PR_IO_TIMEOUT_ERROR, "I/O operation timed out"
+ec PR_IO_PENDING_ERROR, "I/O operation on busy file descriptor"
+ec PR_DIRECTORY_OPEN_ERROR, "The directory could not be opened"
+ec PR_INVALID_ARGUMENT_ERROR, "Invalid function argument"
+ec PR_ADDRESS_NOT_AVAILABLE_ERROR, "Network address not available (in use?)"
+ec PR_ADDRESS_NOT_SUPPORTED_ERROR, "Network address type not supported"
+ec PR_IS_CONNECTED_ERROR, "Already connected"
+ec PR_BAD_ADDRESS_ERROR, "Network address is invalid"
+ec PR_ADDRESS_IN_USE_ERROR, "Local Network address is in use"
+ec PR_CONNECT_REFUSED_ERROR, "Connection refused by peer"
+ec PR_NETWORK_UNREACHABLE_ERROR, "Network address is presently unreachable"
+ec PR_CONNECT_TIMEOUT_ERROR, "Connection attempt timed out"
+ec PR_NOT_CONNECTED_ERROR, "Network file descriptor is not connected"
+ec PR_LOAD_LIBRARY_ERROR, "Failure to load dynamic library"
+ec PR_UNLOAD_LIBRARY_ERROR, "Failure to unload dynamic library"
+ec PR_FIND_SYMBOL_ERROR,
+"Symbol not found in any of the loaded dynamic libraries"
+ec PR_INSUFFICIENT_RESOURCES_ERROR, "Insufficient system resources"
+ec PR_DIRECTORY_LOOKUP_ERROR,
+"A directory lookup on a network address has failed"
+ec PR_TPD_RANGE_ERROR,
+"Attempt to access a TPD key that is out of range"
+ec PR_PROC_DESC_TABLE_FULL_ERROR, "Process open FD table is full"
+ec PR_SYS_DESC_TABLE_FULL_ERROR, "System open FD table is full"
+ec PR_NOT_SOCKET_ERROR,
+"Network operation attempted on non-network file descriptor"
+ec PR_NOT_TCP_SOCKET_ERROR,
+"TCP-specific function attempted on a non-TCP file descriptor"
+ec PR_SOCKET_ADDRESS_IS_BOUND_ERROR, "TCP file descriptor is already bound"
+ec PR_NO_ACCESS_RIGHTS_ERROR, "Access Denied"
+ec PR_OPERATION_NOT_SUPPORTED_ERROR,
+"The requested operation is not supported by the platform"
+ec PR_PROTOCOL_NOT_SUPPORTED_ERROR,
+"The host operating system does not support the protocol requested"
+ec PR_REMOTE_FILE_ERROR, "Access to the remote file has been severed"
+ec PR_BUFFER_OVERFLOW_ERROR,
+"The value requested is too large to be stored in the data buffer provided"
+ec PR_CONNECT_RESET_ERROR, "TCP connection reset by peer"
+ec PR_RANGE_ERROR, "Unused"
+ec PR_DEADLOCK_ERROR, "The operation would have deadlocked"
+ec PR_FILE_IS_LOCKED_ERROR, "The file is already locked"
+ec PR_FILE_TOO_BIG_ERROR,
+"Write would result in file larger than the system allows"
+ec PR_NO_DEVICE_SPACE_ERROR, "The device for storing the file is full"
+ec PR_PIPE_ERROR, "Unused"
+ec PR_NO_SEEK_DEVICE_ERROR, "Unused"
+ec PR_IS_DIRECTORY_ERROR,
+"Cannot perform a normal file operation on a directory"
+ec PR_LOOP_ERROR, "Symbolic link loop"
+ec PR_NAME_TOO_LONG_ERROR, "File name is too long"
+ec PR_FILE_NOT_FOUND_ERROR, "File not found"
+ec PR_NOT_DIRECTORY_ERROR,
+"Cannot perform directory operation on a normal file"
+ec PR_READ_ONLY_FILESYSTEM_ERROR,
+"Cannot write to a read-only file system"
+ec PR_DIRECTORY_NOT_EMPTY_ERROR,
+"Cannot delete a directory that is not empty"
+ec PR_FILESYSTEM_MOUNTED_ERROR,
+"Cannot delete or rename a file object while the file system is busy"
+ec PR_NOT_SAME_DEVICE_ERROR,
+"Cannot rename a file to a file system on another device"
+ec PR_DIRECTORY_CORRUPTED_ERROR,
+"The directory object in the file system is corrupted"
+ec PR_FILE_EXISTS_ERROR,
+"Cannot create or rename a filename that already exists"
+ec PR_MAX_DIRECTORY_ENTRIES_ERROR,
+"Directory is full. No additional filenames may be added"
+ec PR_INVALID_DEVICE_STATE_ERROR,
+"The required device was in an invalid state"
+ec PR_DEVICE_IS_LOCKED_ERROR, "The device is locked"
+ec PR_NO_MORE_FILES_ERROR, "No more entries in the directory"
+ec PR_END_OF_FILE_ERROR, "Encountered end of file"
+ec PR_FILE_SEEK_ERROR, "Seek error"
+ec PR_FILE_IS_BUSY_ERROR, "The file is busy"
+ec PR_OPERATION_ABORTED_ERROR, "The I/O operation was aborted"
+ec PR_IN_PROGRESS_ERROR,
+"Operation is still in progress (probably a non-blocking connect)"
+ec PR_ALREADY_INITIATED_ERROR,
+"Operation has already been initiated (probably a non-blocking connect)"
+ec PR_GROUP_EMPTY_ERROR, "The wait group is empty"
+ec PR_INVALID_STATE_ERROR, "Object state improper for request"
+ec PR_NETWORK_DOWN_ERROR, "Network is down"
+ec PR_SOCKET_SHUTDOWN_ERROR, "Socket shutdown"
+ec PR_CONNECT_ABORTED_ERROR, "Connection aborted"
+ec PR_HOST_UNREACHABLE_ERROR, "Host is unreachable"
+ec PR_LIBRARY_NOT_LOADED_ERROR, "The library is not loaded"
+ec PR_CALL_ONCE_ERROR, "The one-time function was previously called and failed. Its error code is no longer available"
+
+ec PR_MAX_ERROR, "Placeholder for the end of the list"
+
+end
diff --git a/nsprpub/pr/src/misc/prerr.properties b/nsprpub/pr/src/misc/prerr.properties
new file mode 100644
index 0000000000..ef236b138a
--- /dev/null
+++ b/nsprpub/pr/src/misc/prerr.properties
@@ -0,0 +1,85 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#
+# prerr.properties
+# This file is automatically generated; please do not edit it.
+PR_OUT_OF_MEMORY_ERROR=Memory allocation attempt failed
+PR_BAD_DESCRIPTOR_ERROR=Invalid file descriptor
+PR_WOULD_BLOCK_ERROR=The operation would have blocked
+PR_ACCESS_FAULT_ERROR=Invalid memory address argument
+PR_INVALID_METHOD_ERROR=Invalid function for file type
+PR_ILLEGAL_ACCESS_ERROR=Invalid memory address argument
+PR_UNKNOWN_ERROR=Some unknown error has occurred
+PR_PENDING_INTERRUPT_ERROR=Operation interrupted by another thread
+PR_NOT_IMPLEMENTED_ERROR=function not implemented
+PR_IO_ERROR=I/O function error
+PR_IO_TIMEOUT_ERROR=I/O operation timed out
+PR_IO_PENDING_ERROR=I/O operation on busy file descriptor
+PR_DIRECTORY_OPEN_ERROR=The directory could not be opened
+PR_INVALID_ARGUMENT_ERROR=Invalid function argument
+PR_ADDRESS_NOT_AVAILABLE_ERROR=Network address not available (in use?)
+PR_ADDRESS_NOT_SUPPORTED_ERROR=Network address type not supported
+PR_IS_CONNECTED_ERROR=Already connected
+PR_BAD_ADDRESS_ERROR=Network address is invalid
+PR_ADDRESS_IN_USE_ERROR=Local Network address is in use
+PR_CONNECT_REFUSED_ERROR=Connection refused by peer
+PR_NETWORK_UNREACHABLE_ERROR=Network address is presently unreachable
+PR_CONNECT_TIMEOUT_ERROR=Connection attempt timed out
+PR_NOT_CONNECTED_ERROR=Network file descriptor is not connected
+PR_LOAD_LIBRARY_ERROR=Failure to load dynamic library
+PR_UNLOAD_LIBRARY_ERROR=Failure to unload dynamic library
+PR_FIND_SYMBOL_ERROR=Symbol not found in any of the loaded dynamic libraries
+PR_INSUFFICIENT_RESOURCES_ERROR=Insufficient system resources
+PR_DIRECTORY_LOOKUP_ERROR=A directory lookup on a network address has failed
+PR_TPD_RANGE_ERROR=Attempt to access a TPD key that is out of range
+PR_PROC_DESC_TABLE_FULL_ERROR=Process open FD table is full
+PR_SYS_DESC_TABLE_FULL_ERROR=System open FD table is full
+PR_NOT_SOCKET_ERROR=Network operation attempted on non-network file descriptor
+PR_NOT_TCP_SOCKET_ERROR=TCP-specific function attempted on a non-TCP file descriptor
+PR_SOCKET_ADDRESS_IS_BOUND_ERROR=TCP file descriptor is already bound
+PR_NO_ACCESS_RIGHTS_ERROR=Access Denied
+PR_OPERATION_NOT_SUPPORTED_ERROR=The requested operation is not supported by the platform
+PR_PROTOCOL_NOT_SUPPORTED_ERROR=The host operating system does not support the protocol requested
+PR_REMOTE_FILE_ERROR=Access to the remote file has been severed
+PR_BUFFER_OVERFLOW_ERROR=The value requested is too large to be stored in the data buffer provided
+PR_CONNECT_RESET_ERROR=TCP connection reset by peer
+PR_RANGE_ERROR=Unused
+PR_DEADLOCK_ERROR=The operation would have deadlocked
+PR_FILE_IS_LOCKED_ERROR=The file is already locked
+PR_FILE_TOO_BIG_ERROR=Write would result in file larger than the system allows
+PR_NO_DEVICE_SPACE_ERROR=The device for storing the file is full
+PR_PIPE_ERROR=Unused
+PR_NO_SEEK_DEVICE_ERROR=Unused
+PR_IS_DIRECTORY_ERROR=Cannot perform a normal file operation on a directory
+PR_LOOP_ERROR=Symbolic link loop
+PR_NAME_TOO_LONG_ERROR=File name is too long
+PR_FILE_NOT_FOUND_ERROR=File not found
+PR_NOT_DIRECTORY_ERROR=Cannot perform directory operation on a normal file
+PR_READ_ONLY_FILESYSTEM_ERROR=Cannot write to a read-only file system
+PR_DIRECTORY_NOT_EMPTY_ERROR=Cannot delete a directory that is not empty
+PR_FILESYSTEM_MOUNTED_ERROR=Cannot delete or rename a file object while the file system is busy
+PR_NOT_SAME_DEVICE_ERROR=Cannot rename a file to a file system on another device
+PR_DIRECTORY_CORRUPTED_ERROR=The directory object in the file system is corrupted
+PR_FILE_EXISTS_ERROR=Cannot create or rename a filename that already exists
+PR_MAX_DIRECTORY_ENTRIES_ERROR=Directory is full. No additional filenames may be added
+PR_INVALID_DEVICE_STATE_ERROR=The required device was in an invalid state
+PR_DEVICE_IS_LOCKED_ERROR=The device is locked
+PR_NO_MORE_FILES_ERROR=No more entries in the directory
+PR_END_OF_FILE_ERROR=Encountered end of file
+PR_FILE_SEEK_ERROR=Seek error
+PR_FILE_IS_BUSY_ERROR=The file is busy
+PR_OPERATION_ABORTED_ERROR=The I/O operation was aborted
+PR_IN_PROGRESS_ERROR=Operation is still in progress (probably a non-blocking connect)
+PR_ALREADY_INITIATED_ERROR=Operation has already been initiated (probably a non-blocking connect)
+PR_GROUP_EMPTY_ERROR=The wait group is empty
+PR_INVALID_STATE_ERROR=Object state improper for request
+PR_NETWORK_DOWN_ERROR=Network is down
+PR_SOCKET_SHUTDOWN_ERROR=Socket shutdown
+PR_CONNECT_ABORTED_ERROR=Connection aborted
+PR_HOST_UNREACHABLE_ERROR=Host is unreachable
+PR_LIBRARY_NOT_LOADED_ERROR=The library is not loaded
+PR_CALL_ONCE_ERROR=The one-time function was previously called and failed. Its error code is no longer available
+PR_MAX_ERROR=Placeholder for the end of the list
diff --git a/nsprpub/pr/src/misc/prerror.c b/nsprpub/pr/src/misc/prerror.c
new file mode 100644
index 0000000000..a06b78ddbc
--- /dev/null
+++ b/nsprpub/pr/src/misc/prerror.c
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+PR_IMPLEMENT(PRErrorCode) PR_GetError(void)
+{
+ PRThread *thread = PR_GetCurrentThread();
+ return thread->errorCode;
+}
+
+PR_IMPLEMENT(PRInt32) PR_GetOSError(void)
+{
+ PRThread *thread = PR_GetCurrentThread();
+ return thread->osErrorCode;
+}
+
+PR_IMPLEMENT(void) PR_SetError(PRErrorCode code, PRInt32 osErr)
+{
+ PRThread *thread = PR_GetCurrentThread();
+ thread->errorCode = code;
+ thread->osErrorCode = osErr;
+ thread->errorStringLength = 0;
+}
+
+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);
+ }
+ thread->errorStringSize = 0;
+ }
+ else
+ {
+ PRIntn size = textLength + 31; /* actual length to allocate. Plus a little extra */
+ if (thread->errorStringSize < textLength+1) /* do we have room? */
+ {
+ if (NULL != thread->errorString) {
+ PR_DELETE(thread->errorString);
+ }
+ thread->errorString = (char*)PR_MALLOC(size);
+ if ( NULL == thread->errorString ) {
+ thread->errorStringSize = 0;
+ thread->errorStringLength = 0;
+ return;
+ }
+ thread->errorStringSize = size;
+ }
+ memcpy(thread->errorString, text, textLength+1 );
+ }
+ thread->errorStringLength = textLength;
+}
+
+PR_IMPLEMENT(PRInt32) PR_GetErrorTextLength(void)
+{
+ PRThread *thread = PR_GetCurrentThread();
+ return thread->errorStringLength;
+} /* PR_GetErrorTextLength */
+
+PR_IMPLEMENT(PRInt32) PR_GetErrorText(char *text)
+{
+ PRThread *thread = PR_GetCurrentThread();
+ if (0 != thread->errorStringLength) {
+ memcpy(text, thread->errorString, thread->errorStringLength+1);
+ }
+ return thread->errorStringLength;
+} /* PR_GetErrorText */
+
+
diff --git a/nsprpub/pr/src/misc/prerrortable.c b/nsprpub/pr/src/misc/prerrortable.c
new file mode 100644
index 0000000000..a7ff4f0eb5
--- /dev/null
+++ b/nsprpub/pr/src/misc/prerrortable.c
@@ -0,0 +1,209 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+
+
+/*
+
+Copyright 1987, 1988 by the Student Information Processing Board
+ of the Massachusetts Institute of Technology
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is
+hereby granted, provided that the above copyright notice
+appear in all copies and that both that copyright notice and
+this permission notice appear in supporting documentation,
+and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
+used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+M.I.T. and the M.I.T. S.I.P.B. make no representations about
+the suitability of this software for any purpose. It is
+provided "as is" without express or implied warranty.
+
+*/
+
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include "prmem.h"
+#include "prerror.h"
+
+#define ERRCODE_RANGE 8 /* # of bits to shift table number */
+#define BITS_PER_CHAR 6 /* # bits to shift per character in name */
+
+#ifdef NEED_SYS_ERRLIST
+extern char const * const sys_errlist[];
+extern const int sys_nerr;
+#endif
+
+/* List of error tables */
+struct PRErrorTableList {
+ struct PRErrorTableList *next;
+ const struct PRErrorTable *table;
+ struct PRErrorCallbackTablePrivate *table_private;
+};
+static struct PRErrorTableList * Table_List = (struct PRErrorTableList *) NULL;
+
+/* Supported languages */
+static const char * default_languages[] = { "i-default", "en", 0 };
+static const char * const * callback_languages = default_languages;
+
+/* Callback info */
+static struct PRErrorCallbackPrivate *callback_private = 0;
+static PRErrorCallbackLookupFn *callback_lookup = 0;
+static PRErrorCallbackNewTableFn *callback_newtable = 0;
+
+
+static const char char_set[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
+
+static const char *
+error_table_name (PRErrorCode num)
+{
+ static char buf[6]; /* only used if internal code problems exist */
+
+ long ch;
+ int i;
+ char *p;
+
+ /* num = aa aaa abb bbb bcc ccc cdd ddd d?? ??? ??? */
+ p = buf;
+ num >>= ERRCODE_RANGE;
+ /* num = ?? ??? ??? aaa aaa bbb bbb ccc ccc ddd ddd */
+ num &= 077777777;
+ /* num = 00 000 000 aaa aaa bbb bbb ccc ccc ddd ddd */
+ for (i = 4; i >= 0; i--) {
+ ch = (num >> BITS_PER_CHAR * i) & ((1 << BITS_PER_CHAR) - 1);
+ if (ch != 0) {
+ *p++ = char_set[ch-1];
+ }
+ }
+ *p = '\0';
+ return(buf);
+}
+
+PR_IMPLEMENT(const char *)
+PR_ErrorToString(PRErrorCode code, PRLanguageCode language)
+{
+ /* static buffer only used if code is using inconsistent error message
+ * numbers, so just ignore the possible thread contention
+ */
+ static char buffer[25];
+
+ const char *msg;
+ int offset;
+ PRErrorCode table_num;
+ struct PRErrorTableList *et;
+ int started = 0;
+ char *cp;
+
+ for (et = Table_List; et; et = et->next) {
+ if (et->table->base <= code &&
+ et->table->base + et->table->n_msgs > code) {
+ /* This is the right table */
+ if (callback_lookup) {
+ msg = callback_lookup(code, language, et->table,
+ callback_private, et->table_private);
+ if (msg) {
+ return msg;
+ }
+ }
+
+ return(et->table->msgs[code - et->table->base].en_text);
+ }
+ }
+
+ if (code >= 0 && code < 256) {
+ return strerror(code);
+ }
+
+ offset = (int) (code & ((1<<ERRCODE_RANGE)-1));
+ table_num = code - offset;
+ strcpy (buffer, "Unknown code ");
+ if (table_num) {
+ strcat(buffer, error_table_name (table_num));
+ strcat(buffer, " ");
+ }
+ for (cp = buffer; *cp; cp++)
+ ;
+ if (offset >= 100) {
+ *cp++ = (char)('0' + offset / 100);
+ offset %= 100;
+ started++;
+ }
+ if (started || offset >= 10) {
+ *cp++ = (char)('0' + offset / 10);
+ offset %= 10;
+ }
+ *cp++ = (char)('0' + offset);
+ *cp = '\0';
+ return(buffer);
+}
+
+PR_IMPLEMENT(const char *)
+PR_ErrorToName(PRErrorCode code)
+{
+ struct PRErrorTableList *et;
+
+ for (et = Table_List; et; et = et->next) {
+ if (et->table->base <= code &&
+ et->table->base + et->table->n_msgs > code) {
+ /* This is the right table */
+ return(et->table->msgs[code - et->table->base].name);
+ }
+ }
+
+ return 0;
+}
+
+PR_IMPLEMENT(const char * const *)
+PR_ErrorLanguages(void)
+{
+ return callback_languages;
+}
+
+PR_IMPLEMENT(PRErrorCode)
+PR_ErrorInstallTable(const struct PRErrorTable *table)
+{
+ struct PRErrorTableList * new_et;
+
+ new_et = (struct PRErrorTableList *)
+ PR_Malloc(sizeof(struct PRErrorTableList));
+ if (!new_et) {
+ return errno; /* oops */
+ }
+ new_et->table = table;
+ if (callback_newtable) {
+ new_et->table_private = callback_newtable(table, callback_private);
+ } else {
+ new_et->table_private = 0;
+ }
+ new_et->next = Table_List;
+ Table_List = new_et;
+ return 0;
+}
+
+PR_IMPLEMENT(void)
+PR_ErrorInstallCallback(const char * const * languages,
+ PRErrorCallbackLookupFn *lookup,
+ PRErrorCallbackNewTableFn *newtable,
+ struct PRErrorCallbackPrivate *cb_private)
+{
+ struct PRErrorTableList *et;
+
+ assert(strcmp(languages[0], "i-default") == 0);
+ assert(strcmp(languages[1], "en") == 0);
+
+ callback_languages = languages;
+ callback_lookup = lookup;
+ callback_newtable = newtable;
+ callback_private = cb_private;
+
+ if (callback_newtable) {
+ for (et = Table_List; et; et = et->next) {
+ et->table_private = callback_newtable(et->table, callback_private);
+ }
+ }
+}
diff --git a/nsprpub/pr/src/misc/prinit.c b/nsprpub/pr/src/misc/prinit.c
new file mode 100644
index 0000000000..5ac99fe588
--- /dev/null
+++ b/nsprpub/pr/src/misc/prinit.c
@@ -0,0 +1,839 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+#include <ctype.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;
+PRLogModuleInfo *_pr_shm_lm;
+PRLogModuleInfo *_pr_shma_lm;
+
+PRFileDesc *_pr_stdin;
+PRFileDesc *_pr_stdout;
+PRFileDesc *_pr_stderr;
+
+#if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS)
+
+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.
+ **
+ ** This release has a simple version compatibility
+ ** check algorithm. This release is not backward
+ ** compatible with previous major releases. It is
+ ** not compatible with future major, minor, or
+ ** patch releases.
+ */
+ int vmajor = 0, vminor = 0, vpatch = 0;
+ const char *ptr = importedVersion;
+
+ while (isdigit(*ptr)) {
+ vmajor = 10 * vmajor + *ptr - '0';
+ ptr++;
+ }
+ if (*ptr == '.') {
+ ptr++;
+ while (isdigit(*ptr)) {
+ vminor = 10 * vminor + *ptr - '0';
+ ptr++;
+ }
+ if (*ptr == '.') {
+ ptr++;
+ while (isdigit(*ptr)) {
+ vpatch = 10 * vpatch + *ptr - '0';
+ ptr++;
+ }
+ }
+ }
+
+ if (vmajor != PR_VMAJOR) {
+ return PR_FALSE;
+ }
+ if (vmajor == PR_VMAJOR && vminor > PR_VMINOR) {
+ return PR_FALSE;
+ }
+ if (vmajor == PR_VMAJOR && vminor == PR_VMINOR && vpatch > PR_VPATCH) {
+ return PR_FALSE;
+ }
+ return PR_TRUE;
+} /* PR_VersionCheck */
+
+PR_IMPLEMENT(const char*) PR_GetVersion(void)
+{
+ return PR_VERSION;
+}
+
+PR_IMPLEMENT(PRBool) PR_Initialized(void)
+{
+ return _pr_initialized;
+}
+
+PRInt32 _native_threads_only = 0;
+
+#ifdef WINNT
+static void _pr_SetNativeThreadsOnlyMode(void)
+{
+ HMODULE mainExe;
+ PRBool *globalp;
+ char *envp;
+
+ mainExe = GetModuleHandle(NULL);
+ PR_ASSERT(NULL != mainExe);
+ globalp = (PRBool *) GetProcAddress(mainExe, "nspr_native_threads_only");
+ if (globalp) {
+ _native_threads_only = (*globalp != PR_FALSE);
+ } else if (envp = getenv("NSPR_NATIVE_THREADS_ONLY")) {
+ _native_threads_only = (atoi(envp) == 1);
+ }
+}
+#endif
+
+static void _PR_InitStuff(void)
+{
+
+ if (_pr_initialized) {
+ return;
+ }
+ _pr_initialized = PR_TRUE;
+#ifdef _PR_ZONE_ALLOCATOR
+ _PR_InitZones();
+#endif
+#ifdef WINNT
+ _pr_SetNativeThreadsOnlyMode();
+#endif
+
+
+ (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");
+ _pr_shm_lm = PR_NewLogModule("shm");
+ _pr_shma_lm = PR_NewLogModule("shma");
+
+ /* 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();
+ _PR_InitTime();
+ _PR_InitLog();
+ _PR_InitLinker();
+ _PR_InitCallOnce();
+ _PR_InitDtoa();
+ _PR_InitMW();
+ _PR_InitRWLocks();
+
+ nspr_InitializePRErrorTable();
+
+ _PR_MD_FINAL_INIT();
+}
+
+void _PR_ImplicitInitialization(void)
+{
+ _PR_InitStuff();
+
+ /* Enable interrupts */
+#if !defined(_PR_PTHREADS) && !defined(_PR_GLOBAL_THREADS_ONLY)
+ _PR_MD_START_INTERRUPTS();
+#endif
+
+}
+
+PR_IMPLEMENT(void) PR_DisableClockInterrupts(void)
+{
+#if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS)
+ if (!_pr_initialized) {
+ _PR_InitStuff();
+ } else {
+ _PR_MD_DISABLE_CLOCK_INTERRUPTS();
+ }
+#endif
+}
+
+PR_IMPLEMENT(void) PR_EnableClockInterrupts(void)
+{
+#if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS)
+ if (!_pr_initialized) {
+ _PR_InitStuff();
+ }
+ _PR_MD_ENABLE_CLOCK_INTERRUPTS();
+#endif
+}
+
+PR_IMPLEMENT(void) PR_BlockClockInterrupts(void)
+{
+#if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS)
+ _PR_MD_BLOCK_CLOCK_INTERRUPTS();
+#endif
+}
+
+PR_IMPLEMENT(void) PR_UnblockClockInterrupts(void)
+{
+#if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS)
+ _PR_MD_UNBLOCK_CLOCK_INTERRUPTS();
+#endif
+}
+
+PR_IMPLEMENT(void) PR_Init(
+ PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs)
+{
+ _PR_ImplicitInitialization();
+}
+
+PR_IMPLEMENT(PRIntn) PR_Initialize(
+ PRPrimordialFn prmain, PRIntn argc, char **argv, PRUintn maxPTDs)
+{
+ 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) || defined(_PR_BTHREADS)
+/* 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) || defined(_PR_BTHREADS)
+/* 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);
+ }
+ if (me->flags & _PR_SYSTEM) {
+ _pr_systemActive--;
+ } else {
+ _pr_userActive--;
+ }
+ PR_Unlock(_pr_activeLock);
+
+ _PR_MD_EARLY_CLEANUP();
+
+ _PR_CleanupMW();
+ _PR_CleanupTime();
+ _PR_CleanupDtoa();
+ _PR_CleanupCallOnce();
+ _PR_ShutdownLinker();
+ _PR_CleanupNet();
+ _PR_CleanupIO();
+ /* 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"));
+ _PR_LogCleanup();
+
+ /*
+ * 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.
+ */
+#ifdef WINNT
+ _PR_CleanupCPUs();
+#endif
+ _PR_CleanupThreads();
+ _PR_CleanupCMon();
+ PR_DestroyLock(_pr_sleeplock);
+ _pr_sleeplock = NULL;
+ _PR_CleanupLayerCache();
+ _PR_CleanupEnv();
+ _PR_CleanupStacks();
+ _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) || defined(_PR_BTHREADS)
+/* 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);
+ PR_FREEIF(attr->fdInheritBuffer);
+ memset(attr, 0, sizeof(*attr));
+}
+
+PR_IMPLEMENT(void)
+PR_DestroyProcessAttr(PRProcessAttr *attr)
+{
+ PR_FREEIF(attr->currentDirectory);
+ PR_FREEIF(attr->fdInheritBuffer);
+ 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 = (char *) 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(PRStatus)
+PR_ProcessAttrSetInheritableFD(
+ PRProcessAttr *attr,
+ PRFileDesc *fd,
+ const char *name)
+{
+ /* We malloc the fd inherit buffer in multiples of this number. */
+#define FD_INHERIT_BUFFER_INCR 128
+ /* The length of "NSPR_INHERIT_FDS=" */
+#define NSPR_INHERIT_FDS_STRLEN 17
+ /* The length of osfd (PROsfd) printed in hexadecimal with 0x prefix */
+#ifdef _WIN64
+#define OSFD_STRLEN 18
+#else
+#define OSFD_STRLEN 10
+#endif
+ /* The length of fd type (PRDescType) printed in decimal */
+#define FD_TYPE_STRLEN 1
+ PRSize newSize;
+ int remainder;
+ char *newBuffer;
+ int nwritten;
+ char *cur;
+ int freeSize;
+
+ if (fd->identity != PR_NSPR_IO_LAYER) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ if (fd->secret->inheritable == _PR_TRI_UNKNOWN) {
+ _PR_MD_QUERY_FD_INHERITABLE(fd);
+ }
+ if (fd->secret->inheritable != _PR_TRI_TRUE) {
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+ /*
+ * We also need to account for the : separators and the
+ * terminating null byte.
+ */
+ if (NULL == attr->fdInheritBuffer) {
+ /* The first time, we print "NSPR_INHERIT_FDS=<name>:<type>:<val>" */
+ newSize = NSPR_INHERIT_FDS_STRLEN + strlen(name)
+ + FD_TYPE_STRLEN + OSFD_STRLEN + 2 + 1;
+ } else {
+ /* At other times, we print ":<name>:<type>:<val>" */
+ newSize = attr->fdInheritBufferUsed + strlen(name)
+ + FD_TYPE_STRLEN + OSFD_STRLEN + 3 + 1;
+ }
+ if (newSize > attr->fdInheritBufferSize) {
+ /* Make newSize a multiple of FD_INHERIT_BUFFER_INCR */
+ remainder = newSize % FD_INHERIT_BUFFER_INCR;
+ if (remainder != 0) {
+ newSize += (FD_INHERIT_BUFFER_INCR - remainder);
+ }
+ if (NULL == attr->fdInheritBuffer) {
+ newBuffer = (char *) PR_MALLOC(newSize);
+ } else {
+ newBuffer = (char *) PR_REALLOC(attr->fdInheritBuffer, newSize);
+ }
+ if (NULL == newBuffer) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return PR_FAILURE;
+ }
+ attr->fdInheritBuffer = newBuffer;
+ attr->fdInheritBufferSize = newSize;
+ }
+ cur = attr->fdInheritBuffer + attr->fdInheritBufferUsed;
+ freeSize = attr->fdInheritBufferSize - attr->fdInheritBufferUsed;
+ if (0 == attr->fdInheritBufferUsed) {
+ nwritten = PR_snprintf(cur, freeSize,
+ "NSPR_INHERIT_FDS=%s:%d:0x%" PR_PRIxOSFD,
+ name, (PRIntn)fd->methods->file_type, fd->secret->md.osfd);
+ } else {
+ nwritten = PR_snprintf(cur, freeSize, ":%s:%d:0x%" PR_PRIxOSFD,
+ name, (PRIntn)fd->methods->file_type, fd->secret->md.osfd);
+ }
+ attr->fdInheritBufferUsed += nwritten;
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRFileDesc *) PR_GetInheritedFD(
+ const char *name)
+{
+ PRFileDesc *fd;
+ const char *envVar;
+ const char *ptr;
+ int len = strlen(name);
+ PROsfd osfd;
+ int nColons;
+ PRIntn fileType;
+
+ envVar = PR_GetEnv("NSPR_INHERIT_FDS");
+ if (NULL == envVar || '\0' == envVar[0]) {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return NULL;
+ }
+
+ ptr = envVar;
+ while (1) {
+ if ((ptr[len] == ':') && (strncmp(ptr, name, len) == 0)) {
+ ptr += len + 1;
+ if (PR_sscanf(ptr, "%d:0x%" PR_SCNxOSFD, &fileType, &osfd) != 2) {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return NULL;
+ }
+ switch ((PRDescType)fileType) {
+ case PR_DESC_FILE:
+ fd = PR_ImportFile(osfd);
+ break;
+ case PR_DESC_PIPE:
+ fd = PR_ImportPipe(osfd);
+ break;
+ case PR_DESC_SOCKET_TCP:
+ fd = PR_ImportTCPSocket(osfd);
+ break;
+ case PR_DESC_SOCKET_UDP:
+ fd = PR_ImportUDPSocket(osfd);
+ break;
+ default:
+ PR_ASSERT(0);
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ fd = NULL;
+ break;
+ }
+ if (fd) {
+ /*
+ * An inherited FD is inheritable by default.
+ * The child process needs to call PR_SetFDInheritable
+ * to make it non-inheritable if so desired.
+ */
+ fd->secret->inheritable = _PR_TRI_TRUE;
+ }
+ return fd;
+ }
+ /* Skip three colons */
+ nColons = 0;
+ while (*ptr) {
+ if (*ptr == ':') {
+ if (++nColons == 3) {
+ break;
+ }
+ }
+ ptr++;
+ }
+ if (*ptr == '\0') {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return NULL;
+ }
+ ptr++;
+ }
+}
+
+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(void) {
+ 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);
+}
+
+void _PR_CleanupCallOnce()
+{
+ PR_DestroyLock(mod_init.ml);
+ mod_init.ml = NULL;
+ PR_DestroyCondVar(mod_init.cv);
+ mod_init.cv = NULL;
+}
+
+PR_IMPLEMENT(PRStatus) PR_CallOnce(
+ PRCallOnceType *once,
+ PRCallOnceFN func)
+{
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (!once->initialized) {
+ if (PR_ATOMIC_SET(&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);
+ }
+ } else {
+ if (PR_SUCCESS != once->status) {
+ PR_SetError(PR_CALL_ONCE_ERROR, 0);
+ }
+ }
+ return once->status;
+}
+
+PR_IMPLEMENT(PRStatus) PR_CallOnceWithArg(
+ PRCallOnceType *once,
+ PRCallOnceWithArgFN func,
+ void *arg)
+{
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (!once->initialized) {
+ if (PR_ATOMIC_SET(&once->inProgress, 1) == 0) {
+ once->status = (*func)(arg);
+ 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);
+ }
+ } else {
+ if (PR_SUCCESS != once->status) {
+ PR_SetError(PR_CALL_ONCE_ERROR, 0);
+ }
+ }
+ return once->status;
+}
+
+PRBool _PR_Obsolete(const char *obsolete, const char *preferred)
+{
+#if defined(DEBUG)
+ PR_fprintf(
+ PR_STDERR, "'%s' is obsolete. Use '%s' instead.\n",
+ obsolete, (NULL == preferred) ? "something else" : preferred);
+#endif
+ return PR_FALSE;
+} /* _PR_Obsolete */
+
+/* prinit.c */
+
+
diff --git a/nsprpub/pr/src/misc/prinrval.c b/nsprpub/pr/src/misc/prinrval.c
new file mode 100644
index 0000000000..79ff2cbca4
--- /dev/null
+++ b/nsprpub/pr/src/misc/prinrval.c
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * file: prinrval.c
+ * description: implementation for the kernel interval timing functions
+ */
+
+#include "primpl.h"
+
+/*
+ *-----------------------------------------------------------------------
+ *
+ * _PR_InitClock --
+ *
+ *
+ *-----------------------------------------------------------------------
+ */
+
+void _PR_InitClock(void)
+{
+ _PR_MD_INTERVAL_INIT();
+#ifdef DEBUG
+ {
+ PRIntervalTime ticksPerSec = PR_TicksPerSecond();
+
+ PR_ASSERT(ticksPerSec >= PR_INTERVAL_MIN);
+ PR_ASSERT(ticksPerSec <= PR_INTERVAL_MAX);
+ }
+#endif /* DEBUG */
+}
+
+PR_IMPLEMENT(PRIntervalTime) PR_IntervalNow(void)
+{
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ return _PR_MD_GET_INTERVAL();
+} /* PR_IntervalNow */
+
+PR_EXTERN(PRUint32) PR_TicksPerSecond(void)
+{
+ 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/nsprpub/pr/src/misc/pripc.c b/nsprpub/pr/src/misc/pripc.c
new file mode 100644
index 0000000000..64415dd631
--- /dev/null
+++ b/nsprpub/pr/src/misc/pripc.c
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: pripc.c
+ *
+ * Description: functions for IPC support
+ */
+
+#include "primpl.h"
+
+#include <string.h>
+
+/*
+ * A POSIX IPC name must begin with a '/'.
+ * A POSIX IPC name on Solaris cannot contain any '/' except
+ * the required leading '/'.
+ * A POSIX IPC name on HP-UX must be a valid pathname
+ * in the file system.
+ *
+ * The ftok() function for System V IPC requires a valid pathname
+ * in the file system.
+ *
+ * A Win32 IPC name cannot contain '\'.
+ */
+
+static void _pr_ConvertSemName(char *result)
+{
+#ifdef _PR_HAVE_POSIX_SEMAPHORES
+#if defined(SOLARIS)
+ char *p;
+
+ /* Convert '/' to '_' except for the leading '/' */
+ for (p = result+1; *p; p++) {
+ if (*p == '/') {
+ *p = '_';
+ }
+ }
+ return;
+#else
+ return;
+#endif
+#elif defined(_PR_HAVE_SYSV_SEMAPHORES)
+ return;
+#elif defined(WIN32)
+ return;
+#endif
+}
+
+static void _pr_ConvertShmName(char *result)
+{
+#if defined(PR_HAVE_POSIX_NAMED_SHARED_MEMORY)
+#if defined(SOLARIS)
+ char *p;
+
+ /* Convert '/' to '_' except for the leading '/' */
+ for (p = result+1; *p; p++) {
+ if (*p == '/') {
+ *p = '_';
+ }
+ }
+ return;
+#else
+ return;
+#endif
+#elif defined(PR_HAVE_SYSV_NAMED_SHARED_MEMORY)
+ return;
+#elif defined(WIN32)
+ return;
+#else
+ return;
+#endif
+}
+
+PRStatus _PR_MakeNativeIPCName(
+ const char *name,
+ char *result,
+ PRIntn size,
+ _PRIPCType type)
+{
+ if (strlen(name) >= (PRSize)size) {
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
+ return PR_FAILURE;
+ }
+ strcpy(result, name);
+ switch (type) {
+ case _PRIPCSem:
+ _pr_ConvertSemName(result);
+ break;
+ case _PRIPCShm:
+ _pr_ConvertShmName(result);
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
diff --git a/nsprpub/pr/src/misc/pripcsem.c b/nsprpub/pr/src/misc/pripcsem.c
new file mode 100644
index 0000000000..49b051b74d
--- /dev/null
+++ b/nsprpub/pr/src/misc/pripcsem.c
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * File: pripcsem.c
+ *
+ * Description: implements the named semaphores API in prsemipc.h
+ * for classic NSPR. If _PR_HAVE_NAMED_SEMAPHORES is not defined,
+ * the named semaphore functions all fail with the error code
+ * PR_NOT_IMPLEMENTED_ERROR.
+ */
+
+#include "primpl.h"
+
+#ifdef _PR_PTHREADS
+
+#error "This file should not be compiled for the pthreads version"
+
+#else
+
+#ifndef _PR_HAVE_NAMED_SEMAPHORES
+
+PRSem * _PR_MD_OPEN_SEMAPHORE(
+ const char *osname, PRIntn flags, PRIntn mode, PRUintn value)
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+}
+
+PRStatus _PR_MD_WAIT_SEMAPHORE(PRSem *sem)
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRStatus _PR_MD_POST_SEMAPHORE(PRSem *sem)
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRStatus _PR_MD_CLOSE_SEMAPHORE(PRSem *sem)
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRStatus _PR_MD_DELETE_SEMAPHORE(const char *osname)
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+#endif /* !_PR_HAVE_NAMED_SEMAPHORES */
+
+PR_IMPLEMENT(PRSem *) PR_OpenSemaphore(
+ const char *name, PRIntn flags, PRIntn mode, PRUintn value)
+{
+ char osname[PR_IPC_NAME_SIZE];
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem)
+ == PR_FAILURE) {
+ return NULL;
+ }
+ return _PR_MD_OPEN_SEMAPHORE(osname, flags, mode, value);
+}
+
+PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem *sem)
+{
+ return _PR_MD_WAIT_SEMAPHORE(sem);
+}
+
+PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem *sem)
+{
+ return _PR_MD_POST_SEMAPHORE(sem);
+}
+
+PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem *sem)
+{
+ return _PR_MD_CLOSE_SEMAPHORE(sem);
+}
+
+PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char *name)
+{
+ char osname[PR_IPC_NAME_SIZE];
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+ if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem)
+ == PR_FAILURE) {
+ return PR_FAILURE;
+ }
+ return _PR_MD_DELETE_SEMAPHORE(osname);
+}
+
+#endif /* _PR_PTHREADS */
diff --git a/nsprpub/pr/src/misc/prlog2.c b/nsprpub/pr/src/misc/prlog2.c
new file mode 100644
index 0000000000..2d476cd711
--- /dev/null
+++ b/nsprpub/pr/src/misc/prlog2.c
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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;
+ PR_CEILING_LOG2(log2, n);
+ 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;
+ PR_FLOOR_LOG2(log2, n);
+ return log2;
+}
diff --git a/nsprpub/pr/src/misc/prlong.c b/nsprpub/pr/src/misc/prlong.c
new file mode 100644
index 0000000000..9c5abf06bb
--- /dev/null
+++ b/nsprpub/pr/src/misc/prlong.c
@@ -0,0 +1,254 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prlong.h"
+
+static PRInt64 ll_zero = PR_INT64(0x0000000000000000);
+static PRInt64 ll_maxint = PR_INT64(0x7fffffffffffffff);
+static PRInt64 ll_minint = PR_INT64(0x8000000000000000);
+static PRUint64 ll_maxuint = PR_UINT64(0xffffffffffffffff);
+
+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;
+}
+PR_IMPLEMENT(PRUint64) LL_MaxUint(void) {
+ return ll_maxuint;
+}
+
+#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/nsprpub/pr/src/misc/prnetdb.c b/nsprpub/pr/src/misc/prnetdb.c
new file mode 100644
index 0000000000..0f645b6471
--- /dev/null
+++ b/nsprpub/pr/src/misc/prnetdb.c
@@ -0,0 +1,2482 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+#include <string.h>
+
+#if defined(LINUX)
+#include <sys/un.h>
+#endif
+
+/*
+ * 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
+
+/*
+ * The meaning of the macros related to gethostbyname, gethostbyaddr,
+ * and gethostbyname2 is defined below.
+ * - _PR_HAVE_THREADSAFE_GETHOST: the gethostbyXXX functions return
+ * the result in thread specific storage. For example, AIX, HP-UX.
+ * - _PR_HAVE_GETHOST_R: have the gethostbyXXX_r functions. See next
+ * two macros.
+ * - _PR_HAVE_GETHOST_R_INT: the gethostbyXXX_r functions return an
+ * int. For example, Linux glibc.
+ * - _PR_HAVE_GETHOST_R_POINTER: the gethostbyXXX_r functions return
+ * a struct hostent* pointer. For example, Solaris.
+ */
+#if defined(_PR_NO_PREEMPT) || defined(_PR_HAVE_GETHOST_R) \
+ || defined(_PR_HAVE_THREADSAFE_GETHOST)
+#define _PR_NO_DNS_LOCK
+#endif
+
+#if defined(_PR_NO_DNS_LOCK)
+#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_DNS_LOCK) */
+
+/*
+ * Some platforms have the reentrant getprotobyname_r() and
+ * getprotobynumber_r(). However, they come in three flavors.
+ * Some return a pointer to struct protoent, others return
+ * an int, and glibc's flavor takes five arguments.
+ */
+
+#if defined(SOLARIS) || (defined(BSDI) && defined(_REENTRANT)) \
+ || (defined(LINUX) && defined(_REENTRANT) \
+ && defined(__GLIBC__) && __GLIBC__ < 2)
+#define _PR_HAVE_GETPROTO_R
+#define _PR_HAVE_GETPROTO_R_POINTER
+#endif
+
+#if defined(AIX4_3_PLUS) || (defined(AIX) && defined(_THREAD_SAFE)) \
+ || (defined(HPUX10_10) && defined(_REENTRANT)) \
+ || (defined(HPUX10_20) && defined(_REENTRANT)) \
+ || defined(OPENBSD)
+#define _PR_HAVE_GETPROTO_R
+#define _PR_HAVE_GETPROTO_R_INT
+#endif
+
+#if __FreeBSD_version >= 602000
+#define _PR_HAVE_GETPROTO_R
+#define _PR_HAVE_5_ARG_GETPROTO_R
+#endif
+
+/* BeOS has glibc but not the glibc-style getprotobyxxx_r functions. */
+#if (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_PROBE)
+extern PRBool _pr_ipv6_is_present(void);
+#endif
+
+#define _PR_IN6_IS_ADDR_UNSPECIFIED(a) \
+ (((a)->pr_s6_addr32[0] == 0) && \
+ ((a)->pr_s6_addr32[1] == 0) && \
+ ((a)->pr_s6_addr32[2] == 0) && \
+ ((a)->pr_s6_addr32[3] == 0))
+
+#define _PR_IN6_IS_ADDR_LOOPBACK(a) \
+ (((a)->pr_s6_addr32[0] == 0) && \
+ ((a)->pr_s6_addr32[1] == 0) && \
+ ((a)->pr_s6_addr32[2] == 0) && \
+ ((a)->pr_s6_addr[12] == 0) && \
+ ((a)->pr_s6_addr[13] == 0) && \
+ ((a)->pr_s6_addr[14] == 0) && \
+ ((a)->pr_s6_addr[15] == 0x1U))
+
+const PRIPv6Addr _pr_in6addr_any = {{{
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0
+ }
+ }
+};
+
+const PRIPv6Addr _pr_in6addr_loopback = {{{
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0x1U
+ }
+ }
+};
+/*
+ * The values at bytes 10 and 11 are compared using pointers to
+ * 8-bit fields, and not 32-bit fields, to make the comparison work on
+ * both big-endian and little-endian systems
+ */
+
+#define _PR_IN6_IS_ADDR_V4MAPPED(a) \
+ (((a)->pr_s6_addr32[0] == 0) && \
+ ((a)->pr_s6_addr32[1] == 0) && \
+ ((a)->pr_s6_addr[8] == 0) && \
+ ((a)->pr_s6_addr[9] == 0) && \
+ ((a)->pr_s6_addr[10] == 0xff) && \
+ ((a)->pr_s6_addr[11] == 0xff))
+
+#define _PR_IN6_IS_ADDR_V4COMPAT(a) \
+ (((a)->pr_s6_addr32[0] == 0) && \
+ ((a)->pr_s6_addr32[1] == 0) && \
+ ((a)->pr_s6_addr32[2] == 0))
+
+#define _PR_IN6_V4MAPPED_TO_IPADDR(a) ((a)->pr_s6_addr32[3])
+
+#if defined(_PR_INET6) && defined(_PR_HAVE_GETHOSTBYNAME2)
+
+/*
+ * The _pr_QueryNetIfs() function finds out if the system has
+ * IPv4 or IPv6 source addresses configured and sets _pr_have_inet_if
+ * and _pr_have_inet6_if accordingly.
+ *
+ * We have an implementation using SIOCGIFCONF ioctl and a
+ * default implementation that simply sets _pr_have_inet_if
+ * and _pr_have_inet6_if to true. A better implementation
+ * would be to use the routing sockets (see Chapter 17 of
+ * W. Richard Stevens' Unix Network Programming, Vol. 1, 2nd. Ed.)
+ */
+
+static PRLock *_pr_query_ifs_lock = NULL;
+static PRBool _pr_have_inet_if = PR_FALSE;
+static PRBool _pr_have_inet6_if = PR_FALSE;
+
+#undef DEBUG_QUERY_IFS
+
+#if defined(AIX) \
+ || (defined(DARWIN) && !defined(HAVE_GETIFADDRS))
+
+/*
+ * Use SIOCGIFCONF ioctl on platforms that don't have routing
+ * sockets. Warning: whether SIOCGIFCONF ioctl returns AF_INET6
+ * network interfaces is not portable.
+ *
+ * The _pr_QueryNetIfs() function is derived from the code in
+ * src/lib/libc/net/getifaddrs.c in BSD Unix and the code in
+ * Section 16.6 of W. Richard Stevens' Unix Network Programming,
+ * Vol. 1, 2nd. Ed.
+ */
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#ifdef DEBUG_QUERY_IFS
+static void
+_pr_PrintIfreq(struct ifreq *ifr)
+{
+ PRNetAddr addr;
+ struct sockaddr *sa;
+ const char* family;
+ char addrstr[64];
+
+ sa = &ifr->ifr_addr;
+ if (sa->sa_family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+ family = "inet";
+ memcpy(&addr.inet.ip, &sin->sin_addr, sizeof(sin->sin_addr));
+ } else if (sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+ family = "inet6";
+ memcpy(&addr.ipv6.ip, &sin6->sin6_addr, sizeof(sin6->sin6_addr));
+ } else {
+ return; /* skip if not AF_INET or AF_INET6 */
+ }
+ addr.raw.family = sa->sa_family;
+ PR_NetAddrToString(&addr, addrstr, sizeof(addrstr));
+ printf("%s: %s %s\n", ifr->ifr_name, family, addrstr);
+}
+#endif
+
+static void
+_pr_QueryNetIfs(void)
+{
+ int sock;
+ int rv;
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ struct ifreq *lifr;
+ PRUint32 len, lastlen;
+ char *buf;
+
+ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+ return;
+ }
+
+ /* Issue SIOCGIFCONF request in a loop. */
+ lastlen = 0;
+ len = 100 * sizeof(struct ifreq); /* initial buffer size guess */
+ for (;;) {
+ buf = (char *)PR_Malloc(len);
+ if (NULL == buf) {
+ close(sock);
+ return;
+ }
+ ifc.ifc_buf = buf;
+ ifc.ifc_len = len;
+ rv = ioctl(sock, SIOCGIFCONF, &ifc);
+ if (rv < 0) {
+ if (errno != EINVAL || lastlen != 0) {
+ close(sock);
+ PR_Free(buf);
+ return;
+ }
+ } else {
+ if (ifc.ifc_len == lastlen) {
+ break; /* success, len has not changed */
+ }
+ lastlen = ifc.ifc_len;
+ }
+ len += 10 * sizeof(struct ifreq); /* increment */
+ PR_Free(buf);
+ }
+ close(sock);
+
+ ifr = ifc.ifc_req;
+ lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len];
+
+ while (ifr < lifr) {
+ struct sockaddr *sa;
+ int sa_len;
+
+#ifdef DEBUG_QUERY_IFS
+ _pr_PrintIfreq(ifr);
+#endif
+ sa = &ifr->ifr_addr;
+ if (sa->sa_family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *) sa;
+ if (sin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
+ _pr_have_inet_if = PR_TRUE;
+ }
+ } else if (sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
+ if (!IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)
+ && !IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
+ _pr_have_inet6_if = PR_TRUE;
+ }
+ }
+
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ sa_len = PR_MAX(sa->sa_len, sizeof(struct sockaddr));
+#else
+ switch (sa->sa_family) {
+#ifdef AF_LINK
+ case AF_LINK:
+ sa_len = sizeof(struct sockaddr_dl);
+ break;
+#endif
+ case AF_INET6:
+ sa_len = sizeof(struct sockaddr_in6);
+ break;
+ default:
+ sa_len = sizeof(struct sockaddr);
+ break;
+ }
+#endif
+ ifr = (struct ifreq *)(((char *)sa) + sa_len);
+ }
+ PR_Free(buf);
+}
+
+#elif (defined(DARWIN) && defined(HAVE_GETIFADDRS)) || defined(FREEBSD) \
+ || defined(NETBSD) || defined(OPENBSD)
+
+/*
+ * Use the BSD getifaddrs function.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <ifaddrs.h>
+#include <netinet/in.h>
+
+#ifdef DEBUG_QUERY_IFS
+static void
+_pr_PrintIfaddrs(struct ifaddrs *ifa)
+{
+ struct sockaddr *sa;
+ const char* family;
+ void *addrp;
+ char addrstr[64];
+
+ sa = ifa->ifa_addr;
+ if (sa->sa_family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+ family = "inet";
+ addrp = &sin->sin_addr;
+ } else if (sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+ family = "inet6";
+ addrp = &sin6->sin6_addr;
+ } else {
+ return; /* skip if not AF_INET or AF_INET6 */
+ }
+ inet_ntop(sa->sa_family, addrp, addrstr, sizeof(addrstr));
+ printf("%s: %s %s\n", ifa->ifa_name, family, addrstr);
+}
+#endif
+
+static void
+_pr_QueryNetIfs(void)
+{
+ struct ifaddrs *ifp;
+ struct ifaddrs *ifa;
+
+ if (getifaddrs(&ifp) == -1) {
+ return;
+ }
+ for (ifa = ifp; ifa; ifa = ifa->ifa_next) {
+ struct sockaddr *sa;
+
+#ifdef DEBUG_QUERY_IFS
+ _pr_PrintIfaddrs(ifa);
+#endif
+ sa = ifa->ifa_addr;
+ if (sa->sa_family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *) sa;
+ if (sin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
+ _pr_have_inet_if = 1;
+ }
+ } else if (sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
+ if (!IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)
+ && !IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
+ _pr_have_inet6_if = 1;
+ }
+ }
+ }
+ freeifaddrs(ifp);
+}
+
+#else /* default */
+
+/*
+ * Emulate the code in NSPR 4.2 or older. PR_GetIPNodeByName behaves
+ * as if the system had both IPv4 and IPv6 source addresses configured.
+ */
+static void
+_pr_QueryNetIfs(void)
+{
+ _pr_have_inet_if = PR_TRUE;
+ _pr_have_inet6_if = PR_TRUE;
+}
+
+#endif
+
+#endif /* _PR_INET6 && _PR_HAVE_GETHOSTBYNAME2 */
+
+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
+#endif
+#if !defined(_PR_NO_DNS_LOCK)
+ _pr_dnsLock = PR_NewLock();
+#endif
+#if !defined(_PR_HAVE_GETPROTO_R)
+ _getproto_lock = PR_NewLock();
+#endif
+#if defined(_PR_INET6) && defined(_PR_HAVE_GETHOSTBYNAME2)
+ _pr_query_ifs_lock = PR_NewLock();
+#endif
+}
+
+void _PR_CleanupNet(void)
+{
+#if !defined(_PR_NO_DNS_LOCK)
+ if (_pr_dnsLock) {
+ PR_DestroyLock(_pr_dnsLock);
+ _pr_dnsLock = NULL;
+ }
+#endif
+#if !defined(_PR_HAVE_GETPROTO_R)
+ if (_getproto_lock) {
+ PR_DestroyLock(_getproto_lock);
+ _getproto_lock = NULL;
+ }
+#endif
+#if defined(_PR_INET6) && defined(_PR_HAVE_GETHOSTBYNAME2)
+ if (_pr_query_ifs_lock) {
+ PR_DestroyLock(_pr_query_ifs_lock);
+ _pr_query_ifs_lock = NULL;
+ }
+#endif
+}
+
+/*
+** 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;
+}
+
+typedef enum _PRIPAddrConversion {
+ _PRIPAddrNoConversion,
+ _PRIPAddrIPv4Mapped,
+ _PRIPAddrIPv4Compat
+} _PRIPAddrConversion;
+
+/*
+** Convert an IPv4 address (v4) to an IPv4-mapped IPv6 address (v6).
+*/
+static void MakeIPv4MappedAddr(const char *v4, char *v6)
+{
+ memset(v6, 0, 10);
+ memset(v6 + 10, 0xff, 2);
+ memcpy(v6 + 12, v4, 4);
+}
+
+/*
+** Convert an IPv4 address (v4) to an IPv4-compatible IPv6 address (v6).
+*/
+static void MakeIPv4CompatAddr(const char *v4, char *v6)
+{
+ memset(v6, 0, 12);
+ memcpy(v6 + 12, v4, 4);
+}
+
+/*
+** 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,
+ _PRIPAddrConversion conversion,
+ PRHostEnt *to)
+{
+ PRIntn len, na;
+ char **ap;
+
+ if (conversion != _PRIPAddrNoConversion
+ && from->h_addrtype == AF_INET) {
+ PR_ASSERT(from->h_length == 4);
+ to->h_addrtype = PR_AF_INET6;
+ to->h_length = 16;
+ } else {
+#if defined(_PR_INET6) || defined(_PR_INET6_PROBE)
+ if (AF_INET6 == from->h_addrtype) {
+ to->h_addrtype = PR_AF_INET6;
+ }
+ else
+#endif
+ 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 */
+ if (!from->h_aliases) {
+ na = 1;
+ } else {
+ 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 */
+ if (!from->h_aliases) {
+ to->h_aliases[0] = 0;
+ } else {
+ 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;
+ }
+ if (conversion != _PRIPAddrNoConversion
+ && from->h_addrtype == AF_INET) {
+ if (conversion == _PRIPAddrIPv4Mapped) {
+ MakeIPv4MappedAddr(*ap, to->h_addr_list[na]);
+ } else {
+ PR_ASSERT(conversion == _PRIPAddrIPv4Compat);
+ MakeIPv4CompatAddr(*ap, to->h_addr_list[na]);
+ }
+ } else {
+ 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) */
+
+/*
+ * #################################################################
+ * NOTE: tmphe, tmpbuf, bufsize, h, and h_err are local variables
+ * or arguments of PR_GetHostByName, PR_GetIPNodeByName, and
+ * PR_GetHostByAddr. DO NOT CHANGE THE NAMES OF THESE LOCAL
+ * VARIABLES OR ARGUMENTS.
+ * #################################################################
+ */
+#if defined(_PR_HAVE_GETHOST_R_INT)
+
+#define GETHOSTBYNAME(name) \
+ (gethostbyname_r(name, &tmphe, tmpbuf, bufsize, &h, &h_err), h)
+#define GETHOSTBYNAME2(name, af) \
+ (gethostbyname2_r(name, af, &tmphe, tmpbuf, bufsize, &h, &h_err), h)
+#define GETHOSTBYADDR(addr, addrlen, af) \
+ (gethostbyaddr_r(addr, addrlen, af, \
+ &tmphe, tmpbuf, bufsize, &h, &h_err), h)
+
+#elif defined(_PR_HAVE_GETHOST_R_POINTER)
+
+#define GETHOSTBYNAME(name) \
+ gethostbyname_r(name, &tmphe, tmpbuf, bufsize, &h_err)
+#define GETHOSTBYNAME2(name, af) \
+ gethostbyname2_r(name, af, &tmphe, tmpbuf, bufsize, &h_err)
+#define GETHOSTBYADDR(addr, addrlen, af) \
+ gethostbyaddr_r(addr, addrlen, af, &tmphe, tmpbuf, bufsize, &h_err)
+
+#else
+
+#define GETHOSTBYNAME(name) gethostbyname(name)
+#define GETHOSTBYNAME2(name, af) gethostbyname2(name, af)
+#define GETHOSTBYADDR(addr, addrlen, af) gethostbyaddr(addr, addrlen, af)
+
+#endif /* definition of GETHOSTBYXXX */
+
+PR_IMPLEMENT(PRStatus) PR_GetHostByName(
+ const char *name, char *buf, PRIntn bufsize, PRHostEnt *hp)
+{
+ struct hostent *h;
+ PRStatus rv = PR_FAILURE;
+#if defined(_PR_HAVE_GETHOST_R)
+ char localbuf[PR_NETDB_BUF_SIZE];
+ char *tmpbuf;
+ struct hostent tmphe;
+ int h_err;
+#endif
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+#if defined(_PR_HAVE_GETHOST_R)
+ tmpbuf = localbuf;
+ if (bufsize > sizeof(localbuf))
+ {
+ tmpbuf = (char *)PR_Malloc(bufsize);
+ if (NULL == tmpbuf)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return rv;
+ }
+ }
+#endif
+
+ LOCK_DNS();
+
+ h = GETHOSTBYNAME(name);
+
+ if (NULL == h)
+ {
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO());
+ }
+ else
+ {
+ _PRIPAddrConversion conversion = _PRIPAddrNoConversion;
+ rv = CopyHostent(h, &buf, &bufsize, conversion, hp);
+ if (PR_SUCCESS != rv) {
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ }
+ }
+ UNLOCK_DNS();
+#if defined(_PR_HAVE_GETHOST_R)
+ if (tmpbuf != localbuf) {
+ PR_Free(tmpbuf);
+ }
+#endif
+ return rv;
+}
+
+#if !defined(_PR_INET6) && \
+ defined(_PR_INET6_PROBE) && defined(_PR_HAVE_GETIPNODEBYNAME)
+typedef struct hostent * (*_pr_getipnodebyname_t)(const char *, int,
+ int, int *);
+typedef struct hostent * (*_pr_getipnodebyaddr_t)(const void *, size_t,
+ int, int *);
+typedef void (*_pr_freehostent_t)(struct hostent *);
+static void * _pr_getipnodebyname_fp;
+static void * _pr_getipnodebyaddr_fp;
+static void * _pr_freehostent_fp;
+
+/*
+ * Look up the addresses of getipnodebyname, getipnodebyaddr,
+ * and freehostent.
+ */
+PRStatus
+_pr_find_getipnodebyname(void)
+{
+ PRLibrary *lib;
+ PRStatus rv;
+#define GETIPNODEBYNAME "getipnodebyname"
+#define GETIPNODEBYADDR "getipnodebyaddr"
+#define FREEHOSTENT "freehostent"
+
+ _pr_getipnodebyname_fp = PR_FindSymbolAndLibrary(GETIPNODEBYNAME, &lib);
+ if (NULL != _pr_getipnodebyname_fp) {
+ _pr_freehostent_fp = PR_FindSymbol(lib, FREEHOSTENT);
+ if (NULL != _pr_freehostent_fp) {
+ _pr_getipnodebyaddr_fp = PR_FindSymbol(lib, GETIPNODEBYADDR);
+ if (NULL != _pr_getipnodebyaddr_fp) {
+ rv = PR_SUCCESS;
+ }
+ else {
+ rv = PR_FAILURE;
+ }
+ } else {
+ rv = PR_FAILURE;
+ }
+ (void)PR_UnloadLibrary(lib);
+ } else {
+ rv = PR_FAILURE;
+ }
+ return rv;
+}
+#endif
+
+#if defined(_PR_INET6) && defined(_PR_HAVE_GETHOSTBYNAME2)
+/*
+** Append the V4 addresses to the end of the list
+*/
+static PRStatus AppendV4AddrsToHostent(
+ struct hostent *from,
+ char **buf,
+ PRIntn *bufsize,
+ PRHostEnt *to)
+{
+ PRIntn na, na_old;
+ char **ap;
+ char **new_addr_list;
+
+ /* Count the addresses, then grow storage for the pointers */
+ for (na_old = 0, ap = to->h_addr_list; *ap != 0; na_old++, ap++)
+ {;} /* nothing to execute */
+ for (na = na_old + 1, ap = from->h_addr_list; *ap != 0; na++, ap++)
+ {;} /* nothing to execute */
+ new_addr_list = (char**)Alloc(
+ na * sizeof(char*), buf, bufsize, sizeof(char**));
+ if (!new_addr_list) {
+ return PR_FAILURE;
+ }
+
+ /* Copy the V6 addresses, one at a time */
+ for (na = 0, ap = to->h_addr_list; *ap != 0; na++, ap++) {
+ new_addr_list[na] = to->h_addr_list[na];
+ }
+ to->h_addr_list = new_addr_list;
+
+ /* Copy the V4 addresses, one at a time */
+ for (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;
+ }
+ MakeIPv4MappedAddr(*ap, to->h_addr_list[na]);
+ }
+ to->h_addr_list[na] = 0;
+ return PR_SUCCESS;
+}
+#endif
+
+PR_IMPLEMENT(PRStatus) PR_GetIPNodeByName(
+ const char *name, PRUint16 af, PRIntn flags,
+ char *buf, PRIntn bufsize, PRHostEnt *hp)
+{
+ struct hostent *h = 0;
+ PRStatus rv = PR_FAILURE;
+#if defined(_PR_HAVE_GETHOST_R)
+ char localbuf[PR_NETDB_BUF_SIZE];
+ char *tmpbuf;
+ struct hostent tmphe;
+ int h_err;
+#endif
+#if defined(_PR_HAVE_GETIPNODEBYNAME)
+ PRUint16 md_af = af;
+ int error_num;
+ int tmp_flags = 0;
+#endif
+#if defined(_PR_HAVE_GETHOSTBYNAME2)
+ PRBool did_af_inet = PR_FALSE;
+#endif
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (af != PR_AF_INET && af != PR_AF_INET6) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+#if defined(_PR_INET6) && defined(_PR_HAVE_GETHOSTBYNAME2)
+ PR_Lock(_pr_query_ifs_lock);
+ /*
+ * Keep querying the presence of IPv4 and IPv6 interfaces until
+ * at least one is up. This allows us to detect the local
+ * machine going from offline to online.
+ */
+ if (!_pr_have_inet_if && !_pr_have_inet6_if) {
+ _pr_QueryNetIfs();
+#ifdef DEBUG_QUERY_IFS
+ if (_pr_have_inet_if) {
+ printf("Have IPv4 source address\n");
+ }
+ if (_pr_have_inet6_if) {
+ printf("Have IPv6 source address\n");
+ }
+#endif
+ }
+ PR_Unlock(_pr_query_ifs_lock);
+#endif
+
+#if defined(_PR_HAVE_GETIPNODEBYNAME)
+ if (flags & PR_AI_V4MAPPED) {
+ tmp_flags |= AI_V4MAPPED;
+ }
+ if (flags & PR_AI_ADDRCONFIG) {
+ tmp_flags |= AI_ADDRCONFIG;
+ }
+ if (flags & PR_AI_ALL) {
+ tmp_flags |= AI_ALL;
+ }
+ if (af == PR_AF_INET6) {
+ md_af = AF_INET6;
+ }
+ else {
+ md_af = af;
+ }
+#endif
+
+#if defined(_PR_HAVE_GETHOST_R)
+ tmpbuf = localbuf;
+ if (bufsize > sizeof(localbuf))
+ {
+ tmpbuf = (char *)PR_Malloc(bufsize);
+ if (NULL == tmpbuf)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return rv;
+ }
+ }
+#endif
+
+ /* Do not need to lock the DNS lock if getipnodebyname() is called */
+#ifdef _PR_INET6
+#ifdef _PR_HAVE_GETHOSTBYNAME2
+ LOCK_DNS();
+ if (af == PR_AF_INET6)
+ {
+ if ((flags & PR_AI_ADDRCONFIG) == 0 || _pr_have_inet6_if)
+ {
+#ifdef _PR_INET6_PROBE
+ if (_pr_ipv6_is_present())
+#endif
+ h = GETHOSTBYNAME2(name, AF_INET6);
+ }
+ if ((NULL == h) && (flags & PR_AI_V4MAPPED)
+ && ((flags & PR_AI_ADDRCONFIG) == 0 || _pr_have_inet_if))
+ {
+ did_af_inet = PR_TRUE;
+ h = GETHOSTBYNAME2(name, AF_INET);
+ }
+ }
+ else
+ {
+ if ((flags & PR_AI_ADDRCONFIG) == 0 || _pr_have_inet_if)
+ {
+ did_af_inet = PR_TRUE;
+ h = GETHOSTBYNAME2(name, af);
+ }
+ }
+#elif defined(_PR_HAVE_GETIPNODEBYNAME)
+ h = getipnodebyname(name, md_af, tmp_flags, &error_num);
+#else
+#error "Unknown name-to-address translation function"
+#endif /* _PR_HAVE_GETHOSTBYNAME2 */
+#elif defined(_PR_INET6_PROBE) && defined(_PR_HAVE_GETIPNODEBYNAME)
+ if (_pr_ipv6_is_present())
+ {
+#ifdef PR_GETIPNODE_NOT_THREADSAFE
+ LOCK_DNS();
+#endif
+ h = (*((_pr_getipnodebyname_t)_pr_getipnodebyname_fp))(name, md_af, tmp_flags, &error_num);
+ }
+ else
+ {
+ LOCK_DNS();
+ h = GETHOSTBYNAME(name);
+ }
+#else /* _PR_INET6 */
+ LOCK_DNS();
+ h = GETHOSTBYNAME(name);
+#endif /* _PR_INET6 */
+
+ if (NULL == h)
+ {
+#if defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME)
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, error_num);
+#elif defined(_PR_INET6_PROBE) && defined(_PR_HAVE_GETIPNODEBYNAME)
+ if (_pr_ipv6_is_present()) {
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, error_num);
+ }
+ else {
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO());
+ }
+#else
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO());
+#endif
+ }
+ else
+ {
+ _PRIPAddrConversion conversion = _PRIPAddrNoConversion;
+
+ if (af == PR_AF_INET6) {
+ conversion = _PRIPAddrIPv4Mapped;
+ }
+ rv = CopyHostent(h, &buf, &bufsize, conversion, hp);
+ if (PR_SUCCESS != rv) {
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ }
+#if defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME)
+ freehostent(h);
+#elif defined(_PR_INET6_PROBE) && defined(_PR_HAVE_GETIPNODEBYNAME)
+ if (_pr_ipv6_is_present()) {
+ (*((_pr_freehostent_t)_pr_freehostent_fp))(h);
+ }
+#endif
+#if defined(_PR_INET6) && defined(_PR_HAVE_GETHOSTBYNAME2)
+ if ((PR_SUCCESS == rv) && (flags & PR_AI_V4MAPPED)
+ && ((flags & PR_AI_ALL)
+ || ((flags & PR_AI_ADDRCONFIG) && _pr_have_inet_if))
+ && !did_af_inet && (h = GETHOSTBYNAME2(name, AF_INET)) != 0) {
+ rv = AppendV4AddrsToHostent(h, &buf, &bufsize, hp);
+ if (PR_SUCCESS != rv) {
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ }
+ }
+#endif
+ }
+
+ /* Must match the convoluted logic above for LOCK_DNS() */
+#ifdef _PR_INET6
+#ifdef _PR_HAVE_GETHOSTBYNAME2
+ UNLOCK_DNS();
+#endif /* _PR_HAVE_GETHOSTBYNAME2 */
+#elif defined(_PR_INET6_PROBE) && defined(_PR_HAVE_GETIPNODEBYNAME)
+#ifdef PR_GETIPNODE_NOT_THREADSAFE
+ UNLOCK_DNS();
+#else
+ if (!_pr_ipv6_is_present()) {
+ UNLOCK_DNS();
+ }
+#endif
+#else /* _PR_INET6 */
+ UNLOCK_DNS();
+#endif /* _PR_INET6 */
+
+#if defined(_PR_HAVE_GETHOST_R)
+ if (tmpbuf != localbuf) {
+ PR_Free(tmpbuf);
+ }
+#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;
+ PRUint32 tmp_ip;
+ int addrlen;
+ PRInt32 af;
+#if defined(_PR_HAVE_GETHOST_R)
+ char localbuf[PR_NETDB_BUF_SIZE];
+ char *tmpbuf;
+ struct hostent tmphe;
+ int h_err;
+#endif
+#if defined(_PR_HAVE_GETIPNODEBYADDR)
+ int error_num;
+#endif
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (hostaddr->raw.family == PR_AF_INET6)
+ {
+#if defined(_PR_INET6_PROBE)
+ af = _pr_ipv6_is_present() ? AF_INET6 : AF_INET;
+#elif defined(_PR_INET6)
+ af = AF_INET6;
+#else
+ af = AF_INET;
+#endif
+#if defined(_PR_GHBA_DISALLOW_V4MAPPED)
+ if (_PR_IN6_IS_ADDR_V4MAPPED(&hostaddr->ipv6.ip)) {
+ af = AF_INET;
+ }
+#endif
+ }
+ else
+ {
+ PR_ASSERT(hostaddr->raw.family == AF_INET);
+ af = AF_INET;
+ }
+ if (hostaddr->raw.family == PR_AF_INET6) {
+#if defined(_PR_INET6) || defined(_PR_INET6_PROBE)
+ if (af == AF_INET6) {
+ addr = &hostaddr->ipv6.ip;
+ addrlen = sizeof(hostaddr->ipv6.ip);
+ }
+ else
+#endif
+ {
+ PR_ASSERT(af == AF_INET);
+ if (!_PR_IN6_IS_ADDR_V4MAPPED(&hostaddr->ipv6.ip)) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return rv;
+ }
+ tmp_ip = _PR_IN6_V4MAPPED_TO_IPADDR((PRIPv6Addr *)
+ &hostaddr->ipv6.ip);
+ addr = &tmp_ip;
+ addrlen = sizeof(tmp_ip);
+ }
+ } else {
+ PR_ASSERT(hostaddr->raw.family == AF_INET);
+ PR_ASSERT(af == AF_INET);
+ addr = &hostaddr->inet.ip;
+ addrlen = sizeof(hostaddr->inet.ip);
+ }
+
+#if defined(_PR_HAVE_GETHOST_R)
+ tmpbuf = localbuf;
+ if (bufsize > sizeof(localbuf))
+ {
+ tmpbuf = (char *)PR_Malloc(bufsize);
+ if (NULL == tmpbuf)
+ {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return rv;
+ }
+ }
+#endif
+
+ /* Do not need to lock the DNS lock if getipnodebyaddr() is called */
+#if defined(_PR_HAVE_GETIPNODEBYADDR) && defined(_PR_INET6)
+ h = getipnodebyaddr(addr, addrlen, af, &error_num);
+#elif defined(_PR_HAVE_GETIPNODEBYADDR) && defined(_PR_INET6_PROBE)
+ if (_pr_ipv6_is_present())
+ {
+#ifdef PR_GETIPNODE_NOT_THREADSAFE
+ LOCK_DNS();
+#endif
+ h = (*((_pr_getipnodebyaddr_t)_pr_getipnodebyaddr_fp))(addr, addrlen,
+ af, &error_num);
+ }
+ else
+ {
+ LOCK_DNS();
+ h = GETHOSTBYADDR(addr, addrlen, af);
+ }
+#else /* _PR_HAVE_GETIPNODEBYADDR */
+ LOCK_DNS();
+ h = GETHOSTBYADDR(addr, addrlen, af);
+#endif /* _PR_HAVE_GETIPNODEBYADDR */
+ if (NULL == h)
+ {
+#if defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYADDR)
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, error_num);
+#elif defined(_PR_INET6_PROBE) && defined(_PR_HAVE_GETIPNODEBYADDR)
+ if (_pr_ipv6_is_present()) {
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, error_num);
+ }
+ else {
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO());
+ }
+#else
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO());
+#endif
+ }
+ else
+ {
+ _PRIPAddrConversion conversion = _PRIPAddrNoConversion;
+ if (hostaddr->raw.family == PR_AF_INET6) {
+ if (af == AF_INET) {
+ if (_PR_IN6_IS_ADDR_V4MAPPED((PRIPv6Addr*)
+ &hostaddr->ipv6.ip)) {
+ conversion = _PRIPAddrIPv4Mapped;
+ } else if (_PR_IN6_IS_ADDR_V4COMPAT((PRIPv6Addr *)
+ &hostaddr->ipv6.ip)) {
+ conversion = _PRIPAddrIPv4Compat;
+ }
+ }
+ }
+ rv = CopyHostent(h, &buf, &bufsize, conversion, hostentry);
+ if (PR_SUCCESS != rv) {
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ }
+#if defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYADDR)
+ freehostent(h);
+#elif defined(_PR_INET6_PROBE) && defined(_PR_HAVE_GETIPNODEBYADDR)
+ if (_pr_ipv6_is_present()) {
+ (*((_pr_freehostent_t)_pr_freehostent_fp))(h);
+ }
+#endif
+ }
+
+ /* Must match the convoluted logic above for LOCK_DNS() */
+#if defined(_PR_HAVE_GETIPNODEBYADDR) && defined(_PR_INET6)
+#elif defined(_PR_HAVE_GETIPNODEBYADDR) && defined(_PR_INET6_PROBE)
+#ifdef PR_GETIPNODE_NOT_THREADSAFE
+ UNLOCK_DNS();
+#else
+ if (!_pr_ipv6_is_present()) {
+ UNLOCK_DNS();
+ }
+#endif
+#else /* _PR_HAVE_GETIPNODEBYADDR */
+ UNLOCK_DNS();
+#endif /* _PR_HAVE_GETIPNODEBYADDR */
+
+#if defined(_PR_HAVE_GETHOST_R)
+ if (tmpbuf != localbuf) {
+ PR_Free(tmpbuf);
+ }
+#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.
+ */
+
+#if defined(ANDROID)
+/* Android's Bionic libc system includes prototypes for these in netdb.h,
+ * but doesn't actually include implementations. It uses the 5-arg form,
+ * so these functions end up not matching the prototype. So just rename
+ * them if not found.
+ */
+#define getprotobyname_r _pr_getprotobyname_r
+#define getprotobynumber_r _pr_getprotobynumber_r
+#endif
+
+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) */
+
+ 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) */
+
+ 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;
+
+}
+
+PRUintn _PR_NetAddrSize(const PRNetAddr* addr)
+{
+ PRUintn addrsize;
+
+ /*
+ * RFC 2553 added a new field (sin6_scope_id) to
+ * struct sockaddr_in6. PRNetAddr's ipv6 member has a
+ * scope_id field to match the new field. In order to
+ * work with older implementations supporting RFC 2133,
+ * we take the size of struct sockaddr_in6 instead of
+ * addr->ipv6.
+ */
+ if (AF_INET == addr->raw.family) {
+ addrsize = sizeof(addr->inet);
+ }
+ else if (PR_AF_INET6 == addr->raw.family)
+#if defined(_PR_INET6)
+ addrsize = sizeof(struct sockaddr_in6);
+#else
+ addrsize = sizeof(addr->ipv6);
+#endif
+#if defined(XP_UNIX) || defined(XP_OS2)
+ else if (AF_UNIX == addr->raw.family)
+ {
+#if defined(LINUX)
+ if (addr->local.path[0] == 0)
+ /* abstract socket address is supported on Linux only */
+ addrsize = strnlen(addr->local.path + 1,
+ sizeof(addr->local.path)) +
+ offsetof(struct sockaddr_un, sun_path) + 1;
+ else
+#endif
+ 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
+ {
+ address->raw.family = hostEnt->h_addrtype;
+ if (PR_AF_INET6 == hostEnt->h_addrtype)
+ {
+ address->ipv6.port = htons(port);
+ address->ipv6.flowinfo = 0;
+ address->ipv6.scope_id = 0;
+ memcpy(&address->ipv6.ip, addr, hostEnt->h_length);
+ }
+ else
+ {
+ PR_ASSERT(AF_INET == 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 (val != PR_IpAddrNull) {
+ memset(addr, 0, sizeof(*addr));
+ }
+ 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(PRStatus) PR_SetNetAddr(
+ PRNetAddrValue val, PRUint16 af, PRUint16 port, PRNetAddr *addr)
+{
+ PRStatus rv = PR_SUCCESS;
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (af == PR_AF_INET6)
+ {
+ if (val != PR_IpAddrNull) {
+ memset(addr, 0, sizeof(addr->ipv6));
+ }
+ addr->ipv6.family = af;
+ addr->ipv6.port = htons(port);
+ addr->ipv6.flowinfo = 0;
+ addr->ipv6.scope_id = 0;
+ switch (val)
+ {
+ case PR_IpAddrNull:
+ break; /* don't overwrite the address */
+ case PR_IpAddrAny:
+ addr->ipv6.ip = _pr_in6addr_any;
+ break;
+ case PR_IpAddrLoopback:
+ addr->ipv6.ip = _pr_in6addr_loopback;
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ rv = PR_FAILURE;
+ }
+ }
+ else
+ {
+ if (val != PR_IpAddrNull) {
+ memset(addr, 0, sizeof(addr->inet));
+ }
+ addr->inet.family = af;
+ 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_SetNetAddr */
+
+PR_IMPLEMENT(PRBool)
+PR_IsNetAddrType(const PRNetAddr *addr, PRNetAddrValue val)
+{
+ if (addr->raw.family == PR_AF_INET6) {
+ if (val == PR_IpAddrAny) {
+ if (_PR_IN6_IS_ADDR_UNSPECIFIED((PRIPv6Addr *)&addr->ipv6.ip)) {
+ return PR_TRUE;
+ }
+ if (_PR_IN6_IS_ADDR_V4MAPPED((PRIPv6Addr *)&addr->ipv6.ip)
+ && _PR_IN6_V4MAPPED_TO_IPADDR((PRIPv6Addr *)&addr->ipv6.ip)
+ == htonl(INADDR_ANY)) {
+ return PR_TRUE;
+ }
+ } else if (val == PR_IpAddrLoopback) {
+ if (_PR_IN6_IS_ADDR_LOOPBACK((PRIPv6Addr *)&addr->ipv6.ip)) {
+ return PR_TRUE;
+ }
+ if (_PR_IN6_IS_ADDR_V4MAPPED((PRIPv6Addr *)&addr->ipv6.ip)
+ && _PR_IN6_V4MAPPED_TO_IPADDR((PRIPv6Addr *)&addr->ipv6.ip)
+ == htonl(INADDR_LOOPBACK)) {
+ return PR_TRUE;
+ }
+ } else if (val == PR_IpAddrV4Mapped
+ && _PR_IN6_IS_ADDR_V4MAPPED((PRIPv6Addr *)&addr->ipv6.ip)) {
+ return PR_TRUE;
+ }
+ } else {
+ if (addr->raw.family == AF_INET) {
+ if (val == PR_IpAddrAny && addr->inet.ip == htonl(INADDR_ANY)) {
+ return PR_TRUE;
+ }
+ if (val == PR_IpAddrLoopback
+ && addr->inet.ip == htonl(INADDR_LOOPBACK)) {
+ return PR_TRUE;
+ }
+ }
+ }
+ return PR_FALSE;
+}
+
+extern int pr_inet_aton(const char *cp, PRUint32 *addr);
+
+#define XX 127
+static const unsigned char index_hex[256] = {
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,XX,XX, XX,XX,XX,XX,
+ XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+};
+
+/*
+ * StringToV6Addr() returns 1 if the conversion succeeds,
+ * or 0 if the input is not a valid IPv6 address string.
+ * (Same as inet_pton(AF_INET6, string, addr).)
+ */
+static int StringToV6Addr(const char *string, PRIPv6Addr *addr)
+{
+ const unsigned char *s = (const unsigned char *)string;
+ int section = 0; /* index of the current section (a 16-bit
+ * piece of the address */
+ int double_colon = -1; /* index of the section after the first
+ * 16-bit group of zeros represented by
+ * the double colon */
+ unsigned int val;
+ int len;
+
+ /* Handle initial (double) colon */
+ if (*s == ':') {
+ if (s[1] != ':') {
+ return 0;
+ }
+ s += 2;
+ addr->pr_s6_addr16[0] = 0;
+ section = double_colon = 1;
+ }
+
+ while (*s) {
+ if (section == 8) {
+ return 0; /* too long */
+ }
+ if (*s == ':') {
+ if (double_colon != -1) {
+ return 0; /* two double colons */
+ }
+ addr->pr_s6_addr16[section++] = 0;
+ double_colon = section;
+ s++;
+ continue;
+ }
+ for (len = val = 0; len < 4 && index_hex[*s] != XX; len++) {
+ val = (val << 4) + index_hex[*s++];
+ }
+ if (*s == '.') {
+ if (len == 0) {
+ return 0; /* nothing between : and . */
+ }
+ break;
+ }
+ if (*s == ':') {
+ s++;
+ if (!*s) {
+ return 0; /* cannot end with single colon */
+ }
+ } else if (*s) {
+ return 0; /* bad character */
+ }
+ addr->pr_s6_addr16[section++] = htons((unsigned short)val);
+ }
+
+ if (*s == '.') {
+ /* Have a trailing v4 format address */
+ if (section > 6) {
+ return 0; /* not enough room */
+ }
+
+ /*
+ * The number before the '.' is decimal, but we parsed it
+ * as hex. That means it is in BCD. Check it for validity
+ * and convert it to binary.
+ */
+ if (val > 0x0255 || (val & 0xf0) > 0x90 || (val & 0xf) > 9) {
+ return 0;
+ }
+ val = (val >> 8) * 100 + ((val >> 4) & 0xf) * 10 + (val & 0xf);
+ addr->pr_s6_addr[2 * section] = val;
+
+ s++;
+ val = index_hex[*s++];
+ if (val > 9) {
+ return 0;
+ }
+ while (*s >= '0' && *s <= '9') {
+ val = val * 10 + *s++ - '0';
+ if (val > 255) {
+ return 0;
+ }
+ }
+ if (*s != '.') {
+ return 0; /* must have exactly 4 decimal numbers */
+ }
+ addr->pr_s6_addr[2 * section + 1] = val;
+ section++;
+
+ s++;
+ val = index_hex[*s++];
+ if (val > 9) {
+ return 0;
+ }
+ while (*s >= '0' && *s <= '9') {
+ val = val * 10 + *s++ - '0';
+ if (val > 255) {
+ return 0;
+ }
+ }
+ if (*s != '.') {
+ return 0; /* must have exactly 4 decimal numbers */
+ }
+ addr->pr_s6_addr[2 * section] = val;
+
+ s++;
+ val = index_hex[*s++];
+ if (val > 9) {
+ return 0;
+ }
+ while (*s >= '0' && *s <= '9') {
+ val = val * 10 + *s++ - '0';
+ if (val > 255) {
+ return 0;
+ }
+ }
+ if (*s) {
+ return 0; /* must have exactly 4 decimal numbers */
+ }
+ addr->pr_s6_addr[2 * section + 1] = val;
+ section++;
+ }
+
+ if (double_colon != -1) {
+ /* Stretch the double colon */
+ int tosection;
+ int ncopy = section - double_colon;
+ for (tosection = 7; ncopy--; tosection--) {
+ addr->pr_s6_addr16[tosection] =
+ addr->pr_s6_addr16[double_colon + ncopy];
+ }
+ while (tosection >= double_colon) {
+ addr->pr_s6_addr16[tosection--] = 0;
+ }
+ } else if (section != 8) {
+ return 0; /* too short */
+ }
+ return 1;
+}
+#undef XX
+
+#ifndef _PR_HAVE_INET_NTOP
+static const char *basis_hex = "0123456789abcdef";
+
+/*
+ * V6AddrToString() returns a pointer to the buffer containing
+ * the text string if the conversion succeeds, and NULL otherwise.
+ * (Same as inet_ntop(AF_INET6, addr, buf, size), except that errno
+ * is not set on failure.)
+ */
+static const char *V6AddrToString(
+ const PRIPv6Addr *addr, char *buf, PRUint32 size)
+{
+#define STUFF(c) do { \
+ if (!size--) return NULL; \
+ *buf++ = (c); \
+} while (0)
+
+ int double_colon = -1; /* index of the first 16-bit
+ * group of zeros represented
+ * by the double colon */
+ int double_colon_length = 1; /* use double colon only if
+ * there are two or more 16-bit
+ * groups of zeros */
+ int zero_length;
+ int section;
+ unsigned int val;
+ const char *bufcopy = buf;
+
+ /* Scan to find the placement of the double colon */
+ for (section = 0; section < 8; section++) {
+ if (addr->pr_s6_addr16[section] == 0) {
+ zero_length = 1;
+ section++;
+ while (section < 8 && addr->pr_s6_addr16[section] == 0) {
+ zero_length++;
+ section++;
+ }
+ /* Select the longest sequence of zeros */
+ if (zero_length > double_colon_length) {
+ double_colon = section - zero_length;
+ double_colon_length = zero_length;
+ }
+ }
+ }
+
+ /* Now start converting to a string */
+ section = 0;
+
+ if (double_colon == 0) {
+ if (double_colon_length == 6 ||
+ (double_colon_length == 5 && addr->pr_s6_addr16[5] == 0xffff)) {
+ /* ipv4 format address */
+ STUFF(':');
+ STUFF(':');
+ if (double_colon_length == 5) {
+ STUFF('f');
+ STUFF('f');
+ STUFF('f');
+ STUFF('f');
+ STUFF(':');
+ }
+ if (addr->pr_s6_addr[12] > 99) {
+ STUFF(addr->pr_s6_addr[12]/100 + '0');
+ }
+ if (addr->pr_s6_addr[12] > 9) {
+ STUFF((addr->pr_s6_addr[12]%100)/10 + '0');
+ }
+ STUFF(addr->pr_s6_addr[12]%10 + '0');
+ STUFF('.');
+ if (addr->pr_s6_addr[13] > 99) {
+ STUFF(addr->pr_s6_addr[13]/100 + '0');
+ }
+ if (addr->pr_s6_addr[13] > 9) {
+ STUFF((addr->pr_s6_addr[13]%100)/10 + '0');
+ }
+ STUFF(addr->pr_s6_addr[13]%10 + '0');
+ STUFF('.');
+ if (addr->pr_s6_addr[14] > 99) {
+ STUFF(addr->pr_s6_addr[14]/100 + '0');
+ }
+ if (addr->pr_s6_addr[14] > 9) {
+ STUFF((addr->pr_s6_addr[14]%100)/10 + '0');
+ }
+ STUFF(addr->pr_s6_addr[14]%10 + '0');
+ STUFF('.');
+ if (addr->pr_s6_addr[15] > 99) {
+ STUFF(addr->pr_s6_addr[15]/100 + '0');
+ }
+ if (addr->pr_s6_addr[15] > 9) {
+ STUFF((addr->pr_s6_addr[15]%100)/10 + '0');
+ }
+ STUFF(addr->pr_s6_addr[15]%10 + '0');
+ STUFF('\0');
+ return bufcopy;
+ }
+ }
+
+ while (section < 8) {
+ if (section == double_colon) {
+ STUFF(':');
+ STUFF(':');
+ section += double_colon_length;
+ continue;
+ }
+ val = ntohs(addr->pr_s6_addr16[section]);
+ if (val > 0xfff) {
+ STUFF(basis_hex[val >> 12]);
+ }
+ if (val > 0xff) {
+ STUFF(basis_hex[(val >> 8) & 0xf]);
+ }
+ if (val > 0xf) {
+ STUFF(basis_hex[(val >> 4) & 0xf]);
+ }
+ STUFF(basis_hex[val & 0xf]);
+ section++;
+ if (section < 8 && section != double_colon) {
+ STUFF(':');
+ }
+ }
+ STUFF('\0');
+ return bufcopy;
+#undef STUFF
+}
+#endif /* !_PR_HAVE_INET_NTOP */
+
+/*
+ * Convert an IPv4 addr to an (IPv4-mapped) IPv6 addr
+ */
+PR_IMPLEMENT(void) PR_ConvertIPv4AddrToIPv6(PRUint32 v4addr, PRIPv6Addr *v6addr)
+{
+ PRUint8 *dstp;
+ dstp = v6addr->pr_s6_addr;
+ memset(dstp, 0, 10);
+ memset(dstp + 10, 0xff, 2);
+ memcpy(dstp + 12,(char *) &v4addr, 4);
+}
+
+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)
+{
+#ifdef IS_BIG_ENDIAN
+ return n;
+#else
+ PRUint32 hi, lo;
+ lo = (PRUint32)n;
+ hi = (PRUint32)(n >> 32);
+ hi = PR_ntohl(hi);
+ lo = PR_ntohl(lo);
+ return ((PRUint64)lo << 32) + (PRUint64)hi;
+#endif
+} /* ntohll */
+
+PR_IMPLEMENT(PRUint64) PR_htonll(PRUint64 n)
+{
+#ifdef IS_BIG_ENDIAN
+ return n;
+#else
+ PRUint32 hi, lo;
+ lo = (PRUint32)n;
+ hi = (PRUint32)(n >> 32);
+ hi = htonl(hi);
+ lo = htonl(lo);
+ return ((PRUint64)lo << 32) + (PRUint64)hi;
+#endif
+} /* htonll */
+
+
+/*
+ * Implementation of PR_GetAddrInfoByName and friends
+ *
+ * Compile-time options:
+ *
+ * _PR_HAVE_GETADDRINFO Define this macro if the target system provides
+ * getaddrinfo. With this defined, NSPR will require
+ * getaddrinfo at run time. If this if not defined,
+ * then NSPR will attempt to dynamically resolve
+ * getaddrinfo, falling back to PR_GetHostByName if
+ * getaddrinfo does not exist on the target system.
+ *
+ * Since getaddrinfo is a relatively new system call on many systems,
+ * we are forced to dynamically resolve it at run time in most cases.
+ * The exception includes any system (such as Mac OS X) that is known to
+ * provide getaddrinfo in all versions that NSPR cares to support.
+ */
+
+#if defined(_PR_HAVE_GETADDRINFO)
+
+#if defined(_PR_INET6)
+
+typedef struct addrinfo PRADDRINFO;
+#define GETADDRINFO getaddrinfo
+#define FREEADDRINFO freeaddrinfo
+#define GETNAMEINFO getnameinfo
+
+#elif defined(_PR_INET6_PROBE)
+
+typedef struct addrinfo PRADDRINFO;
+
+/* getaddrinfo/freeaddrinfo/getnameinfo prototypes */
+#if defined(WIN32)
+#define FUNC_MODIFIER __stdcall
+#else
+#define FUNC_MODIFIER
+#endif
+typedef int (FUNC_MODIFIER * FN_GETADDRINFO)
+(const char *nodename,
+ const char *servname,
+ const PRADDRINFO *hints,
+ PRADDRINFO **res);
+typedef int (FUNC_MODIFIER * FN_FREEADDRINFO)
+(PRADDRINFO *ai);
+typedef int (FUNC_MODIFIER * FN_GETNAMEINFO)
+(const struct sockaddr *addr, int addrlen,
+ char *host, int hostlen,
+ char *serv, int servlen, int flags);
+
+/* global state */
+static FN_GETADDRINFO _pr_getaddrinfo = NULL;
+static FN_FREEADDRINFO _pr_freeaddrinfo = NULL;
+static FN_GETNAMEINFO _pr_getnameinfo = NULL;
+
+#define GETADDRINFO_SYMBOL "getaddrinfo"
+#define FREEADDRINFO_SYMBOL "freeaddrinfo"
+#define GETNAMEINFO_SYMBOL "getnameinfo"
+
+PRStatus
+_pr_find_getaddrinfo(void)
+{
+ PRLibrary *lib;
+#ifdef WIN32
+ /*
+ * On windows, we need to search ws2_32.dll or wship6.dll
+ * (Microsoft IPv6 Technology Preview for Windows 2000) for
+ * getaddrinfo and freeaddrinfo. These libraries might not
+ * be loaded yet.
+ */
+ const char *libname[] = { "ws2_32.dll", "wship6.dll" };
+ int i;
+
+ for (i = 0; i < sizeof(libname)/sizeof(libname[0]); i++) {
+ lib = PR_LoadLibrary(libname[i]);
+ if (!lib) {
+ continue;
+ }
+ _pr_getaddrinfo = (FN_GETADDRINFO)
+ PR_FindFunctionSymbol(lib, GETADDRINFO_SYMBOL);
+ if (!_pr_getaddrinfo) {
+ PR_UnloadLibrary(lib);
+ continue;
+ }
+ _pr_freeaddrinfo = (FN_FREEADDRINFO)
+ PR_FindFunctionSymbol(lib, FREEADDRINFO_SYMBOL);
+ _pr_getnameinfo = (FN_GETNAMEINFO)
+ PR_FindFunctionSymbol(lib, GETNAMEINFO_SYMBOL);
+ if (!_pr_freeaddrinfo || !_pr_getnameinfo) {
+ PR_UnloadLibrary(lib);
+ continue;
+ }
+ /* Keep the library loaded. */
+ return PR_SUCCESS;
+ }
+ return PR_FAILURE;
+#else
+ /*
+ * Resolve getaddrinfo by searching all loaded libraries. Then
+ * search library containing getaddrinfo for freeaddrinfo.
+ */
+ _pr_getaddrinfo = (FN_GETADDRINFO)
+ PR_FindFunctionSymbolAndLibrary(GETADDRINFO_SYMBOL, &lib);
+ if (!_pr_getaddrinfo) {
+ return PR_FAILURE;
+ }
+ _pr_freeaddrinfo = (FN_FREEADDRINFO)
+ PR_FindFunctionSymbol(lib, FREEADDRINFO_SYMBOL);
+ _pr_getnameinfo = (FN_GETNAMEINFO)
+ PR_FindFunctionSymbol(lib, GETNAMEINFO_SYMBOL);
+ PR_UnloadLibrary(lib);
+ if (!_pr_freeaddrinfo || !_pr_getnameinfo) {
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+#endif
+}
+
+#define GETADDRINFO (*_pr_getaddrinfo)
+#define FREEADDRINFO (*_pr_freeaddrinfo)
+#define GETNAMEINFO (*_pr_getnameinfo)
+
+#endif /* _PR_INET6 */
+
+#endif /* _PR_HAVE_GETADDRINFO */
+
+#if !defined(_PR_HAVE_GETADDRINFO) || defined(_PR_INET6_PROBE)
+/*
+ * If getaddrinfo does not exist, then we will fall back on
+ * PR_GetHostByName, which requires that we allocate a buffer for the
+ * PRHostEnt data structure and its members.
+ */
+typedef struct PRAddrInfoFB {
+ char buf[PR_NETDB_BUF_SIZE];
+ PRHostEnt hostent;
+ PRBool has_cname;
+} PRAddrInfoFB;
+
+static PRAddrInfo *
+pr_GetAddrInfoByNameFB(const char *hostname,
+ PRUint16 af,
+ PRIntn flags)
+{
+ PRStatus rv;
+ PRAddrInfoFB *ai;
+ /* fallback on PR_GetHostByName */
+ ai = PR_NEW(PRAddrInfoFB);
+ if (!ai) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+ }
+ rv = PR_GetHostByName(hostname, ai->buf, sizeof ai->buf, &ai->hostent);
+ if (rv == PR_FAILURE) {
+ PR_Free(ai);
+ return NULL;
+ }
+ ai->has_cname = !(flags & PR_AI_NOCANONNAME);
+
+ return (PRAddrInfo *) ai;
+}
+#endif /* !_PR_HAVE_GETADDRINFO || _PR_INET6_PROBE */
+
+PR_IMPLEMENT(PRAddrInfo *) PR_GetAddrInfoByName(const char *hostname,
+ PRUint16 af,
+ PRIntn flags)
+{
+ /* restrict input to supported values */
+ if ((af != PR_AF_INET && af != PR_AF_UNSPEC) ||
+ (flags & ~ PR_AI_NOCANONNAME) != PR_AI_ADDRCONFIG) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+ }
+
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+#if !defined(_PR_HAVE_GETADDRINFO)
+ return pr_GetAddrInfoByNameFB(hostname, af, flags);
+#else
+#if defined(_PR_INET6_PROBE)
+ if (!_pr_ipv6_is_present()) {
+ return pr_GetAddrInfoByNameFB(hostname, af, flags);
+ }
+#endif
+ {
+ PRADDRINFO *res, hints;
+ int rv;
+
+ /*
+ * we assume a RFC 2553 compliant getaddrinfo. this may at some
+ * point need to be customized as platforms begin to adopt the
+ * RFC 3493.
+ */
+
+ memset(&hints, 0, sizeof(hints));
+ if (!(flags & PR_AI_NOCANONNAME)) {
+ hints.ai_flags |= AI_CANONNAME;
+ }
+#ifdef AI_ADDRCONFIG
+ /*
+ * Propagate AI_ADDRCONFIG to the GETADDRINFO call if PR_AI_ADDRCONFIG
+ * is set.
+ *
+ * Need a workaround for loopback host addresses:
+ * The problem is that in glibc and Windows, AI_ADDRCONFIG applies the
+ * existence of an outgoing network interface to IP addresses of the
+ * loopback interface, due to a strict interpretation of the
+ * specification. For example, if a computer does not have any
+ * outgoing IPv6 network interface, but its loopback network interface
+ * supports IPv6, a getaddrinfo call on "localhost" with AI_ADDRCONFIG
+ * won't return the IPv6 loopback address "::1", because getaddrinfo
+ * thinks the computer cannot connect to any IPv6 destination,
+ * ignoring the remote vs. local/loopback distinction.
+ */
+ if ((flags & PR_AI_ADDRCONFIG) &&
+ strcmp(hostname, "localhost") != 0 &&
+ strcmp(hostname, "localhost.localdomain") != 0 &&
+ strcmp(hostname, "localhost6") != 0 &&
+ strcmp(hostname, "localhost6.localdomain6") != 0) {
+ hints.ai_flags |= AI_ADDRCONFIG;
+ }
+#endif
+ hints.ai_family = (af == PR_AF_INET) ? AF_INET : AF_UNSPEC;
+
+ /*
+ * it is important to select a socket type in the hints, otherwise we
+ * will get back repetitive entries: one for each socket type. since
+ * we do not expose ai_socktype through our API, it is okay to do this
+ * here. the application may still choose to create a socket of some
+ * other type.
+ */
+ hints.ai_socktype = SOCK_STREAM;
+
+ rv = GETADDRINFO(hostname, NULL, &hints, &res);
+#ifdef AI_ADDRCONFIG
+ if (rv == EAI_BADFLAGS && (hints.ai_flags & AI_ADDRCONFIG)) {
+ hints.ai_flags &= ~AI_ADDRCONFIG;
+ rv = GETADDRINFO(hostname, NULL, &hints, &res);
+ }
+#endif
+ if (rv == 0) {
+ return (PRAddrInfo *) res;
+ }
+
+ PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, rv);
+ }
+ return NULL;
+#endif
+}
+
+PR_IMPLEMENT(void) PR_FreeAddrInfo(PRAddrInfo *ai)
+{
+#if defined(_PR_HAVE_GETADDRINFO)
+#if defined(_PR_INET6_PROBE)
+ if (!_pr_ipv6_is_present()) {
+ PR_Free((PRAddrInfoFB *) ai);
+ }
+ else
+#endif
+ FREEADDRINFO((PRADDRINFO *) ai);
+#else
+ PR_Free((PRAddrInfoFB *) ai);
+#endif
+}
+
+PR_IMPLEMENT(void *) PR_EnumerateAddrInfo(void *iterPtr,
+ const PRAddrInfo *base,
+ PRUint16 port,
+ PRNetAddr *result)
+{
+#if defined(_PR_HAVE_GETADDRINFO)
+ PRADDRINFO *ai;
+#if defined(_PR_INET6_PROBE)
+ if (!_pr_ipv6_is_present()) {
+ /* using PRAddrInfoFB */
+ PRIntn iter = (PRIntn)(PRPtrdiff) iterPtr;
+ iter = PR_EnumerateHostEnt(iter, &((PRAddrInfoFB *) base)->hostent, port, result);
+ if (iter < 0) {
+ iter = 0;
+ }
+ return (void *)(PRPtrdiff) iter;
+ }
+#endif
+
+ if (iterPtr) {
+ ai = ((PRADDRINFO *) iterPtr)->ai_next;
+ }
+ else {
+ ai = (PRADDRINFO *) base;
+ }
+
+ while (ai && ai->ai_addrlen > sizeof(PRNetAddr)) {
+ ai = ai->ai_next;
+ }
+
+ if (ai) {
+ /* copy sockaddr to PRNetAddr */
+ memcpy(result, ai->ai_addr, ai->ai_addrlen);
+ result->raw.family = ai->ai_addr->sa_family;
+#ifdef _PR_INET6
+ if (AF_INET6 == result->raw.family) {
+ result->raw.family = PR_AF_INET6;
+ }
+#endif
+ if (ai->ai_addrlen < sizeof(PRNetAddr)) {
+ memset(((char*)result)+ai->ai_addrlen, 0, sizeof(PRNetAddr) - ai->ai_addrlen);
+ }
+
+ if (result->raw.family == PR_AF_INET) {
+ result->inet.port = htons(port);
+ }
+ else {
+ result->ipv6.port = htons(port);
+ }
+ }
+
+ return ai;
+#else
+ /* using PRAddrInfoFB */
+ PRIntn iter = (PRIntn) iterPtr;
+ iter = PR_EnumerateHostEnt(iter, &((PRAddrInfoFB *) base)->hostent, port, result);
+ if (iter < 0) {
+ iter = 0;
+ }
+ return (void *) iter;
+#endif
+}
+
+PR_IMPLEMENT(const char *) PR_GetCanonNameFromAddrInfo(const PRAddrInfo *ai)
+{
+#if defined(_PR_HAVE_GETADDRINFO)
+#if defined(_PR_INET6_PROBE)
+ if (!_pr_ipv6_is_present()) {
+ const PRAddrInfoFB *fb = (const PRAddrInfoFB *) ai;
+ return fb->has_cname ? fb->hostent.h_name : NULL;
+ }
+#endif
+ return ((const PRADDRINFO *) ai)->ai_canonname;
+#else
+ const PRAddrInfoFB *fb = (const PRAddrInfoFB *) ai;
+ return fb->has_cname ? fb->hostent.h_name : NULL;
+#endif
+}
+
+#if defined(_PR_HAVE_GETADDRINFO)
+static PRStatus pr_StringToNetAddrGAI(const char *string, PRNetAddr *addr)
+{
+ PRADDRINFO *res, hints;
+ int rv; /* 0 for success, or the error code EAI_xxx */
+ PRNetAddr laddr;
+ PRStatus status = PR_SUCCESS;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_NUMERICHOST;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ rv = GETADDRINFO(string, NULL, &hints, &res);
+ if (rv != 0)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, rv);
+ return PR_FAILURE;
+ }
+
+ /* pick up the first addr */
+ memcpy(&laddr, res->ai_addr, res->ai_addrlen);
+ if (AF_INET6 == res->ai_addr->sa_family)
+ {
+ addr->ipv6.family = PR_AF_INET6;
+ addr->ipv6.ip = laddr.ipv6.ip;
+ addr->ipv6.scope_id = laddr.ipv6.scope_id;
+ }
+ else if (AF_INET == res->ai_addr->sa_family)
+ {
+ addr->inet.family = PR_AF_INET;
+ addr->inet.ip = laddr.inet.ip;
+ }
+ else
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ status = PR_FAILURE;
+ }
+
+ FREEADDRINFO(res);
+ return status;
+}
+#endif /* _PR_HAVE_GETADDRINFO */
+
+static PRStatus pr_StringToNetAddrFB(const char *string, PRNetAddr *addr)
+{
+ PRIntn rv;
+
+ rv = pr_inet_aton(string, &addr->inet.ip);
+ if (1 == rv)
+ {
+ addr->raw.family = AF_INET;
+ return PR_SUCCESS;
+ }
+
+ PR_ASSERT(0 == rv);
+ /* clean up after the failed call */
+ memset(&addr->inet.ip, 0, sizeof(addr->inet.ip));
+
+ rv = StringToV6Addr(string, &addr->ipv6.ip);
+ if (1 == rv)
+ {
+ addr->raw.family = PR_AF_INET6;
+ return PR_SUCCESS;
+ }
+
+ PR_ASSERT(0 == rv);
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PR_IMPLEMENT(PRStatus) PR_StringToNetAddr(const char *string, PRNetAddr *addr)
+{
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+ if (!addr || !string || !*string)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+
+#if !defined(_PR_HAVE_GETADDRINFO)
+ return pr_StringToNetAddrFB(string, addr);
+#else
+ /*
+ * getaddrinfo with AI_NUMERICHOST is much slower than pr_inet_aton on some
+ * platforms, such as Mac OS X (bug 404399), Linux glibc 2.10 (bug 344809),
+ * and most likely others. So we only use it to convert literal IP addresses
+ * that contain IPv6 scope IDs, which pr_inet_aton cannot convert.
+ */
+ if (!strchr(string, '%')) {
+ return pr_StringToNetAddrFB(string, addr);
+ }
+
+#if defined(_PR_INET6_PROBE)
+ if (!_pr_ipv6_is_present()) {
+ return pr_StringToNetAddrFB(string, addr);
+ }
+#endif
+
+ return pr_StringToNetAddrGAI(string, addr);
+#endif
+}
+
+#if defined(_PR_HAVE_GETADDRINFO)
+static PRStatus pr_NetAddrToStringGNI(
+ const PRNetAddr *addr, char *string, PRUint32 size)
+{
+ int addrlen;
+ const PRNetAddr *addrp = addr;
+#if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6)
+ PRUint16 md_af = addr->raw.family;
+ PRNetAddr addrcopy;
+#endif
+ int rv; /* 0 for success, or the error code EAI_xxx */
+
+#ifdef _PR_INET6
+ if (addr->raw.family == PR_AF_INET6)
+ {
+ md_af = AF_INET6;
+#ifndef _PR_HAVE_SOCKADDR_LEN
+ addrcopy = *addr;
+ addrcopy.raw.family = md_af;
+ addrp = &addrcopy;
+#endif
+ }
+#endif
+
+ addrlen = PR_NETADDR_SIZE(addr);
+#ifdef _PR_HAVE_SOCKADDR_LEN
+ addrcopy = *addr;
+ ((struct sockaddr*)&addrcopy)->sa_len = addrlen;
+ ((struct sockaddr*)&addrcopy)->sa_family = md_af;
+ addrp = &addrcopy;
+#endif
+ rv = GETNAMEINFO((const struct sockaddr *)addrp, addrlen,
+ string, size, NULL, 0, NI_NUMERICHOST);
+ if (rv != 0)
+ {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, rv);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+#endif /* _PR_HAVE_GETADDRINFO */
+
+#if !defined(_PR_HAVE_GETADDRINFO) || defined(_PR_INET6_PROBE)
+static PRStatus pr_NetAddrToStringFB(
+ const PRNetAddr *addr, char *string, PRUint32 size)
+{
+ if (PR_AF_INET6 == addr->raw.family)
+ {
+#if defined(_PR_HAVE_INET_NTOP)
+ if (NULL == inet_ntop(AF_INET6, &addr->ipv6.ip, string, size))
+#else
+ if (NULL == V6AddrToString(&addr->ipv6.ip, string, size))
+#endif
+ {
+ /* the size of the result buffer is inadequate */
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
+ return PR_FAILURE;
+ }
+ }
+ else
+ {
+ if (size < 16) {
+ goto failed;
+ }
+ if (AF_INET != addr->raw.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_NetAddrToStringFB */
+#endif /* !_PR_HAVE_GETADDRINFO || _PR_INET6_PROBE */
+
+PR_IMPLEMENT(PRStatus) PR_NetAddrToString(
+ const PRNetAddr *addr, char *string, PRUint32 size)
+{
+ if (!_pr_initialized) {
+ _PR_ImplicitInitialization();
+ }
+
+#if !defined(_PR_HAVE_GETADDRINFO)
+ return pr_NetAddrToStringFB(addr, string, size);
+#else
+#if defined(_PR_INET6_PROBE)
+ if (!_pr_ipv6_is_present()) {
+ return pr_NetAddrToStringFB(addr, string, size);
+ }
+#endif
+ return pr_NetAddrToStringGNI(addr, string, size);
+#endif
+} /* PR_NetAddrToString */
diff --git a/nsprpub/pr/src/misc/prolock.c b/nsprpub/pr/src/misc/prolock.c
new file mode 100644
index 0000000000..77a26bb2f7
--- /dev/null
+++ b/nsprpub/pr/src/misc/prolock.c
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** prolock.c -- NSPR Ordered Lock
+**
+** Implement the API defined in prolock.h
+**
+*/
+#include "prolock.h"
+#include "prlog.h"
+#include "prerror.h"
+
+PR_IMPLEMENT(PROrderedLock *)
+PR_CreateOrderedLock(
+ PRInt32 order,
+ const char *name
+)
+{
+ PR_NOT_REACHED("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_NOT_REACHED("Not implemented"); /* Not implemented yet */
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+} /* end PR_DestroyOrderedLock() */
+
+
+PR_IMPLEMENT(void)
+PR_LockOrderedLock(
+ PROrderedLock *lock
+)
+{
+ PR_NOT_REACHED("Not implemented"); /* Not implemented yet */
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+} /* end PR_LockOrderedLock() */
+
+
+PR_IMPLEMENT(PRStatus)
+PR_UnlockOrderedLock(
+ PROrderedLock *lock
+)
+{
+ PR_NOT_REACHED("Not implemented"); /* Not implemented yet */
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+} /* end PR_UnlockOrderedLock() */
diff --git a/nsprpub/pr/src/misc/prrng.c b/nsprpub/pr/src/misc/prrng.c
new file mode 100644
index 0000000000..b3e3d4878b
--- /dev/null
+++ b/nsprpub/pr/src/misc/prrng.c
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+
+/*
+ * We were not including <string.h> in optimized builds. On AIX this
+ * caused libnspr4.so to export memcpy and some binaries linked with
+ * libnspr4.so resolved their memcpy references with libnspr4.so. To
+ * be backward compatible with old libnspr4.so binaries, we do not
+ * include <string.h> in optimized builds for AIX. (bug 200561)
+ */
+#if !(defined(AIX) && !defined(DEBUG))
+#include <string.h>
+#endif
+
+PRSize _pr_CopyLowBits(
+ void *dst,
+ PRSize dstlen,
+ void *src,
+ PRSize srclen )
+{
+ if (srclen <= dstlen) {
+ memcpy(dst, src, srclen);
+ return srclen;
+ }
+#if defined IS_BIG_ENDIAN
+ memcpy(dst, (char*)src + (srclen - dstlen), dstlen);
+#else
+ memcpy(dst, src, dstlen);
+#endif
+ return dstlen;
+}
+
+PR_IMPLEMENT(PRSize) PR_GetRandomNoise(
+ void *buf,
+ PRSize size
+)
+{
+ return( _PR_MD_GET_RANDOM_NOISE( buf, size ));
+} /* end PR_GetRandomNoise() */
+/* end prrng.c */
diff --git a/nsprpub/pr/src/misc/prsystem.c b/nsprpub/pr/src/misc/prsystem.c
new file mode 100644
index 0000000000..109f1ed23f
--- /dev/null
+++ b/nsprpub/pr/src/misc/prsystem.c
@@ -0,0 +1,369 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "primpl.h"
+#include "prsystem.h"
+#include "prprf.h"
+#include "prlong.h"
+
+#if defined(OS2)
+#define INCL_DOS
+#define INCL_DOSMISC
+#include <os2.h>
+/* define the required constant if it is not already defined in the headers */
+#ifndef QSV_NUMPROCESSORS
+#define QSV_NUMPROCESSORS 26
+#endif
+#endif
+
+/* BSD-derived systems use sysctl() to get the number of processors */
+#if defined(BSDI) || defined(FREEBSD) || defined(NETBSD) \
+ || defined(OPENBSD) || defined(DRAGONFLY) || defined(DARWIN)
+#define _PR_HAVE_SYSCTL
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#endif
+
+#if defined(DARWIN)
+#include <mach/mach_init.h>
+#include <mach/mach_host.h>
+#include <mach/mach_port.h>
+#endif
+
+#if defined(HPUX)
+#include <sys/mpctl.h>
+#include <sys/pstat.h>
+#endif
+
+#if defined(XP_UNIX)
+#include <unistd.h>
+#include <sys/utsname.h>
+#endif
+
+#if defined(LINUX)
+#include <string.h>
+#include <ctype.h>
+#define MAX_LINE 512
+#endif
+
+#if defined(AIX)
+#include <cf.h>
+#include <sys/cfgodm.h>
+#endif
+
+PR_IMPLEMENT(char) PR_GetDirectorySeparator(void)
+{
+ return PR_DIRECTORY_SEPARATOR;
+} /* PR_GetDirectorySeparator */
+
+/*
+** OBSOLETE -- the function name is misspelled.
+*/
+PR_IMPLEMENT(char) PR_GetDirectorySepartor(void)
+{
+#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(char) PR_GetPathSeparator(void)
+{
+ return PR_PATH_SEPARATOR;
+} /* PR_GetPathSeparator */
+
+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:
+ case PR_SI_HOSTNAME_UNTRUNCATED:
+ if (PR_FAILURE == _PR_MD_GETHOSTNAME(buf, (PRUintn)buflen)) {
+ return PR_FAILURE;
+ }
+
+ if (cmd == PR_SI_HOSTNAME_UNTRUNCATED) {
+ break;
+ }
+ /*
+ * On some platforms a system does not have a hostname and
+ * its IP address is returned instead. The following code
+ * should be skipped on those platforms.
+ */
+#ifndef _PR_GET_HOST_ADDR_AS_NAME
+ /* Return the unqualified hostname */
+ while (buf[len] && (len < buflen)) {
+ if (buf[len] == '.') {
+ buf[len] = '\0';
+ break;
+ }
+ len += 1;
+ }
+#endif
+ break;
+
+ case PR_SI_SYSNAME:
+ /* Return the operating system name */
+#if defined(XP_UNIX) || defined(WIN32)
+ if (PR_FAILURE == _PR_MD_GETSYSINFO(cmd, buf, (PRUintn)buflen)) {
+ return PR_FAILURE;
+ }
+#else
+ (void)PR_snprintf(buf, buflen, _PR_SI_SYSNAME);
+#endif
+ break;
+
+ case PR_SI_RELEASE:
+ /* Return the version of the operating system */
+#if defined(XP_UNIX) || defined(WIN32)
+ if (PR_FAILURE == _PR_MD_GETSYSINFO(cmd, buf, (PRUintn)buflen)) {
+ return PR_FAILURE;
+ }
+#endif
+#if defined(XP_OS2)
+ {
+ ULONG os2ver[2] = {0};
+ DosQuerySysInfo(QSV_VERSION_MINOR, QSV_VERSION_REVISION,
+ &os2ver, sizeof(os2ver));
+ /* Formatting for normal usage (2.11, 3.0, 4.0, 4.5); officially,
+ Warp 4 is version 2.40.00, WSeB 2.45.00 */
+ if (os2ver[0] < 30)
+ (void)PR_snprintf(buf, buflen, "%s%lu",
+ "2.", os2ver[0]);
+ else if (os2ver[0] < 45)
+ (void)PR_snprintf(buf, buflen, "%lu%s%lu",
+ os2ver[0]/10, ".", os2ver[1]);
+ else
+ (void)PR_snprintf(buf, buflen, "%.1f",
+ os2ver[0]/10.0);
+ }
+#endif /* OS2 */
+ break;
+
+ case PR_SI_RELEASE_BUILD:
+ /* Return the version of the operating system */
+#if defined(XP_UNIX) || defined(WIN32)
+ if (PR_FAILURE == _PR_MD_GETSYSINFO(cmd, buf, (PRUintn)buflen)) {
+ return PR_FAILURE;
+ }
+#else
+ if (buflen) {
+ *buf = 0;
+ }
+#endif /* XP_UNIX || WIN32 */
+ break;
+
+ case PR_SI_ARCHITECTURE:
+ /* Return the architecture of the machine (ie. x86, mips, alpha, ...)*/
+ (void)PR_snprintf(buf, buflen, _PR_SI_ARCHITECTURE);
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+/*
+** PR_GetNumberOfProcessors()
+**
+** Implementation notes:
+** Every platform does it a bit different.
+** numCpus is the returned value.
+** for each platform's "if defined" section
+** declare your local variable
+** do your thing, assign to numCpus
+** order of the if defined()s may be important,
+** especially for unix variants. Do platform
+** specific implementations before XP_UNIX.
+**
+*/
+PR_IMPLEMENT(PRInt32) PR_GetNumberOfProcessors( void )
+{
+ PRInt32 numCpus;
+#if defined(WIN32)
+ SYSTEM_INFO info;
+
+ GetSystemInfo( &info );
+ numCpus = info.dwNumberOfProcessors;
+#elif defined(OS2)
+ DosQuerySysInfo( QSV_NUMPROCESSORS, QSV_NUMPROCESSORS, &numCpus, sizeof(numCpus));
+#elif defined(_PR_HAVE_SYSCTL)
+ int mib[2];
+ int rc;
+ size_t len = sizeof(numCpus);
+
+ mib[0] = CTL_HW;
+ mib[1] = HW_NCPU;
+ rc = sysctl( mib, 2, &numCpus, &len, NULL, 0 );
+ if ( -1 == rc ) {
+ numCpus = -1; /* set to -1 for return value on error */
+ _PR_MD_MAP_DEFAULT_ERROR( _MD_ERRNO() );
+ }
+#elif defined(HPUX)
+ numCpus = mpctl( MPC_GETNUMSPUS, 0, 0 );
+ if ( numCpus < 1 ) {
+ numCpus = -1; /* set to -1 for return value on error */
+ _PR_MD_MAP_DEFAULT_ERROR( _MD_ERRNO() );
+ }
+#elif defined(RISCOS)
+ numCpus = 1;
+#elif defined(LINUX)
+ /* for the benefit of devices with advanced power-saving, that
+ actually hotplug their cpus in heavy load, try to figure out
+ the real number of CPUs */
+ char buf[MAX_LINE];
+ FILE *fin;
+ const char *cpu_present = "/sys/devices/system/cpu/present";
+ size_t strsize;
+ numCpus = 0;
+ fin = fopen(cpu_present, "r");
+ if (fin != NULL) {
+ if (fgets(buf, MAX_LINE, fin) != NULL) {
+ /* check that the format is what we expect */
+ if (buf[0] == '0') {
+ strsize = strlen(buf);
+ if (strsize == 1) {
+ /* single core */
+ numCpus = 1;
+ } else if (strsize >= 3 && strsize <= 5) {
+ /* should be of the form 0-999 */
+ /* parse the part after the 0-, note count is 0-based */
+ if (buf[1] == '-' && isdigit(buf[2])) {
+ numCpus = 1 + atoi(buf + 2);
+ }
+ }
+ }
+ }
+ fclose(fin);
+ }
+ /* if that fails, fall back to more standard methods */
+ if (!numCpus) {
+ numCpus = sysconf( _SC_NPROCESSORS_CONF );
+ }
+#elif defined(XP_UNIX)
+ numCpus = sysconf( _SC_NPROCESSORS_CONF );
+#else
+#error "An implementation is required"
+#endif
+ return(numCpus);
+} /* end PR_GetNumberOfProcessors() */
+
+/*
+** PR_GetPhysicalMemorySize()
+**
+** Implementation notes:
+** Every platform does it a bit different.
+** bytes is the returned value.
+** for each platform's "if defined" section
+** declare your local variable
+** do your thing, assign to bytes.
+**
+*/
+PR_IMPLEMENT(PRUint64) PR_GetPhysicalMemorySize(void)
+{
+ PRUint64 bytes = 0;
+
+#if defined(LINUX) || defined(SOLARIS)
+
+ long pageSize = sysconf(_SC_PAGESIZE);
+ long pageCount = sysconf(_SC_PHYS_PAGES);
+ if (pageSize >= 0 && pageCount >= 0) {
+ bytes = (PRUint64) pageSize * pageCount;
+ }
+
+#elif defined(NETBSD) || defined(OPENBSD) \
+ || defined(FREEBSD) || defined(DRAGONFLY)
+
+ int mib[2];
+ int rc;
+#ifdef HW_PHYSMEM64
+ uint64_t memSize;
+#else
+ unsigned long memSize;
+#endif
+ size_t len = sizeof(memSize);
+
+ mib[0] = CTL_HW;
+#ifdef HW_PHYSMEM64
+ mib[1] = HW_PHYSMEM64;
+#else
+ mib[1] = HW_PHYSMEM;
+#endif
+ rc = sysctl(mib, 2, &memSize, &len, NULL, 0);
+ if (-1 != rc) {
+ bytes = memSize;
+ }
+
+#elif defined(HPUX)
+
+ struct pst_static info;
+ int result = pstat_getstatic(&info, sizeof(info), 1, 0);
+ if (result == 1) {
+ bytes = (PRUint64) info.physical_memory * info.page_size;
+ }
+
+#elif defined(DARWIN)
+
+ mach_port_t mach_host = mach_host_self();
+ struct host_basic_info hInfo;
+ mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
+
+ int result = host_info(mach_host,
+ HOST_BASIC_INFO,
+ (host_info_t) &hInfo,
+ &count);
+ mach_port_deallocate(mach_task_self(), mach_host);
+ if (result == KERN_SUCCESS) {
+ bytes = hInfo.max_mem;
+ }
+
+#elif defined(WIN32)
+
+ MEMORYSTATUSEX memStat;
+ memStat.dwLength = sizeof(memStat);
+ if (GlobalMemoryStatusEx(&memStat)) {
+ bytes = memStat.ullTotalPhys;
+ }
+
+#elif defined(OS2)
+
+ ULONG ulPhysMem;
+ DosQuerySysInfo(QSV_TOTPHYSMEM,
+ QSV_TOTPHYSMEM,
+ &ulPhysMem,
+ sizeof(ulPhysMem));
+ bytes = ulPhysMem;
+
+#elif defined(AIX)
+
+ if (odm_initialize() == 0) {
+ int how_many;
+ struct CuAt *obj = getattr("sys0", "realmem", 0, &how_many);
+ if (obj != NULL) {
+ bytes = (PRUint64) atoi(obj->value) * 1024;
+ free(obj);
+ }
+ odm_terminate();
+ }
+
+#else
+
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+
+#endif
+
+ return bytes;
+} /* end PR_GetPhysicalMemorySize() */
diff --git a/nsprpub/pr/src/misc/prthinfo.c b/nsprpub/pr/src/misc/prthinfo.c
new file mode 100644
index 0000000000..14602df0be
--- /dev/null
+++ b/nsprpub/pr/src/misc/prthinfo.c
@@ -0,0 +1,211 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "prlog.h"
+#include "prthread.h"
+#include "private/pprthred.h"
+#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;
+ }
+
+ /* if thread is not allocated on stack, this is redundant. */
+ 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)
+{
+ 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_GetCurrentThread();
+ 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/nsprpub/pr/src/misc/prtime.c b/nsprpub/pr/src/misc/prtime.c
new file mode 100644
index 0000000000..6d711a6b8d
--- /dev/null
+++ b/nsprpub/pr/src/misc/prtime.c
@@ -0,0 +1,2150 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * 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>
+#include <errno.h> /* for EINVAL */
+#include <time.h>
+
+/*
+ * The COUNT_LEAPS macro counts the number of leap years passed by
+ * till the start of the given year Y. At the start of the year 4
+ * A.D. the number of leap years passed by is 0, while at the start of
+ * the year 5 A.D. this count is 1. The number of years divisible by
+ * 100 but not divisible by 400 (the non-leap years) is deducted from
+ * the count to get the correct number of leap years.
+ *
+ * The COUNT_DAYS macro counts the number of days since 01/01/01 till the
+ * start of the given year Y. The number of days at the start of the year
+ * 1 is 0 while the number of days at the start of the year 2 is 365
+ * (which is ((2)-1) * 365) and so on. The reference point is 01/01/01
+ * midnight 00:00:00.
+ */
+
+#define COUNT_LEAPS(Y) ( ((Y)-1)/4 - ((Y)-1)/100 + ((Y)-1)/400 )
+#define COUNT_DAYS(Y) ( ((Y)-1)*365 + COUNT_LEAPS(Y) )
+#define DAYS_BETWEEN_YEARS(A, B) (COUNT_DAYS(B) - COUNT_DAYS(A))
+
+/*
+ * 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 const 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 const 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
+ *
+ *------------------------------------------------------------------------
+ */
+
+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 year by finding the 400 year period, then working
+ * down from there.
+ *
+ * Since numDays is originally the number of days since January 1, 1970,
+ * we must change it to be the number of days from January 1, 0001.
+ */
+
+ numDays += 719162; /* 719162 = days from year 1 up to 1970 */
+ tmp = numDays / 146097; /* 146097 = days in 400 years */
+ rem = numDays % 146097;
+ gmt->tm_year = tmp * 400 + 1;
+
+ /* Compute the 100 year period. */
+
+ tmp = rem / 36524; /* 36524 = days in 100 years */
+ rem %= 36524;
+ if (tmp == 4) { /* the 400th year is a leap year */
+ tmp = 3;
+ rem = 36524;
+ }
+ gmt->tm_year += tmp * 100;
+
+ /* Compute the 4 year period. */
+
+ tmp = rem / 1461; /* 1461 = days in 4 years */
+ rem %= 1461;
+ gmt->tm_year += tmp * 4;
+
+ /* Compute which year in the 4. */
+
+ tmp = rem / 365;
+ rem %= 365;
+ if (tmp == 4) { /* the 4th year is a leap year */
+ tmp = 3;
+ rem = 365;
+ }
+
+ gmt->tm_year += tmp;
+ gmt->tm_yday = rem;
+ isLeap = IsLeapYear(gmt->tm_year);
+
+ /* 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.
+ *
+ *------------------------------------------------------------------------
+ */
+PR_IMPLEMENT(PRTime)
+PR_ImplodeTime(const PRExplodedTime *exploded)
+{
+ PRExplodedTime copy;
+ PRTime retVal;
+ PRInt64 secPerDay, usecPerSec;
+ PRInt64 temp;
+ PRInt64 numSecs64;
+ PRInt32 numDays;
+ PRInt32 numSecs;
+
+ /* Normalize first. Do this on our copy */
+ copy = *exploded;
+ PR_NormalizeTime(&copy, PR_GMTParameters);
+
+ numDays = DAYS_BETWEEN_YEARS(1970, copy.tm_year);
+
+ 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;
+ }
+ 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 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];
+
+ numDays = DAYS_BETWEEN_YEARS(1970, time->tm_year) + 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, PC, and BeOS
+ * use this version. A platform may have its own machine-dependent
+ * implementation of this function.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#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
+
+#elif defined(_MSC_VER)
+
+/* Visual C++ has had localtime_s() since Visual C++ 2005. */
+
+static struct tm *MT_safe_localtime(const time_t *clock, struct tm *result)
+{
+ errno_t err = localtime_s(result, clock);
+ if (err != 0) {
+ errno = err;
+ return NULL;
+ }
+ return result;
+}
+
+#else
+
+#define HAVE_LOCALTIME_MONITOR 1 /* We use 'monitor' to serialize our calls
+ * to localtime(). */
+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) {
+ 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.
+ *
+ * With negative values of clock, OS/2 returns the struct tm for
+ * clock plus ULONG_MAX. So we also have to check for the invalid
+ * structs returned for timezones west of Greenwich when clock == 0.
+ */
+
+ tmPtr = localtime(clock);
+
+#if defined(WIN16) || defined(XP_OS2)
+ if ( (PRInt32) *clock < 0 ||
+ ( (PRInt32) *clock == 0 && tmPtr->tm_year != 70)) {
+ 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() */
+
+void _PR_InitTime(void)
+{
+#ifdef HAVE_LOCALTIME_MONITOR
+ monitor = PR_NewLock();
+#endif
+#ifdef WINCE
+ _MD_InitTime();
+#endif
+}
+
+void _PR_CleanupTime(void)
+{
+#ifdef HAVE_LOCALTIME_MONITOR
+ if (monitor) {
+ PR_DestroyLock(monitor);
+ monitor = NULL;
+ }
+#endif
+#ifdef WINCE
+ _MD_CleanupTime();
+#endif
+}
+
+#if defined(XP_UNIX) || defined(XP_PC)
+
+PR_IMPLEMENT(PRTimeParameters)
+PR_LocalTimeParameters(const PRExplodedTime *gmt)
+{
+
+ PRTimeParameters retVal;
+ struct tm localTime;
+ struct tm *localTimeResult;
+ time_t secs;
+ PRTime secs64;
+ PRInt64 usecPerSec;
+ PRInt64 usecPerSec_1;
+ PRInt64 maxInt32;
+ PRInt64 minInt32;
+ 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;
+ localTimeResult = MT_safe_localtime(&secs, &localTime);
+ PR_ASSERT(localTimeResult != NULL);
+ if (localTimeResult == NULL) {
+ /* Shouldn't happen. Use safe fallback for optimized builds. */
+ return PR_GMTParameters(gmt);
+ }
+
+ /* 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_I2L(usecPerSec_1, PR_USEC_PER_SEC - 1);
+ /* Convert to seconds, truncating down (3.1 -> 3 and -3.1 -> -4) */
+ if (LL_GE_ZERO(secs64)) {
+ LL_DIV(secs64, secs64, usecPerSec);
+ } else {
+ LL_NEG(secs64, secs64);
+ LL_ADD(secs64, secs64, usecPerSec_1);
+ LL_DIV(secs64, secs64, usecPerSec);
+ LL_NEG(secs64, secs64);
+ }
+ LL_I2L(maxInt32, PR_INT32_MAX);
+ LL_I2L(minInt32, PR_INT32_MIN);
+ if (LL_CMP(secs64, >, maxInt32) || LL_CMP(secs64, <, minInt32)) {
+ /* secs64 is too large or too small 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.
+ *
+ *------------------------------------------------------------------------
+ */
+
+/*
+ * Returns the mday of the first sunday of the month, where
+ * mday and wday are for a given day in the month.
+ * mdays start with 1 (e.g. 1..31).
+ * wdays start with 0 and are in the range 0..6. 0 = Sunday.
+ */
+#define firstSunday(mday, wday) (((mday - wday + 7 - 1) % 7) + 1)
+
+/*
+ * Returns the mday for the N'th Sunday of the month, where
+ * mday and wday are for a given day in the month.
+ * mdays start with 1 (e.g. 1..31).
+ * wdays start with 0 and are in the range 0..6. 0 = Sunday.
+ * N has the following values: 0 = first, 1 = second (etc), -1 = last.
+ * ndays is the number of days in that month, the same value as the
+ * mday of the last day of the month.
+ */
+static PRInt32
+NthSunday(PRInt32 mday, PRInt32 wday, PRInt32 N, PRInt32 ndays)
+{
+ PRInt32 firstSun = firstSunday(mday, wday);
+
+ if (N < 0) {
+ N = (ndays - firstSun) / 7;
+ }
+ return firstSun + (7 * N);
+}
+
+typedef struct DSTParams {
+ PRInt8 dst_start_month; /* 0 = January */
+ PRInt8 dst_start_Nth_Sunday; /* N as defined above */
+ PRInt8 dst_start_month_ndays; /* ndays as defined above */
+ PRInt8 dst_end_month; /* 0 = January */
+ PRInt8 dst_end_Nth_Sunday; /* N as defined above */
+ PRInt8 dst_end_month_ndays; /* ndays as defined above */
+} DSTParams;
+
+static const DSTParams dstParams[2] = {
+ /* year < 2007: First April Sunday - Last October Sunday */
+ { 3, 0, 30, 9, -1, 31 },
+ /* year >= 2007: Second March Sunday - First November Sunday */
+ { 2, 1, 31, 10, 0, 30 }
+};
+
+PR_IMPLEMENT(PRTimeParameters)
+PR_USPacificTimeParameters(const PRExplodedTime *gmt)
+{
+ const DSTParams *dst;
+ PRTimeParameters retVal;
+ PRExplodedTime st;
+
+ /*
+ * 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.
+ */
+
+ st.tm_usec = gmt->tm_usec;
+ st.tm_sec = gmt->tm_sec;
+ st.tm_min = gmt->tm_min;
+ st.tm_hour = gmt->tm_hour;
+ st.tm_mday = gmt->tm_mday;
+ st.tm_month = gmt->tm_month;
+ st.tm_year = gmt->tm_year;
+ st.tm_wday = gmt->tm_wday;
+ st.tm_yday = gmt->tm_yday;
+
+ /* Apply the offset to GMT to obtain the local standard time */
+ ApplySecOffset(&st, retVal.tp_gmt_offset);
+
+ if (st.tm_year < 2007) { /* first April Sunday - Last October Sunday */
+ dst = &dstParams[0];
+ } else { /* Second March Sunday - First November Sunday */
+ dst = &dstParams[1];
+ }
+
+ /*
+ * Apply the rules on standard time or GMT to obtain daylight saving
+ * time offset. In this implementation, we use the US DST rule.
+ */
+ if (st.tm_month < dst->dst_start_month) {
+ retVal.tp_dst_offset = 0L;
+ } else if (st.tm_month == dst->dst_start_month) {
+ int NthSun = NthSunday(st.tm_mday, st.tm_wday,
+ dst->dst_start_Nth_Sunday,
+ dst->dst_start_month_ndays);
+ if (st.tm_mday < NthSun) { /* Before starting Sunday */
+ retVal.tp_dst_offset = 0L;
+ } else if (st.tm_mday == NthSun) { /* Starting Sunday */
+ /* 01:59:59 PST -> 03:00:00 PDT */
+ if (st.tm_hour < 2) {
+ retVal.tp_dst_offset = 0L;
+ } else {
+ retVal.tp_dst_offset = 3600L;
+ }
+ } else { /* After starting Sunday */
+ retVal.tp_dst_offset = 3600L;
+ }
+ } else if (st.tm_month < dst->dst_end_month) {
+ retVal.tp_dst_offset = 3600L;
+ } else if (st.tm_month == dst->dst_end_month) {
+ int NthSun = NthSunday(st.tm_mday, st.tm_wday,
+ dst->dst_end_Nth_Sunday,
+ dst->dst_end_month_ndays);
+ if (st.tm_mday < NthSun) { /* Before ending Sunday */
+ retVal.tp_dst_offset = 3600L;
+ } else if (st.tm_mday == NthSun) { /* Ending Sunday */
+ /* 01:59:59 PDT -> 01:00:00 PST */
+ if (st.tm_hour < 1) {
+ retVal.tp_dst_offset = 3600L;
+ } else {
+ retVal.tp_dst_offset = 0L;
+ }
+ } else { /* After ending Sunday */
+ 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)
+{
+ 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_ParseTimeStringToExplodedTime(
+ const char *string,
+ PRBool default_to_gmt,
+ PRExplodedTime *result)
+{
+ TIME_TOKEN dotw = TT_UNKNOWN;
+ TIME_TOKEN month = TT_UNKNOWN;
+ TIME_TOKEN zone = TT_UNKNOWN;
+ int zone_offset = -1;
+ int dst_offset = 0;
+ int date = -1;
+ PRInt32 year = -1;
+ int hour = -1;
+ int min = -1;
+ int sec = -1;
+ struct tm *localTimeResult;
+
+ const char *rest = string;
+
+ int iterations = 0;
+
+ PR_ASSERT(string && result);
+ if (!string || !result) {
+ return PR_FAILURE;
+ }
+
+ while (*rest)
+ {
+
+ if (iterations++ > 1000)
+ {
+ return PR_FAILURE;
+ }
+
+ 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;
+ }
+ if ((end - rest) == 2)
+ tmp_hour = ((rest[0]-'0')*10 +
+ (rest[1]-'0'));
+ else {
+ tmp_hour = (rest[0]-'0');
+ }
+
+ /* move over the colon, and parse minutes */
+
+ rest = ++end;
+ while (*end >= '0' && *end <= '9') {
+ end++;
+ }
+
+ if (end == rest)
+ /* no digits after first colon? */
+ {
+ break;
+ }
+ if ((end - rest) > 2)
+ /* it is [0-9][0-9][0-9]+: */
+ {
+ break;
+ }
+ 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;
+ }
+ 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;
+ }
+ 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, 4, or 5 digits */
+ break;
+ }
+ n3 = (*s++ - '0');
+ if (*s >= '0' && *s <= '9') {
+ n3 = n3*10 + (*s++ - '0');
+ }
+
+ if (*s >= '0' && *s <= '9') /* optional digits 3, 4, and 5 */
+ {
+ n3 = n3*10 + (*s++ - '0');
+ if (*s < '0' || *s > '9') {
+ break;
+ }
+ n3 = n3*10 + (*s++ - '0');
+ if (*s >= '0' && *s <= '9') {
+ 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) == 5) /* five digits is a year */
+ year = (year < 0
+ ? ((rest[0]-'0')*10000L +
+ (rest[1]-'0')*1000L +
+ (rest[2]-'0')*100L +
+ (rest[3]-'0')*10L +
+ (rest[4]-'0'))
+ : year);
+ 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 five 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((unsigned char)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 = -8 * 60; dst_offset = 1 * 60; break;
+ case TT_MST: zone_offset = -7 * 60; break;
+ case TT_MDT: zone_offset = -7 * 60; dst_offset = 1 * 60; break;
+ case TT_CST: zone_offset = -6 * 60; break;
+ case TT_CDT: zone_offset = -6 * 60; dst_offset = 1 * 60; break;
+ case TT_EST: zone_offset = -5 * 60; break;
+ case TT_EDT: zone_offset = -5 * 60; dst_offset = 1 * 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 = 0 * 60; dst_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 || year > PR_INT16_MAX) {
+ return PR_FAILURE;
+ }
+
+ memset(result, 0, sizeof(*result));
+ if (sec != -1) {
+ result->tm_sec = sec;
+ }
+ if (min != -1) {
+ result->tm_min = min;
+ }
+ if (hour != -1) {
+ result->tm_hour = hour;
+ }
+ if (date != -1) {
+ result->tm_mday = date;
+ }
+ if (month != TT_UNKNOWN) {
+ result->tm_month = (((int)month) - ((int)TT_JAN));
+ }
+ if (year != -1) {
+ result->tm_year = year;
+ }
+ if (dotw != TT_UNKNOWN) {
+ result->tm_wday = (((int)dotw) - ((int)TT_SUN));
+ }
+ /*
+ * Mainly to compute wday and yday, but normalized time is also required
+ * by the check below that works around a Visual C++ 2005 mktime problem.
+ */
+ PR_NormalizeTime(result, PR_GMTParameters);
+ /* The remaining work is to set the gmt and dst offsets in tm_params. */
+
+ 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(result->tm_month > -1 &&
+ result->tm_mday > 0 &&
+ result->tm_hour > -1 &&
+ result->tm_min > -1 &&
+ result->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(result->tm_year >= 1970)
+ {
+ PRInt64 usec_per_sec;
+
+ localTime.tm_sec = result->tm_sec;
+ localTime.tm_min = result->tm_min;
+ localTime.tm_hour = result->tm_hour;
+ localTime.tm_mday = result->tm_mday;
+ localTime.tm_mon = result->tm_month;
+ localTime.tm_year = result->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;
+
+#if _MSC_VER == 1400 /* 1400 = Visual C++ 2005 (8.0) */
+ /*
+ * mktime will return (time_t) -1 if the input is a date
+ * after 23:59:59, December 31, 3000, US Pacific Time (not
+ * UTC as documented):
+ * http://msdn.microsoft.com/en-us/library/d1y53h2a(VS.80).aspx
+ * But if the year is 3001, mktime also invokes the invalid
+ * parameter handler, causing the application to crash. This
+ * problem has been reported in
+ * http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=266036.
+ * We avoid this crash by not calling mktime if the date is
+ * out of range. To use a simple test that works in any time
+ * zone, we consider year 3000 out of range as well. (See
+ * bug 480740.)
+ */
+ if (result->tm_year >= 3000) {
+ /* Emulate what mktime would have done. */
+ errno = EINVAL;
+ secs = (time_t) -1;
+ } else {
+ secs = mktime(&localTime);
+ }
+#else
+ secs = mktime(&localTime);
+#endif
+ if (secs != (time_t) -1)
+ {
+ PRTime usecs64;
+ LL_I2L(usecs64, secs);
+ LL_I2L(usec_per_sec, PR_USEC_PER_SEC);
+ LL_MUL(usecs64, usecs64, usec_per_sec);
+ PR_ExplodeTime(usecs64, PR_LocalTimeParameters, result);
+ 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;
+ localTimeResult = MT_safe_localtime(&secs, &localTime);
+ PR_ASSERT(localTimeResult != NULL);
+ if (localTimeResult == NULL) {
+ return PR_FAILURE;
+ }
+ zone_offset = localTime.tm_min
+ + 60 * localTime.tm_hour
+ + 1440 * (localTime.tm_mday - 2);
+ }
+
+ result->tm_params.tp_gmt_offset = zone_offset * 60;
+ result->tm_params.tp_dst_offset = dst_offset * 60;
+
+ return PR_SUCCESS;
+}
+
+PR_IMPLEMENT(PRStatus)
+PR_ParseTimeString(
+ const char *string,
+ PRBool default_to_gmt,
+ PRTime *result)
+{
+ PRExplodedTime tm;
+ PRStatus rv;
+
+ rv = PR_ParseTimeStringToExplodedTime(string,
+ default_to_gmt,
+ &tm);
+ if (rv != PR_SUCCESS) {
+ return rv;
+ }
+
+ *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 *time)
+{
+ size_t rv;
+ struct tm a;
+ struct tm *ap;
+
+ if (time) {
+ ap = &a;
+ a.tm_sec = time->tm_sec;
+ a.tm_min = time->tm_min;
+ a.tm_hour = time->tm_hour;
+ a.tm_mday = time->tm_mday;
+ a.tm_mon = time->tm_month;
+ a.tm_wday = time->tm_wday;
+ a.tm_year = time->tm_year - 1900;
+ a.tm_yday = time->tm_yday;
+ a.tm_isdst = time->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.
+ */
+
+#if (__GLIBC__ >= 2) || defined(NETBSD) \
+ || defined(OPENBSD) || defined(FREEBSD) \
+ || defined(DARWIN) || defined(ANDROID)
+ a.tm_zone = NULL;
+ a.tm_gmtoff = time->tm_params.tp_gmt_offset +
+ time->tm_params.tp_dst_offset;
+#endif
+ } else {
+ ap = NULL;
+ }
+
+ rv = strftime(buf, buflen, fmt, ap);
+ if (!rv && buf && buflen > 0) {
+ /*
+ * When strftime fails, the contents of buf are indeterminate.
+ * Some callers don't check the return value from this function,
+ * so store an empty string in buf in case they try to print it.
+ */
+ buf[0] = '\0';
+ }
+ return rv;
+}
+
+
+/*
+ * 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;
+ }
+
+ /* 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/nsprpub/pr/src/misc/prtpool.c b/nsprpub/pr/src/misc/prtpool.c
new file mode 100644
index 0000000000..69f588ef6e
--- /dev/null
+++ b/nsprpub/pr/src/misc/prtpool.c
@@ -0,0 +1,1236 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nspr.h"
+
+/*
+ * Thread pools
+ * Thread pools create and manage threads to provide support for
+ * scheduling jobs onto one or more threads.
+ *
+ */
+#ifdef OPT_WINNT
+#include <windows.h>
+#endif
+
+/*
+ * worker thread
+ */
+typedef struct wthread {
+ PRCList links;
+ PRThread *thread;
+} wthread;
+
+/*
+ * queue of timer jobs
+ */
+typedef struct timer_jobq {
+ PRCList list;
+ PRLock *lock;
+ PRCondVar *cv;
+ PRInt32 cnt;
+ PRCList wthreads;
+} timer_jobq;
+
+/*
+ * queue of jobs
+ */
+typedef struct tp_jobq {
+ PRCList list;
+ PRInt32 cnt;
+ PRLock *lock;
+ PRCondVar *cv;
+ PRCList wthreads;
+#ifdef OPT_WINNT
+ HANDLE nt_completion_port;
+#endif
+} tp_jobq;
+
+/*
+ * queue of IO jobs
+ */
+typedef struct io_jobq {
+ PRCList list;
+ PRPollDesc *pollfds;
+ PRInt32 npollfds;
+ PRJob **polljobs;
+ PRLock *lock;
+ PRInt32 cnt;
+ PRFileDesc *notify_fd;
+ PRCList wthreads;
+} io_jobq;
+
+/*
+ * Threadpool
+ */
+struct PRThreadPool {
+ PRInt32 init_threads;
+ PRInt32 max_threads;
+ PRInt32 current_threads;
+ PRInt32 idle_threads;
+ PRUint32 stacksize;
+ tp_jobq jobq;
+ io_jobq ioq;
+ timer_jobq timerq;
+ PRLock *join_lock; /* used with jobp->join_cv */
+ PRCondVar *shutdown_cv;
+ PRBool shutdown;
+};
+
+typedef enum io_op_type
+{ JOB_IO_READ, JOB_IO_WRITE, JOB_IO_CONNECT, JOB_IO_ACCEPT } io_op_type;
+
+#ifdef OPT_WINNT
+typedef struct NT_notifier {
+ OVERLAPPED overlapped; /* must be first */
+ PRJob *jobp;
+} NT_notifier;
+#endif
+
+struct PRJob {
+ PRCList links; /* for linking jobs */
+ PRBool on_ioq; /* job on ioq */
+ PRBool on_timerq; /* job on timerq */
+ PRJobFn job_func;
+ void *job_arg;
+ PRCondVar *join_cv;
+ PRBool join_wait; /* == PR_TRUE, when waiting to join */
+ PRCondVar *cancel_cv; /* for cancelling IO jobs */
+ PRBool cancel_io; /* for cancelling IO jobs */
+ PRThreadPool *tpool; /* back pointer to thread pool */
+ PRJobIoDesc *iod;
+ io_op_type io_op;
+ PRInt16 io_poll_flags;
+ PRNetAddr *netaddr;
+ PRIntervalTime timeout; /* relative value */
+ PRIntervalTime absolute;
+#ifdef OPT_WINNT
+ NT_notifier nt_notifier;
+#endif
+};
+
+#define JOB_LINKS_PTR(_qp) \
+ ((PRJob *) ((char *) (_qp) - offsetof(PRJob, links)))
+
+#define WTHREAD_LINKS_PTR(_qp) \
+ ((wthread *) ((char *) (_qp) - offsetof(wthread, links)))
+
+#define JOINABLE_JOB(_jobp) (NULL != (_jobp)->join_cv)
+
+#define JOIN_NOTIFY(_jobp) \
+ PR_BEGIN_MACRO \
+ PR_Lock(_jobp->tpool->join_lock); \
+ _jobp->join_wait = PR_FALSE; \
+ PR_NotifyCondVar(_jobp->join_cv); \
+ PR_Unlock(_jobp->tpool->join_lock); \
+ PR_END_MACRO
+
+#define CANCEL_IO_JOB(jobp) \
+ PR_BEGIN_MACRO \
+ jobp->cancel_io = PR_FALSE; \
+ jobp->on_ioq = PR_FALSE; \
+ PR_REMOVE_AND_INIT_LINK(&jobp->links); \
+ tp->ioq.cnt--; \
+ PR_NotifyCondVar(jobp->cancel_cv); \
+ PR_END_MACRO
+
+static void delete_job(PRJob *jobp);
+static PRThreadPool * alloc_threadpool(void);
+static PRJob * alloc_job(PRBool joinable, PRThreadPool *tp);
+static void notify_ioq(PRThreadPool *tp);
+static void notify_timerq(PRThreadPool *tp);
+
+/*
+ * locks are acquired in the following order
+ *
+ * tp->ioq.lock,tp->timerq.lock
+ * |
+ * V
+ * tp->jobq->lock
+ */
+
+/*
+ * worker thread function
+ */
+static void wstart(void *arg)
+{
+ PRThreadPool *tp = (PRThreadPool *) arg;
+ PRCList *head;
+
+ /*
+ * execute jobs until shutdown
+ */
+ while (!tp->shutdown) {
+ PRJob *jobp;
+#ifdef OPT_WINNT
+ BOOL rv;
+ DWORD unused, shutdown;
+ LPOVERLAPPED olp;
+
+ PR_Lock(tp->jobq.lock);
+ tp->idle_threads++;
+ PR_Unlock(tp->jobq.lock);
+ rv = GetQueuedCompletionStatus(tp->jobq.nt_completion_port,
+ &unused, &shutdown, &olp, INFINITE);
+
+ PR_ASSERT(rv);
+ if (shutdown) {
+ break;
+ }
+ jobp = ((NT_notifier *) olp)->jobp;
+ PR_Lock(tp->jobq.lock);
+ tp->idle_threads--;
+ tp->jobq.cnt--;
+ PR_Unlock(tp->jobq.lock);
+#else
+
+ PR_Lock(tp->jobq.lock);
+ while (PR_CLIST_IS_EMPTY(&tp->jobq.list) && (!tp->shutdown)) {
+ tp->idle_threads++;
+ PR_WaitCondVar(tp->jobq.cv, PR_INTERVAL_NO_TIMEOUT);
+ tp->idle_threads--;
+ }
+ if (tp->shutdown) {
+ PR_Unlock(tp->jobq.lock);
+ break;
+ }
+ head = PR_LIST_HEAD(&tp->jobq.list);
+ /*
+ * remove job from queue
+ */
+ PR_REMOVE_AND_INIT_LINK(head);
+ tp->jobq.cnt--;
+ jobp = JOB_LINKS_PTR(head);
+ PR_Unlock(tp->jobq.lock);
+#endif
+
+ jobp->job_func(jobp->job_arg);
+ if (!JOINABLE_JOB(jobp)) {
+ delete_job(jobp);
+ } else {
+ JOIN_NOTIFY(jobp);
+ }
+ }
+ PR_Lock(tp->jobq.lock);
+ tp->current_threads--;
+ PR_Unlock(tp->jobq.lock);
+}
+
+/*
+ * add a job to the work queue
+ */
+static void
+add_to_jobq(PRThreadPool *tp, PRJob *jobp)
+{
+ /*
+ * add to jobq
+ */
+#ifdef OPT_WINNT
+ PR_Lock(tp->jobq.lock);
+ tp->jobq.cnt++;
+ PR_Unlock(tp->jobq.lock);
+ /*
+ * notify worker thread(s)
+ */
+ PostQueuedCompletionStatus(tp->jobq.nt_completion_port, 0,
+ FALSE, &jobp->nt_notifier.overlapped);
+#else
+ PR_Lock(tp->jobq.lock);
+ PR_APPEND_LINK(&jobp->links,&tp->jobq.list);
+ tp->jobq.cnt++;
+ if ((tp->idle_threads < tp->jobq.cnt) &&
+ (tp->current_threads < tp->max_threads)) {
+ wthread *wthrp;
+ /*
+ * increment thread count and unlock the jobq lock
+ */
+ tp->current_threads++;
+ PR_Unlock(tp->jobq.lock);
+ /* create new worker thread */
+ wthrp = PR_NEWZAP(wthread);
+ if (wthrp) {
+ wthrp->thread = PR_CreateThread(PR_USER_THREAD, wstart,
+ tp, PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,PR_JOINABLE_THREAD,tp->stacksize);
+ if (NULL == wthrp->thread) {
+ PR_DELETE(wthrp); /* this sets wthrp to NULL */
+ }
+ }
+ PR_Lock(tp->jobq.lock);
+ if (NULL == wthrp) {
+ tp->current_threads--;
+ } else {
+ PR_APPEND_LINK(&wthrp->links, &tp->jobq.wthreads);
+ }
+ }
+ /*
+ * wakeup a worker thread
+ */
+ PR_NotifyCondVar(tp->jobq.cv);
+ PR_Unlock(tp->jobq.lock);
+#endif
+}
+
+/*
+ * io worker thread function
+ */
+static void io_wstart(void *arg)
+{
+ PRThreadPool *tp = (PRThreadPool *) arg;
+ int pollfd_cnt, pollfds_used;
+ int rv;
+ PRCList *qp, *nextqp;
+ PRPollDesc *pollfds = NULL;
+ PRJob **polljobs = NULL;
+ int poll_timeout;
+ PRIntervalTime now;
+
+ /*
+ * scan io_jobq
+ * construct poll list
+ * call PR_Poll
+ * for all fds, for which poll returns true, move the job to
+ * jobq and wakeup worker thread.
+ */
+ while (!tp->shutdown) {
+ PRJob *jobp;
+
+ pollfd_cnt = tp->ioq.cnt + 10;
+ if (pollfd_cnt > tp->ioq.npollfds) {
+
+ /*
+ * re-allocate pollfd array if the current one is not large
+ * enough
+ */
+ if (NULL != tp->ioq.pollfds) {
+ PR_Free(tp->ioq.pollfds);
+ }
+ tp->ioq.pollfds = (PRPollDesc *) PR_Malloc(pollfd_cnt *
+ (sizeof(PRPollDesc) + sizeof(PRJob *)));
+ PR_ASSERT(NULL != tp->ioq.pollfds);
+ /*
+ * array of pollfds
+ */
+ pollfds = tp->ioq.pollfds;
+ tp->ioq.polljobs = (PRJob **) (&tp->ioq.pollfds[pollfd_cnt]);
+ /*
+ * parallel array of jobs
+ */
+ polljobs = tp->ioq.polljobs;
+ tp->ioq.npollfds = pollfd_cnt;
+ }
+
+ pollfds_used = 0;
+ /*
+ * add the notify fd; used for unblocking io thread(s)
+ */
+ pollfds[pollfds_used].fd = tp->ioq.notify_fd;
+ pollfds[pollfds_used].in_flags = PR_POLL_READ;
+ pollfds[pollfds_used].out_flags = 0;
+ polljobs[pollfds_used] = NULL;
+ pollfds_used++;
+ /*
+ * fill in the pollfd array
+ */
+ PR_Lock(tp->ioq.lock);
+ for (qp = tp->ioq.list.next; qp != &tp->ioq.list; qp = nextqp) {
+ nextqp = qp->next;
+ jobp = JOB_LINKS_PTR(qp);
+ if (jobp->cancel_io) {
+ CANCEL_IO_JOB(jobp);
+ continue;
+ }
+ if (pollfds_used == (pollfd_cnt)) {
+ break;
+ }
+ pollfds[pollfds_used].fd = jobp->iod->socket;
+ pollfds[pollfds_used].in_flags = jobp->io_poll_flags;
+ pollfds[pollfds_used].out_flags = 0;
+ polljobs[pollfds_used] = jobp;
+
+ pollfds_used++;
+ }
+ if (!PR_CLIST_IS_EMPTY(&tp->ioq.list)) {
+ qp = tp->ioq.list.next;
+ jobp = JOB_LINKS_PTR(qp);
+ if (PR_INTERVAL_NO_TIMEOUT == jobp->timeout) {
+ poll_timeout = PR_INTERVAL_NO_TIMEOUT;
+ }
+ else if (PR_INTERVAL_NO_WAIT == jobp->timeout) {
+ poll_timeout = PR_INTERVAL_NO_WAIT;
+ }
+ else {
+ poll_timeout = jobp->absolute - PR_IntervalNow();
+ if (poll_timeout <= 0) { /* already timed out */
+ poll_timeout = PR_INTERVAL_NO_WAIT;
+ }
+ }
+ } else {
+ poll_timeout = PR_INTERVAL_NO_TIMEOUT;
+ }
+ PR_Unlock(tp->ioq.lock);
+
+ /*
+ * XXXX
+ * should retry if more jobs have been added to the queue?
+ *
+ */
+ PR_ASSERT(pollfds_used <= pollfd_cnt);
+ rv = PR_Poll(tp->ioq.pollfds, pollfds_used, poll_timeout);
+
+ if (tp->shutdown) {
+ break;
+ }
+
+ if (rv > 0) {
+ /*
+ * at least one io event is set
+ */
+ PRStatus rval_status;
+ PRInt32 index;
+
+ PR_ASSERT(pollfds[0].fd == tp->ioq.notify_fd);
+ /*
+ * reset the pollable event, if notified
+ */
+ if (pollfds[0].out_flags & PR_POLL_READ) {
+ rval_status = PR_WaitForPollableEvent(tp->ioq.notify_fd);
+ PR_ASSERT(PR_SUCCESS == rval_status);
+ }
+
+ for(index = 1; index < (pollfds_used); index++) {
+ PRInt16 events = pollfds[index].in_flags;
+ PRInt16 revents = pollfds[index].out_flags;
+ jobp = polljobs[index];
+
+ if ((revents & PR_POLL_NVAL) || /* busted in all cases */
+ (revents & PR_POLL_ERR) ||
+ ((events & PR_POLL_WRITE) &&
+ (revents & PR_POLL_HUP))) { /* write op & hup */
+ PR_Lock(tp->ioq.lock);
+ if (jobp->cancel_io) {
+ CANCEL_IO_JOB(jobp);
+ PR_Unlock(tp->ioq.lock);
+ continue;
+ }
+ PR_REMOVE_AND_INIT_LINK(&jobp->links);
+ tp->ioq.cnt--;
+ jobp->on_ioq = PR_FALSE;
+ PR_Unlock(tp->ioq.lock);
+
+ /* set error */
+ if (PR_POLL_NVAL & revents) {
+ jobp->iod->error = PR_BAD_DESCRIPTOR_ERROR;
+ }
+ else if (PR_POLL_HUP & revents) {
+ jobp->iod->error = PR_CONNECT_RESET_ERROR;
+ }
+ else {
+ jobp->iod->error = PR_IO_ERROR;
+ }
+
+ /*
+ * add to jobq
+ */
+ add_to_jobq(tp, jobp);
+ } else if (revents) {
+ /*
+ * add to jobq
+ */
+ PR_Lock(tp->ioq.lock);
+ if (jobp->cancel_io) {
+ CANCEL_IO_JOB(jobp);
+ PR_Unlock(tp->ioq.lock);
+ continue;
+ }
+ PR_REMOVE_AND_INIT_LINK(&jobp->links);
+ tp->ioq.cnt--;
+ jobp->on_ioq = PR_FALSE;
+ PR_Unlock(tp->ioq.lock);
+
+ if (jobp->io_op == JOB_IO_CONNECT) {
+ if (PR_GetConnectStatus(&pollfds[index]) == PR_SUCCESS) {
+ jobp->iod->error = 0;
+ }
+ else {
+ jobp->iod->error = PR_GetError();
+ }
+ } else {
+ jobp->iod->error = 0;
+ }
+
+ add_to_jobq(tp, jobp);
+ }
+ }
+ }
+ /*
+ * timeout processing
+ */
+ now = PR_IntervalNow();
+ PR_Lock(tp->ioq.lock);
+ for (qp = tp->ioq.list.next; qp != &tp->ioq.list; qp = nextqp) {
+ nextqp = qp->next;
+ jobp = JOB_LINKS_PTR(qp);
+ if (jobp->cancel_io) {
+ CANCEL_IO_JOB(jobp);
+ continue;
+ }
+ if (PR_INTERVAL_NO_TIMEOUT == jobp->timeout) {
+ break;
+ }
+ if ((PR_INTERVAL_NO_WAIT != jobp->timeout) &&
+ ((PRInt32)(jobp->absolute - now) > 0)) {
+ break;
+ }
+ PR_REMOVE_AND_INIT_LINK(&jobp->links);
+ tp->ioq.cnt--;
+ jobp->on_ioq = PR_FALSE;
+ jobp->iod->error = PR_IO_TIMEOUT_ERROR;
+ add_to_jobq(tp, jobp);
+ }
+ PR_Unlock(tp->ioq.lock);
+ }
+}
+
+/*
+ * timer worker thread function
+ */
+static void timer_wstart(void *arg)
+{
+ PRThreadPool *tp = (PRThreadPool *) arg;
+ PRCList *qp;
+ PRIntervalTime timeout;
+ PRIntervalTime now;
+
+ /*
+ * call PR_WaitCondVar with minimum value of all timeouts
+ */
+ while (!tp->shutdown) {
+ PRJob *jobp;
+
+ PR_Lock(tp->timerq.lock);
+ if (PR_CLIST_IS_EMPTY(&tp->timerq.list)) {
+ timeout = PR_INTERVAL_NO_TIMEOUT;
+ } else {
+ PRCList *qp;
+
+ qp = tp->timerq.list.next;
+ jobp = JOB_LINKS_PTR(qp);
+
+ timeout = jobp->absolute - PR_IntervalNow();
+ if (timeout <= 0) {
+ timeout = PR_INTERVAL_NO_WAIT; /* already timed out */
+ }
+ }
+ if (PR_INTERVAL_NO_WAIT != timeout) {
+ PR_WaitCondVar(tp->timerq.cv, timeout);
+ }
+ if (tp->shutdown) {
+ PR_Unlock(tp->timerq.lock);
+ break;
+ }
+ /*
+ * move expired-timer jobs to jobq
+ */
+ now = PR_IntervalNow();
+ while (!PR_CLIST_IS_EMPTY(&tp->timerq.list)) {
+ qp = tp->timerq.list.next;
+ jobp = JOB_LINKS_PTR(qp);
+
+ if ((PRInt32)(jobp->absolute - now) > 0) {
+ break;
+ }
+ /*
+ * job timed out
+ */
+ PR_REMOVE_AND_INIT_LINK(&jobp->links);
+ tp->timerq.cnt--;
+ jobp->on_timerq = PR_FALSE;
+ add_to_jobq(tp, jobp);
+ }
+ PR_Unlock(tp->timerq.lock);
+ }
+}
+
+static void
+delete_threadpool(PRThreadPool *tp)
+{
+ if (NULL != tp) {
+ if (NULL != tp->shutdown_cv) {
+ PR_DestroyCondVar(tp->shutdown_cv);
+ }
+ if (NULL != tp->jobq.cv) {
+ PR_DestroyCondVar(tp->jobq.cv);
+ }
+ if (NULL != tp->jobq.lock) {
+ PR_DestroyLock(tp->jobq.lock);
+ }
+ if (NULL != tp->join_lock) {
+ PR_DestroyLock(tp->join_lock);
+ }
+#ifdef OPT_WINNT
+ if (NULL != tp->jobq.nt_completion_port) {
+ CloseHandle(tp->jobq.nt_completion_port);
+ }
+#endif
+ /* Timer queue */
+ if (NULL != tp->timerq.cv) {
+ PR_DestroyCondVar(tp->timerq.cv);
+ }
+ if (NULL != tp->timerq.lock) {
+ PR_DestroyLock(tp->timerq.lock);
+ }
+
+ if (NULL != tp->ioq.lock) {
+ PR_DestroyLock(tp->ioq.lock);
+ }
+ if (NULL != tp->ioq.pollfds) {
+ PR_Free(tp->ioq.pollfds);
+ }
+ if (NULL != tp->ioq.notify_fd) {
+ PR_DestroyPollableEvent(tp->ioq.notify_fd);
+ }
+ PR_Free(tp);
+ }
+ return;
+}
+
+static PRThreadPool *
+alloc_threadpool(void)
+{
+ PRThreadPool *tp;
+
+ tp = (PRThreadPool *) PR_CALLOC(sizeof(*tp));
+ if (NULL == tp) {
+ goto failed;
+ }
+ tp->jobq.lock = PR_NewLock();
+ if (NULL == tp->jobq.lock) {
+ goto failed;
+ }
+ tp->jobq.cv = PR_NewCondVar(tp->jobq.lock);
+ if (NULL == tp->jobq.cv) {
+ goto failed;
+ }
+ tp->join_lock = PR_NewLock();
+ if (NULL == tp->join_lock) {
+ goto failed;
+ }
+#ifdef OPT_WINNT
+ tp->jobq.nt_completion_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
+ NULL, 0, 0);
+ if (NULL == tp->jobq.nt_completion_port) {
+ goto failed;
+ }
+#endif
+
+ tp->ioq.lock = PR_NewLock();
+ if (NULL == tp->ioq.lock) {
+ goto failed;
+ }
+
+ /* Timer queue */
+
+ tp->timerq.lock = PR_NewLock();
+ if (NULL == tp->timerq.lock) {
+ goto failed;
+ }
+ tp->timerq.cv = PR_NewCondVar(tp->timerq.lock);
+ if (NULL == tp->timerq.cv) {
+ goto failed;
+ }
+
+ tp->shutdown_cv = PR_NewCondVar(tp->jobq.lock);
+ if (NULL == tp->shutdown_cv) {
+ goto failed;
+ }
+ tp->ioq.notify_fd = PR_NewPollableEvent();
+ if (NULL == tp->ioq.notify_fd) {
+ goto failed;
+ }
+ return tp;
+failed:
+ delete_threadpool(tp);
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+}
+
+/* Create thread pool */
+PR_IMPLEMENT(PRThreadPool *)
+PR_CreateThreadPool(PRInt32 initial_threads, PRInt32 max_threads,
+ PRUint32 stacksize)
+{
+ PRThreadPool *tp;
+ PRThread *thr;
+ int i;
+ wthread *wthrp;
+
+ tp = alloc_threadpool();
+ if (NULL == tp) {
+ return NULL;
+ }
+
+ tp->init_threads = initial_threads;
+ tp->max_threads = max_threads;
+ tp->stacksize = stacksize;
+ PR_INIT_CLIST(&tp->jobq.list);
+ PR_INIT_CLIST(&tp->ioq.list);
+ PR_INIT_CLIST(&tp->timerq.list);
+ PR_INIT_CLIST(&tp->jobq.wthreads);
+ PR_INIT_CLIST(&tp->ioq.wthreads);
+ PR_INIT_CLIST(&tp->timerq.wthreads);
+ tp->shutdown = PR_FALSE;
+
+ PR_Lock(tp->jobq.lock);
+ for(i=0; i < initial_threads; ++i) {
+
+ thr = PR_CreateThread(PR_USER_THREAD, wstart,
+ tp, PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD, PR_JOINABLE_THREAD,stacksize);
+ PR_ASSERT(thr);
+ wthrp = PR_NEWZAP(wthread);
+ PR_ASSERT(wthrp);
+ wthrp->thread = thr;
+ PR_APPEND_LINK(&wthrp->links, &tp->jobq.wthreads);
+ }
+ tp->current_threads = initial_threads;
+
+ thr = PR_CreateThread(PR_USER_THREAD, io_wstart,
+ tp, PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,PR_JOINABLE_THREAD,stacksize);
+ PR_ASSERT(thr);
+ wthrp = PR_NEWZAP(wthread);
+ PR_ASSERT(wthrp);
+ wthrp->thread = thr;
+ PR_APPEND_LINK(&wthrp->links, &tp->ioq.wthreads);
+
+ thr = PR_CreateThread(PR_USER_THREAD, timer_wstart,
+ tp, PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,PR_JOINABLE_THREAD,stacksize);
+ PR_ASSERT(thr);
+ wthrp = PR_NEWZAP(wthread);
+ PR_ASSERT(wthrp);
+ wthrp->thread = thr;
+ PR_APPEND_LINK(&wthrp->links, &tp->timerq.wthreads);
+
+ PR_Unlock(tp->jobq.lock);
+ return tp;
+}
+
+static void
+delete_job(PRJob *jobp)
+{
+ if (NULL != jobp) {
+ if (NULL != jobp->join_cv) {
+ PR_DestroyCondVar(jobp->join_cv);
+ jobp->join_cv = NULL;
+ }
+ if (NULL != jobp->cancel_cv) {
+ PR_DestroyCondVar(jobp->cancel_cv);
+ jobp->cancel_cv = NULL;
+ }
+ PR_DELETE(jobp);
+ }
+}
+
+static PRJob *
+alloc_job(PRBool joinable, PRThreadPool *tp)
+{
+ PRJob *jobp;
+
+ jobp = PR_NEWZAP(PRJob);
+ if (NULL == jobp) {
+ goto failed;
+ }
+ if (joinable) {
+ jobp->join_cv = PR_NewCondVar(tp->join_lock);
+ jobp->join_wait = PR_TRUE;
+ if (NULL == jobp->join_cv) {
+ goto failed;
+ }
+ } else {
+ jobp->join_cv = NULL;
+ }
+#ifdef OPT_WINNT
+ jobp->nt_notifier.jobp = jobp;
+#endif
+ return jobp;
+failed:
+ delete_job(jobp);
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return NULL;
+}
+
+/* queue a job */
+PR_IMPLEMENT(PRJob *)
+PR_QueueJob(PRThreadPool *tpool, PRJobFn fn, void *arg, PRBool joinable)
+{
+ PRJob *jobp;
+
+ jobp = alloc_job(joinable, tpool);
+ if (NULL == jobp) {
+ return NULL;
+ }
+
+ jobp->job_func = fn;
+ jobp->job_arg = arg;
+ jobp->tpool = tpool;
+
+ add_to_jobq(tpool, jobp);
+ return jobp;
+}
+
+/* queue a job, when a socket is readable or writeable */
+static PRJob *
+queue_io_job(PRThreadPool *tpool, PRJobIoDesc *iod, PRJobFn fn, void * arg,
+ PRBool joinable, io_op_type op)
+{
+ PRJob *jobp;
+ PRIntervalTime now;
+
+ jobp = alloc_job(joinable, tpool);
+ if (NULL == jobp) {
+ return NULL;
+ }
+
+ /*
+ * Add a new job to io_jobq
+ * wakeup io worker thread
+ */
+
+ jobp->job_func = fn;
+ jobp->job_arg = arg;
+ jobp->tpool = tpool;
+ jobp->iod = iod;
+ if (JOB_IO_READ == op) {
+ jobp->io_op = JOB_IO_READ;
+ jobp->io_poll_flags = PR_POLL_READ;
+ } else if (JOB_IO_WRITE == op) {
+ jobp->io_op = JOB_IO_WRITE;
+ jobp->io_poll_flags = PR_POLL_WRITE;
+ } else if (JOB_IO_ACCEPT == op) {
+ jobp->io_op = JOB_IO_ACCEPT;
+ jobp->io_poll_flags = PR_POLL_READ;
+ } else if (JOB_IO_CONNECT == op) {
+ jobp->io_op = JOB_IO_CONNECT;
+ jobp->io_poll_flags = PR_POLL_WRITE|PR_POLL_EXCEPT;
+ } else {
+ delete_job(jobp);
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+ }
+
+ jobp->timeout = iod->timeout;
+ if ((PR_INTERVAL_NO_TIMEOUT == iod->timeout) ||
+ (PR_INTERVAL_NO_WAIT == iod->timeout)) {
+ jobp->absolute = iod->timeout;
+ } else {
+ now = PR_IntervalNow();
+ jobp->absolute = now + iod->timeout;
+ }
+
+
+ PR_Lock(tpool->ioq.lock);
+
+ if (PR_CLIST_IS_EMPTY(&tpool->ioq.list) ||
+ (PR_INTERVAL_NO_TIMEOUT == iod->timeout)) {
+ PR_APPEND_LINK(&jobp->links,&tpool->ioq.list);
+ } else if (PR_INTERVAL_NO_WAIT == iod->timeout) {
+ PR_INSERT_LINK(&jobp->links,&tpool->ioq.list);
+ } else {
+ PRCList *qp;
+ PRJob *tmp_jobp;
+ /*
+ * insert into the timeout-sorted ioq
+ */
+ for (qp = tpool->ioq.list.prev; qp != &tpool->ioq.list;
+ qp = qp->prev) {
+ tmp_jobp = JOB_LINKS_PTR(qp);
+ if ((PRInt32)(jobp->absolute - tmp_jobp->absolute) >= 0) {
+ break;
+ }
+ }
+ PR_INSERT_AFTER(&jobp->links,qp);
+ }
+
+ jobp->on_ioq = PR_TRUE;
+ tpool->ioq.cnt++;
+ /*
+ * notify io worker thread(s)
+ */
+ PR_Unlock(tpool->ioq.lock);
+ notify_ioq(tpool);
+ return jobp;
+}
+
+/* queue a job, when a socket is readable */
+PR_IMPLEMENT(PRJob *)
+PR_QueueJob_Read(PRThreadPool *tpool, PRJobIoDesc *iod, PRJobFn fn, void * arg,
+ PRBool joinable)
+{
+ return (queue_io_job(tpool, iod, fn, arg, joinable, JOB_IO_READ));
+}
+
+/* queue a job, when a socket is writeable */
+PR_IMPLEMENT(PRJob *)
+PR_QueueJob_Write(PRThreadPool *tpool, PRJobIoDesc *iod, PRJobFn fn,void * arg,
+ PRBool joinable)
+{
+ return (queue_io_job(tpool, iod, fn, arg, joinable, JOB_IO_WRITE));
+}
+
+
+/* queue a job, when a socket has a pending connection */
+PR_IMPLEMENT(PRJob *)
+PR_QueueJob_Accept(PRThreadPool *tpool, PRJobIoDesc *iod, PRJobFn fn,
+ void * arg, PRBool joinable)
+{
+ return (queue_io_job(tpool, iod, fn, arg, joinable, JOB_IO_ACCEPT));
+}
+
+/* queue a job, when a socket can be connected */
+PR_IMPLEMENT(PRJob *)
+PR_QueueJob_Connect(PRThreadPool *tpool, PRJobIoDesc *iod,
+ const PRNetAddr *addr, PRJobFn fn, void * arg, PRBool joinable)
+{
+ PRStatus rv;
+ PRErrorCode err;
+
+ rv = PR_Connect(iod->socket, addr, PR_INTERVAL_NO_WAIT);
+ if ((rv == PR_FAILURE) && ((err = PR_GetError()) == PR_IN_PROGRESS_ERROR)) {
+ /* connection pending */
+ return(queue_io_job(tpool, iod, fn, arg, joinable, JOB_IO_CONNECT));
+ }
+ /*
+ * connection succeeded or failed; add to jobq right away
+ */
+ if (rv == PR_FAILURE) {
+ iod->error = err;
+ }
+ else {
+ iod->error = 0;
+ }
+ return(PR_QueueJob(tpool, fn, arg, joinable));
+
+}
+
+/* queue a job, when a timer expires */
+PR_IMPLEMENT(PRJob *)
+PR_QueueJob_Timer(PRThreadPool *tpool, PRIntervalTime timeout,
+ PRJobFn fn, void * arg, PRBool joinable)
+{
+ PRIntervalTime now;
+ PRJob *jobp;
+
+ if (PR_INTERVAL_NO_TIMEOUT == timeout) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+ }
+ if (PR_INTERVAL_NO_WAIT == timeout) {
+ /*
+ * no waiting; add to jobq right away
+ */
+ return(PR_QueueJob(tpool, fn, arg, joinable));
+ }
+ jobp = alloc_job(joinable, tpool);
+ if (NULL == jobp) {
+ return NULL;
+ }
+
+ /*
+ * Add a new job to timer_jobq
+ * wakeup timer worker thread
+ */
+
+ jobp->job_func = fn;
+ jobp->job_arg = arg;
+ jobp->tpool = tpool;
+ jobp->timeout = timeout;
+
+ now = PR_IntervalNow();
+ jobp->absolute = now + timeout;
+
+
+ PR_Lock(tpool->timerq.lock);
+ jobp->on_timerq = PR_TRUE;
+ if (PR_CLIST_IS_EMPTY(&tpool->timerq.list)) {
+ PR_APPEND_LINK(&jobp->links,&tpool->timerq.list);
+ }
+ else {
+ PRCList *qp;
+ PRJob *tmp_jobp;
+ /*
+ * insert into the sorted timer jobq
+ */
+ for (qp = tpool->timerq.list.prev; qp != &tpool->timerq.list;
+ qp = qp->prev) {
+ tmp_jobp = JOB_LINKS_PTR(qp);
+ if ((PRInt32)(jobp->absolute - tmp_jobp->absolute) >= 0) {
+ break;
+ }
+ }
+ PR_INSERT_AFTER(&jobp->links,qp);
+ }
+ tpool->timerq.cnt++;
+ /*
+ * notify timer worker thread(s)
+ */
+ notify_timerq(tpool);
+ PR_Unlock(tpool->timerq.lock);
+ return jobp;
+}
+
+static void
+notify_timerq(PRThreadPool *tp)
+{
+ /*
+ * wakeup the timer thread(s)
+ */
+ PR_NotifyCondVar(tp->timerq.cv);
+}
+
+static void
+notify_ioq(PRThreadPool *tp)
+{
+ PRStatus rval_status;
+
+ /*
+ * wakeup the io thread(s)
+ */
+ rval_status = PR_SetPollableEvent(tp->ioq.notify_fd);
+ PR_ASSERT(PR_SUCCESS == rval_status);
+}
+
+/*
+ * cancel a job
+ *
+ * XXXX: is this needed? likely to be removed
+ */
+PR_IMPLEMENT(PRStatus)
+PR_CancelJob(PRJob *jobp) {
+
+ PRStatus rval = PR_FAILURE;
+ PRThreadPool *tp;
+
+ if (jobp->on_timerq) {
+ /*
+ * now, check again while holding the timerq lock
+ */
+ tp = jobp->tpool;
+ PR_Lock(tp->timerq.lock);
+ if (jobp->on_timerq) {
+ jobp->on_timerq = PR_FALSE;
+ PR_REMOVE_AND_INIT_LINK(&jobp->links);
+ tp->timerq.cnt--;
+ PR_Unlock(tp->timerq.lock);
+ if (!JOINABLE_JOB(jobp)) {
+ delete_job(jobp);
+ } else {
+ JOIN_NOTIFY(jobp);
+ }
+ rval = PR_SUCCESS;
+ } else {
+ PR_Unlock(tp->timerq.lock);
+ }
+ } else if (jobp->on_ioq) {
+ /*
+ * now, check again while holding the ioq lock
+ */
+ tp = jobp->tpool;
+ PR_Lock(tp->ioq.lock);
+ if (jobp->on_ioq) {
+ jobp->cancel_cv = PR_NewCondVar(tp->ioq.lock);
+ if (NULL == jobp->cancel_cv) {
+ PR_Unlock(tp->ioq.lock);
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
+ return PR_FAILURE;
+ }
+ /*
+ * mark job 'cancelled' and notify io thread(s)
+ * XXXX:
+ * this assumes there is only one io thread; when there
+ * are multiple threads, the io thread processing this job
+ * must be notified.
+ */
+ jobp->cancel_io = PR_TRUE;
+ PR_Unlock(tp->ioq.lock); /* release, reacquire ioq lock */
+ notify_ioq(tp);
+ PR_Lock(tp->ioq.lock);
+ while (jobp->cancel_io) {
+ PR_WaitCondVar(jobp->cancel_cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(tp->ioq.lock);
+ PR_ASSERT(!jobp->on_ioq);
+ if (!JOINABLE_JOB(jobp)) {
+ delete_job(jobp);
+ } else {
+ JOIN_NOTIFY(jobp);
+ }
+ rval = PR_SUCCESS;
+ } else {
+ PR_Unlock(tp->ioq.lock);
+ }
+ }
+ if (PR_FAILURE == rval) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ }
+ return rval;
+}
+
+/* join a job, wait until completion */
+PR_IMPLEMENT(PRStatus)
+PR_JoinJob(PRJob *jobp)
+{
+ if (!JOINABLE_JOB(jobp)) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+ }
+ PR_Lock(jobp->tpool->join_lock);
+ while(jobp->join_wait) {
+ PR_WaitCondVar(jobp->join_cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+ PR_Unlock(jobp->tpool->join_lock);
+ delete_job(jobp);
+ return PR_SUCCESS;
+}
+
+/* shutdown threadpool */
+PR_IMPLEMENT(PRStatus)
+PR_ShutdownThreadPool(PRThreadPool *tpool)
+{
+ PRStatus rval = PR_SUCCESS;
+
+ PR_Lock(tpool->jobq.lock);
+ tpool->shutdown = PR_TRUE;
+ PR_NotifyAllCondVar(tpool->shutdown_cv);
+ PR_Unlock(tpool->jobq.lock);
+
+ return rval;
+}
+
+/*
+ * join thread pool
+ * wait for termination of worker threads
+ * reclaim threadpool resources
+ */
+PR_IMPLEMENT(PRStatus)
+PR_JoinThreadPool(PRThreadPool *tpool)
+{
+ PRStatus rval = PR_SUCCESS;
+ PRCList *head;
+ PRStatus rval_status;
+
+ PR_Lock(tpool->jobq.lock);
+ while (!tpool->shutdown) {
+ PR_WaitCondVar(tpool->shutdown_cv, PR_INTERVAL_NO_TIMEOUT);
+ }
+
+ /*
+ * wakeup worker threads
+ */
+#ifdef OPT_WINNT
+ /*
+ * post shutdown notification for all threads
+ */
+ {
+ int i;
+ for(i=0; i < tpool->current_threads; i++) {
+ PostQueuedCompletionStatus(tpool->jobq.nt_completion_port, 0,
+ TRUE, NULL);
+ }
+ }
+#else
+ PR_NotifyAllCondVar(tpool->jobq.cv);
+#endif
+
+ /*
+ * wakeup io thread(s)
+ */
+ notify_ioq(tpool);
+
+ /*
+ * wakeup timer thread(s)
+ */
+ PR_Lock(tpool->timerq.lock);
+ notify_timerq(tpool);
+ PR_Unlock(tpool->timerq.lock);
+
+ while (!PR_CLIST_IS_EMPTY(&tpool->jobq.wthreads)) {
+ wthread *wthrp;
+
+ head = PR_LIST_HEAD(&tpool->jobq.wthreads);
+ PR_REMOVE_AND_INIT_LINK(head);
+ PR_Unlock(tpool->jobq.lock);
+ wthrp = WTHREAD_LINKS_PTR(head);
+ rval_status = PR_JoinThread(wthrp->thread);
+ PR_ASSERT(PR_SUCCESS == rval_status);
+ PR_DELETE(wthrp);
+ PR_Lock(tpool->jobq.lock);
+ }
+ PR_Unlock(tpool->jobq.lock);
+ while (!PR_CLIST_IS_EMPTY(&tpool->ioq.wthreads)) {
+ wthread *wthrp;
+
+ head = PR_LIST_HEAD(&tpool->ioq.wthreads);
+ PR_REMOVE_AND_INIT_LINK(head);
+ wthrp = WTHREAD_LINKS_PTR(head);
+ rval_status = PR_JoinThread(wthrp->thread);
+ PR_ASSERT(PR_SUCCESS == rval_status);
+ PR_DELETE(wthrp);
+ }
+
+ while (!PR_CLIST_IS_EMPTY(&tpool->timerq.wthreads)) {
+ wthread *wthrp;
+
+ head = PR_LIST_HEAD(&tpool->timerq.wthreads);
+ PR_REMOVE_AND_INIT_LINK(head);
+ wthrp = WTHREAD_LINKS_PTR(head);
+ rval_status = PR_JoinThread(wthrp->thread);
+ PR_ASSERT(PR_SUCCESS == rval_status);
+ PR_DELETE(wthrp);
+ }
+
+ /*
+ * Delete queued jobs
+ */
+ while (!PR_CLIST_IS_EMPTY(&tpool->jobq.list)) {
+ PRJob *jobp;
+
+ head = PR_LIST_HEAD(&tpool->jobq.list);
+ PR_REMOVE_AND_INIT_LINK(head);
+ jobp = JOB_LINKS_PTR(head);
+ tpool->jobq.cnt--;
+ delete_job(jobp);
+ }
+
+ /* delete io jobs */
+ while (!PR_CLIST_IS_EMPTY(&tpool->ioq.list)) {
+ PRJob *jobp;
+
+ head = PR_LIST_HEAD(&tpool->ioq.list);
+ PR_REMOVE_AND_INIT_LINK(head);
+ tpool->ioq.cnt--;
+ jobp = JOB_LINKS_PTR(head);
+ delete_job(jobp);
+ }
+
+ /* delete timer jobs */
+ while (!PR_CLIST_IS_EMPTY(&tpool->timerq.list)) {
+ PRJob *jobp;
+
+ head = PR_LIST_HEAD(&tpool->timerq.list);
+ PR_REMOVE_AND_INIT_LINK(head);
+ tpool->timerq.cnt--;
+ jobp = JOB_LINKS_PTR(head);
+ delete_job(jobp);
+ }
+
+ PR_ASSERT(0 == tpool->jobq.cnt);
+ PR_ASSERT(0 == tpool->ioq.cnt);
+ PR_ASSERT(0 == tpool->timerq.cnt);
+
+ delete_threadpool(tpool);
+ return rval;
+}
diff --git a/nsprpub/pr/src/misc/prtrace.c b/nsprpub/pr/src/misc/prtrace.c
new file mode 100644
index 0000000000..ef6b65104e
--- /dev/null
+++ b/nsprpub/pr/src/misc/prtrace.c
@@ -0,0 +1,901 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** prtrace.c -- NSPR Trace Instrumentation
+**
+** Implement the API defined in prtrace.h
+**
+**
+**
+*/
+
+#include <string.h>
+#include "primpl.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 *)&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 *)&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, lose 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_Unlock( 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 = NULL;
+ 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_GetEnvSecure( "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() */
+
+/* end prtrace.c */