Merge "Update timezone data to 2015g"
diff --git a/libc/Android.mk b/libc/Android.mk
index 6919fbb..140ec82 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -1,6 +1,6 @@
LOCAL_PATH := $(call my-dir)
-bionic_coverage := false
+bionic_coverage ?= false
# Make everything depend on any changes to included makefiles.
libc_common_additional_dependencies := $(LOCAL_PATH)/Android.mk
@@ -59,6 +59,7 @@
bionic/system_properties_compat.c \
stdio/findfp.c \
stdio/fread.c \
+ stdio/refill.c \
stdio/snprintf.c\
stdio/sprintf.c \
stdio/stdio.c \
@@ -104,6 +105,7 @@
bionic/accept.cpp \
bionic/accept4.cpp \
bionic/access.cpp \
+ bionic/arpa_inet.cpp \
bionic/assert.cpp \
bionic/atof.cpp \
bionic/bionic_systrace.cpp \
@@ -395,11 +397,9 @@
upstream-openbsd/lib/libc/locale/wctomb.c \
upstream-openbsd/lib/libc/net/htonl.c \
upstream-openbsd/lib/libc/net/htons.c \
- upstream-openbsd/lib/libc/net/inet_addr.c \
upstream-openbsd/lib/libc/net/inet_lnaof.c \
upstream-openbsd/lib/libc/net/inet_makeaddr.c \
upstream-openbsd/lib/libc/net/inet_netof.c \
- upstream-openbsd/lib/libc/net/inet_network.c \
upstream-openbsd/lib/libc/net/inet_ntoa.c \
upstream-openbsd/lib/libc/net/inet_ntop.c \
upstream-openbsd/lib/libc/net/inet_pton.c \
@@ -459,7 +459,6 @@
upstream-openbsd/lib/libc/stdio/puts.c \
upstream-openbsd/lib/libc/stdio/putwc.c \
upstream-openbsd/lib/libc/stdio/putwchar.c \
- upstream-openbsd/lib/libc/stdio/refill.c \
upstream-openbsd/lib/libc/stdio/remove.c \
upstream-openbsd/lib/libc/stdio/rewind.c \
upstream-openbsd/lib/libc/stdio/rget.c \
@@ -688,11 +687,13 @@
LOCAL_CFLAGS := $(libc_common_cflags) \
-fvisibility=hidden \
+ -Wno-unused-parameter \
# Don't use ridiculous amounts of stack.
LOCAL_CFLAGS += -DALL_STATE
# Include tzsetwall, timelocal, timegm, time2posix, and posix2time.
LOCAL_CFLAGS += -DSTD_INSPIRED
+LOCAL_CFLAGS += -DTHREAD_SAFE
# The name of the tm_gmtoff field in our struct tm.
LOCAL_CFLAGS += -DTM_GMTOFF=tm_gmtoff
# Where we store our tzdata.
diff --git a/libc/arch-mips/bionic/setjmp.S b/libc/arch-mips/bionic/setjmp.S
index 3ef0f11..73002e8 100644
--- a/libc/arch-mips/bionic/setjmp.S
+++ b/libc/arch-mips/bionic/setjmp.S
@@ -132,10 +132,9 @@
/* field: byte offset: size: */
/* dynam filler (0*4) 0-4 bytes of rounddown filler, DON'T TOUCH!!
often overlays user storage!! */
-#define SC_MAGIC_OFFSET (1*4) /* 4 bytes, identify jmpbuf, first actual field */
-#define SC_FLAG_OFFSET (2*4) /* 4 bytes, savesigs flag */
-#define SC_FPSR_OFFSET (3*4) /* 4 bytes, floating point control/status reg */
+#define SC_FPSR_OFFSET (1*4) /* 4 bytes, floating point control/status reg */
/* following fields are 8-byte aligned */
+#define SC_FLAG_OFFSET (2*4) /* 8 bytes, cookie and savesigs flag, first actual field */
#define SC_MASK_OFFSET (4*4) /* 16 bytes, mips32/mips64 version of sigset_t */
#define SC_SPARE_OFFSET (8*4) /* 8 bytes, reserved for future uses */
@@ -166,6 +165,16 @@
#error _JBLEN is too small
#endif
+.macro m_mangle_reg_and_store reg, cookie, temp, offset
+ xor \temp, \reg, \cookie
+ REG_S \temp, \offset
+.endm
+
+.macro m_unmangle_reg_and_load reg, cookie, temp, offset
+ REG_L \temp, \offset
+ xor \reg, \temp, \cookie
+.endm
+
/*
*
* GPOFF and FRAMESIZE must be the same for all setjmp/longjmp routines
@@ -190,36 +199,46 @@
li t0, ~7
and a0, t0 # round jmpbuf addr DOWN to 8-byte boundary
#endif
- sw a1, SC_FLAG_OFFSET(a0) # save savesigs flag
- beqz a1, 1f # do saving of signal mask?
-
REG_S ra, RAOFF(sp) # spill state
REG_S a0, A0OFF(sp)
+
+ # get the cookie and store it along with the signal flag.
+ move a0, a1
+ jal __bionic_setjmp_cookie_get
+ REG_L a0, A0OFF(sp)
+
+ REG_S v0, SC_FLAG_OFFSET(a0) # save cookie and savesigs flag
+ andi t0, v0, 1 # extract savesigs flag
+
+ beqz t0, 1f # do saving of signal mask?
+
# call sigprocmask(int how ignored, sigset_t* null, sigset_t* SC_MASK(a0)):
LA a2, SC_MASK_OFFSET(a0) # gets current signal mask
li a0, 0 # how; ignored when new mask is null
li a1, 0 # null new mask
jal sigprocmask # get current signal mask
REG_L a0, A0OFF(sp)
- REG_L ra, RAOFF(sp)
1:
- li v0, 0xACEDBADE # sigcontext magic number
- sw v0, SC_MAGIC_OFFSET(a0)
+ REG_L gp, GPOFF(sp) # restore spills
+ REG_L ra, RAOFF(sp)
+ REG_L t0, SC_FLAG_OFFSET(a0) # move cookie to temp reg
+
# callee-saved long-sized regs:
- REG_S ra, SC_REGS+0*REGSZ(a0)
- REG_S s0, SC_REGS+1*REGSZ(a0)
- REG_S s1, SC_REGS+2*REGSZ(a0)
- REG_S s2, SC_REGS+3*REGSZ(a0)
- REG_S s3, SC_REGS+4*REGSZ(a0)
- REG_S s4, SC_REGS+5*REGSZ(a0)
- REG_S s5, SC_REGS+6*REGSZ(a0)
- REG_S s6, SC_REGS+7*REGSZ(a0)
- REG_S s7, SC_REGS+8*REGSZ(a0)
- REG_S s8, SC_REGS+9*REGSZ(a0)
- REG_L v0, GPOFF(sp)
- REG_S v0, SC_REGS+10*REGSZ(a0) # save gp
- PTR_ADDU v0, sp, FRAMESZ
- REG_S v0, SC_REGS+11*REGSZ(a0) # save orig sp
+ PTR_ADDU v1, sp, FRAMESZ # save orig sp
+
+ # m_mangle_reg_and_store reg, cookie, temp, offset
+ m_mangle_reg_and_store ra, t0, t1, SC_REGS+0*REGSZ(a0)
+ m_mangle_reg_and_store s0, t0, t2, SC_REGS+1*REGSZ(a0)
+ m_mangle_reg_and_store s1, t0, t3, SC_REGS+2*REGSZ(a0)
+ m_mangle_reg_and_store s2, t0, t1, SC_REGS+3*REGSZ(a0)
+ m_mangle_reg_and_store s3, t0, t2, SC_REGS+4*REGSZ(a0)
+ m_mangle_reg_and_store s4, t0, t3, SC_REGS+5*REGSZ(a0)
+ m_mangle_reg_and_store s5, t0, t1, SC_REGS+6*REGSZ(a0)
+ m_mangle_reg_and_store s6, t0, t2, SC_REGS+7*REGSZ(a0)
+ m_mangle_reg_and_store s7, t0, t3, SC_REGS+8*REGSZ(a0)
+ m_mangle_reg_and_store s8, t0, t1, SC_REGS+9*REGSZ(a0)
+ m_mangle_reg_and_store gp, t0, t2, SC_REGS+10*REGSZ(a0)
+ m_mangle_reg_and_store v1, t0, t3, SC_REGS+11*REGSZ(a0)
cfc1 v0, $31
@@ -288,36 +307,41 @@
li t0, ~7
and a0, t0 # round jmpbuf addr DOWN to 8-byte boundary
#endif
- lw v0, SC_MAGIC_OFFSET(a0)
- li t0, 0xACEDBADE
- bne v0, t0, longjmp_botch # jump if error
- lw t0, SC_FLAG_OFFSET(a0) # get savesigs flag
+ move s1, a1 # temp spill
+ move s0, a0
+
+ # extract savesigs flag
+ REG_L s2, SC_FLAG_OFFSET(s0)
+ andi t0, s2, 1
beqz t0, 1f # restore signal mask?
- REG_S a1, A1OFF(sp) # temp spill
- REG_S a0, A0OFF(sp)
- # call sigprocmask(int how SIG_SETMASK, sigset_t* SC_MASK(a0), sigset_t* null):
- LA a1, SC_MASK_OFFSET(a0) # signals being restored
+ # call sigprocmask(int how SIG_SETMASK, sigset_t* SC_MASK(a0), sigset_t* null):
+ LA a1, SC_MASK_OFFSET(s0) # signals being restored
li a0, 3 # mips SIG_SETMASK
li a2, 0 # null
jal sigprocmask # restore signal mask
- REG_L a0, A0OFF(sp)
- REG_L a1, A1OFF(sp)
1:
+ move t0, s2 # get cookie to temp reg
+ move a1, s1
+ move a0, s0
+
# callee-saved long-sized regs:
- REG_L ra, SC_REGS+0*REGSZ(a0)
- REG_L s0, SC_REGS+1*REGSZ(a0)
- REG_L s1, SC_REGS+2*REGSZ(a0)
- REG_L s2, SC_REGS+3*REGSZ(a0)
- REG_L s3, SC_REGS+4*REGSZ(a0)
- REG_L s4, SC_REGS+5*REGSZ(a0)
- REG_L s5, SC_REGS+6*REGSZ(a0)
- REG_L s6, SC_REGS+7*REGSZ(a0)
- REG_L s7, SC_REGS+8*REGSZ(a0)
- REG_L s8, SC_REGS+9*REGSZ(a0)
- REG_L gp, SC_REGS+10*REGSZ(a0)
- REG_L sp, SC_REGS+11*REGSZ(a0)
+
+ # m_unmangle_reg_and_load reg, cookie, temp, offset
+ # don't restore gp yet, old value is needed for cookie_check call
+ m_unmangle_reg_and_load ra, t0, t1, SC_REGS+0*REGSZ(a0)
+ m_unmangle_reg_and_load s0, t0, t2, SC_REGS+1*REGSZ(a0)
+ m_unmangle_reg_and_load s1, t0, t3, SC_REGS+2*REGSZ(a0)
+ m_unmangle_reg_and_load s2, t0, t1, SC_REGS+3*REGSZ(a0)
+ m_unmangle_reg_and_load s3, t0, t2, SC_REGS+4*REGSZ(a0)
+ m_unmangle_reg_and_load s4, t0, t3, SC_REGS+5*REGSZ(a0)
+ m_unmangle_reg_and_load s5, t0, t1, SC_REGS+6*REGSZ(a0)
+ m_unmangle_reg_and_load s6, t0, t2, SC_REGS+7*REGSZ(a0)
+ m_unmangle_reg_and_load s7, t0, t3, SC_REGS+8*REGSZ(a0)
+ m_unmangle_reg_and_load s8, t0, t1, SC_REGS+9*REGSZ(a0)
+ m_unmangle_reg_and_load v1, t0, t2, SC_REGS+10*REGSZ(a0)
+ m_unmangle_reg_and_load sp, t0, t3, SC_REGS+11*REGSZ(a0)
lw v0, SC_FPSR_OFFSET(a0)
ctc1 v0, $31 # restore old fr mode before fp values
@@ -341,15 +365,22 @@
l.d $f28, SC_FPREGS+4*REGSZ_FP(a0)
l.d $f30, SC_FPREGS+5*REGSZ_FP(a0)
#endif
- bne a1, zero, 1f
- li a1, 1 # never return 0!
-1:
- move v0, a1
- j ra # return to setjmp call site
-longjmp_botch:
- jal longjmperror
- jal abort
+ # check cookie
+ PTR_SUBU sp, FRAMESZ
+ REG_S v1, GPOFF(sp)
+ REG_S ra, RAOFF(sp)
+ REG_S a1, A1OFF(sp)
+ move a0, t0
+ jal __bionic_setjmp_cookie_check
+ REG_L gp, GPOFF(sp)
+ REG_L ra, RAOFF(sp)
+ REG_L a1, A1OFF(sp)
+ PTR_ADDU sp, FRAMESZ
+
+ sltiu t0, a1, 1 # never return 0!
+ xor v0, a1, t0
+ j ra # return to setjmp call site
END(siglongjmp)
ALIAS_SYMBOL(longjmp, siglongjmp)
diff --git a/libc/arch-x86/bionic/setjmp.S b/libc/arch-x86/bionic/setjmp.S
index 8a2f30c..86e6e3c 100644
--- a/libc/arch-x86/bionic/setjmp.S
+++ b/libc/arch-x86/bionic/setjmp.S
@@ -151,9 +151,11 @@
PIC_PROLOGUE
pushl %eax
+ pushl %ecx
pushl (_JB_SIGFLAG * 4)(%edx)
call PIC_PLT(__bionic_setjmp_cookie_check)
addl $4,%esp
+ popl %ecx
popl %eax
PIC_EPILOGUE
diff --git a/libc/arch-x86_64/bionic/setjmp.S b/libc/arch-x86_64/bionic/setjmp.S
index 09d61f5..56ebb07 100644
--- a/libc/arch-x86_64/bionic/setjmp.S
+++ b/libc/arch-x86_64/bionic/setjmp.S
@@ -159,8 +159,10 @@
// Check the cookie.
pushq %rax
+ pushq %r11
movq %rcx, %rdi
call PIC_PLT(__bionic_setjmp_cookie_check)
+ popq %r11
popq %rax
// Return 1 if value is 0.
diff --git a/libc/bionic/arpa_inet.cpp b/libc/bionic/arpa_inet.cpp
new file mode 100644
index 0000000..9d4afe3
--- /dev/null
+++ b/libc/bionic/arpa_inet.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+
+#include "private/ErrnoRestorer.h"
+
+// The difference between inet_network(3) and inet_addr(3) is that
+// inet_network uses host order and inet_addr network order.
+in_addr_t inet_network(const char* cp) {
+ in_addr_t network_order = inet_addr(cp);
+ return ntohl(network_order);
+}
+
+in_addr_t inet_addr(const char* cp) {
+ in_addr addr;
+ return inet_aton(cp, &addr) ? addr.s_addr : INADDR_NONE;
+}
+
+int inet_aton(const char* cp, in_addr* addr) {
+ ErrnoRestorer errno_restorer;
+
+ unsigned long parts[4];
+ size_t i;
+ for (i = 0; i < 4; ++i) {
+ char* end;
+ errno = 0;
+ parts[i] = strtoul(cp, &end, 0);
+ if (errno != 0 || end == cp || (*end != '.' && *end != '\0')) return 0;
+ if (*end == '\0') break;
+ cp = end + 1;
+ }
+
+ uint32_t result = 0;
+ if (i == 0) {
+ // a (a 32-bit).
+ if (parts[0] > 0xffffffff) return 0;
+ result = parts[0];
+ } else if (i == 1) {
+ // a.b (b 24-bit).
+ if (parts[0] > 0xff || parts[1] > 0xffffff) return 0;
+ result = (parts[0] << 24) | parts[1];
+ } else if (i == 2) {
+ // a.b.c (c 16-bit).
+ if (parts[0] > 0xff || parts[1] > 0xff || parts[2] > 0xffff) return 0;
+ result = (parts[0] << 24) | (parts[1] << 16) | parts[2];
+ } else if (i == 3) {
+ // a.b.c.d (d 8-bit).
+ if (parts[0] > 0xff || parts[1] > 0xff || parts[2] > 0xff || parts[3] > 0xff) return 0;
+ result = (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3];
+ } else {
+ return 0;
+ }
+
+ if (addr != nullptr) addr->s_addr = htonl(result);
+ return 1;
+}
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index 3bbb210..4995414 100644
--- a/libc/bionic/libc_init_common.cpp
+++ b/libc/bionic/libc_init_common.cpp
@@ -42,19 +42,20 @@
#include <unistd.h>
#include "private/bionic_auxv.h"
+#include "private/bionic_globals.h"
#include "private/bionic_ssp.h"
#include "private/bionic_tls.h"
#include "private/KernelArgumentBlock.h"
#include "private/libc_logging.h"
+#include "private/WriteProtected.h"
#include "pthread_internal.h"
extern "C" abort_msg_t** __abort_message_ptr;
-extern "C" void __bionic_setjmp_cookie_init(void);
extern "C" int __system_properties_init(void);
extern "C" int __set_tls(void* ptr);
extern "C" int __set_tid_address(int* tid_address);
-__LIBC_HIDDEN__ void __libc_init_vdso();
+__LIBC_HIDDEN__ WriteProtected<libc_globals> __libc_globals;
// Not public, but well-known in the BSDs.
const char* __progname;
@@ -105,6 +106,17 @@
__init_alternate_signal_stack(&main_thread);
}
+void __libc_init_globals(KernelArgumentBlock& args) {
+ // Initialize libc globals that are needed in both the linker and in libc.
+ // In dynamic binaries, this is run at least twice for different copies of the
+ // globals, once for the linker's copy and once for the one in libc.so.
+ __libc_globals.initialize();
+ __libc_globals.mutate([&args](libc_globals* globals) {
+ __libc_init_vdso(globals, args);
+ __libc_init_setjmp_cookie(globals, args);
+ });
+}
+
void __libc_init_common(KernelArgumentBlock& args) {
// Initialize various globals.
environ = args.envp;
@@ -121,9 +133,6 @@
__pthread_internal_add(main_thread);
__system_properties_init(); // Requires 'environ'.
-
- __bionic_setjmp_cookie_init();
- __libc_init_vdso();
}
__noreturn static void __early_abort(int line) {
diff --git a/libc/bionic/libc_init_common.h b/libc/bionic/libc_init_common.h
index 673ad5b..5066652 100644
--- a/libc/bionic/libc_init_common.h
+++ b/libc/bionic/libc_init_common.h
@@ -51,6 +51,9 @@
#if defined(__cplusplus)
class KernelArgumentBlock;
+
+__LIBC_HIDDEN__ void __libc_init_globals(KernelArgumentBlock& args);
+
__LIBC_HIDDEN__ void __libc_init_common(KernelArgumentBlock& args);
__LIBC_HIDDEN__ void __libc_init_AT_SECURE(KernelArgumentBlock& args);
diff --git a/libc/bionic/libc_init_dynamic.cpp b/libc/bionic/libc_init_dynamic.cpp
index 78125f9..3bb6e89 100644
--- a/libc/bionic/libc_init_dynamic.cpp
+++ b/libc/bionic/libc_init_dynamic.cpp
@@ -74,6 +74,7 @@
// __libc_init_common() will change the TLS area so the old one won't be accessible anyway.
*args_slot = NULL;
+ __libc_init_globals(*args);
__libc_init_common(*args);
// Hooks for various libraries to let them know that we're starting up.
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index 2b1a8cb..2fe86d0 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -84,7 +84,12 @@
int (*slingshot)(int, char**, char**),
structors_array_t const * const structors) {
KernelArgumentBlock args(raw_args);
+
__libc_init_main_thread(args);
+
+ // Initializing the globals requires TLS to be available for errno.
+ __libc_init_globals(args);
+
__libc_init_AT_SECURE(args);
__libc_init_common(args);
diff --git a/libc/bionic/setjmp_cookie.cpp b/libc/bionic/setjmp_cookie.cpp
index cf79e83..ce57fd1 100644
--- a/libc/bionic/setjmp_cookie.cpp
+++ b/libc/bionic/setjmp_cookie.cpp
@@ -34,30 +34,30 @@
#include <sys/auxv.h>
#include <sys/cdefs.h>
+#include "private/bionic_globals.h"
#include "private/libc_logging.h"
+#include "private/KernelArgumentBlock.h"
-extern "C" __LIBC_HIDDEN__ int getentropy(void*, size_t);
-static long __bionic_setjmp_cookie;
-
-extern "C" void __bionic_setjmp_cookie_init() {
- char* random_data = reinterpret_cast<char*>(getauxval(AT_RANDOM));
+void __libc_init_setjmp_cookie(libc_globals* globals,
+ KernelArgumentBlock& args) {
+ char* random_data = reinterpret_cast<char*>(args.getauxval(AT_RANDOM));
long value = *reinterpret_cast<long*>(random_data + 8);
// Mask off the last bit to store the signal flag.
- __bionic_setjmp_cookie = value & ~1;
+ globals->setjmp_cookie = value & ~1;
}
-extern "C" long __bionic_setjmp_cookie_get(long sigflag) {
+extern "C" __LIBC_HIDDEN__ long __bionic_setjmp_cookie_get(long sigflag) {
if (sigflag & ~1) {
__libc_fatal("unexpected sigflag value: %ld", sigflag);
}
- return __bionic_setjmp_cookie | sigflag;
+ return __libc_globals->setjmp_cookie | sigflag;
}
// Aborts if cookie doesn't match, returns the signal flag otherwise.
-extern "C" long __bionic_setjmp_cookie_check(long cookie) {
- if (__bionic_setjmp_cookie != (cookie & ~1)) {
+extern "C" __LIBC_HIDDEN__ long __bionic_setjmp_cookie_check(long cookie) {
+ if (__libc_globals->setjmp_cookie != (cookie & ~1)) {
__libc_fatal("setjmp cookie mismatch");
}
diff --git a/libc/bionic/vdso.cpp b/libc/bionic/vdso.cpp
index 9eae3d5..029c148 100644
--- a/libc/bionic/vdso.cpp
+++ b/libc/bionic/vdso.cpp
@@ -14,66 +14,47 @@
* limitations under the License.
*/
-#include <link.h>
-#include <string.h>
-#include <sys/auxv.h>
-#include <unistd.h>
+#include "private/bionic_globals.h"
+#include "private/bionic_vdso.h"
#if defined(__aarch64__) || defined(__x86_64__) || defined (__i386__)
-#if defined(__aarch64__)
-#define VDSO_CLOCK_GETTIME_SYMBOL "__kernel_clock_gettime"
-#define VDSO_GETTIMEOFDAY_SYMBOL "__kernel_gettimeofday"
-#elif defined(__x86_64__) || defined(__i386__)
-#define VDSO_CLOCK_GETTIME_SYMBOL "__vdso_clock_gettime"
-#define VDSO_GETTIMEOFDAY_SYMBOL "__vdso_gettimeofday"
-#endif
-
-#include <errno.h>
#include <limits.h>
-#include <sys/mman.h>
+#include <link.h>
+#include <string.h>
+#include <sys/cdefs.h>
+#include <sys/time.h>
#include <time.h>
-
-#include "private/bionic_prctl.h"
-#include "private/libc_logging.h"
-
-extern "C" int __clock_gettime(int, timespec*);
-extern "C" int __gettimeofday(timeval*, struct timezone*);
-
-struct vdso_entry {
- const char* name;
- void* fn;
-};
-
-enum {
- VDSO_CLOCK_GETTIME = 0,
- VDSO_GETTIMEOFDAY,
- VDSO_END
-};
-
-static union {
- vdso_entry entries[VDSO_END];
- char padding[PAGE_SIZE];
-} vdso __attribute__((aligned(PAGE_SIZE))) = {{
- [VDSO_CLOCK_GETTIME] = { VDSO_CLOCK_GETTIME_SYMBOL, reinterpret_cast<void*>(__clock_gettime) },
- [VDSO_GETTIMEOFDAY] = { VDSO_GETTIMEOFDAY_SYMBOL, reinterpret_cast<void*>(__gettimeofday) },
-}};
+#include <unistd.h>
+#include "private/KernelArgumentBlock.h"
int clock_gettime(int clock_id, timespec* tp) {
- int (*vdso_clock_gettime)(int, timespec*) =
- reinterpret_cast<int (*)(int, timespec*)>(vdso.entries[VDSO_CLOCK_GETTIME].fn);
- return vdso_clock_gettime(clock_id, tp);
+ auto vdso_clock_gettime = reinterpret_cast<decltype(&clock_gettime)>(
+ __libc_globals->vdso[VDSO_CLOCK_GETTIME].fn);
+ if (__predict_true(vdso_clock_gettime)) {
+ return vdso_clock_gettime(clock_id, tp);
+ }
+ return __clock_gettime(clock_id, tp);
}
int gettimeofday(timeval* tv, struct timezone* tz) {
- int (*vdso_gettimeofday)(timeval*, struct timezone*) =
- reinterpret_cast<int (*)(timeval*, struct timezone*)>(vdso.entries[VDSO_GETTIMEOFDAY].fn);
- return vdso_gettimeofday(tv, tz);
+ auto vdso_gettimeofday = reinterpret_cast<decltype(&gettimeofday)>(
+ __libc_globals->vdso[VDSO_GETTIMEOFDAY].fn);
+ if (__predict_true(vdso_gettimeofday)) {
+ return vdso_gettimeofday(tv, tz);
+ }
+ return __gettimeofday(tv, tz);
}
-static void __libc_init_vdso_entries() {
+void __libc_init_vdso(libc_globals* globals, KernelArgumentBlock& args) {
+ auto&& vdso = globals->vdso;
+ vdso[VDSO_CLOCK_GETTIME] = { VDSO_CLOCK_GETTIME_SYMBOL,
+ reinterpret_cast<void*>(__clock_gettime) };
+ vdso[VDSO_GETTIMEOFDAY] = { VDSO_GETTIMEOFDAY_SYMBOL,
+ reinterpret_cast<void*>(__gettimeofday) };
+
// Do we have a vdso?
- uintptr_t vdso_ehdr_addr = getauxval(AT_SYSINFO_EHDR);
+ uintptr_t vdso_ehdr_addr = args.getauxval(AT_SYSINFO_EHDR);
ElfW(Ehdr)* vdso_ehdr = reinterpret_cast<ElfW(Ehdr)*>(vdso_ehdr_addr);
if (vdso_ehdr == nullptr) {
return;
@@ -123,27 +104,16 @@
// Are there any symbols we want?
for (size_t i = 0; i < symbol_count; ++i) {
for (size_t j = 0; j < VDSO_END; ++j) {
- if (strcmp(vdso.entries[j].name, strtab + symtab[i].st_name) == 0) {
- vdso.entries[j].fn = reinterpret_cast<void*>(vdso_addr + symtab[i].st_value);
+ if (strcmp(vdso[j].name, strtab + symtab[i].st_name) == 0) {
+ vdso[j].fn = reinterpret_cast<void*>(vdso_addr + symtab[i].st_value);
}
}
}
}
-void __libc_init_vdso() {
- __libc_init_vdso_entries();
-
- // We can't use PR_SET_VMA because this isn't an anonymous region.
- // Long-term we should be able to replace all of this with ifuncs.
- static_assert(PAGE_SIZE == sizeof(vdso), "sizeof(vdso) too large");
- if (mprotect(vdso.entries, sizeof(vdso), PROT_READ) == -1) {
- __libc_fatal("failed to mprotect PROT_READ vdso function pointer table: %s", strerror(errno));
- }
-}
-
#else
-void __libc_init_vdso() {
+void __libc_init_vdso(libc_globals*, KernelArgumentBlock&) {
}
#endif
diff --git a/libc/include/stdio.h b/libc/include/stdio.h
index c4bfb4f..1df4b54 100644
--- a/libc/include/stdio.h
+++ b/libc/include/stdio.h
@@ -38,14 +38,6 @@
#ifndef _STDIO_H_
#define _STDIO_H_
-/*
- * This file must contain a reference to __gnuc_va_list so that GCC's
- * fixincludes knows that that's what's being used for va_list, and so
- * to leave our <stdio.h> alone. (fixincludes gets in the way of pointing
- * one toolchain at various different sets of platform headers.)
- * If you alter this comment, be sure to keep "__gnuc_va_list" in it!
- */
-
#include <sys/cdefs.h>
#include <sys/types.h>
@@ -59,94 +51,9 @@
typedef off_t fpos_t; /* stdio file position type */
-/*
- * NB: to fit things in six character monocase externals, the stdio
- * code uses the prefix `__s' for stdio objects, typically followed
- * by a three-character attempt at a mnemonic.
- */
+struct __sFILE;
+typedef struct __sFILE FILE;
-/* stdio buffers */
-#if defined(__LP64__)
-struct __sbuf {
- unsigned char* _base;
- size_t _size;
-};
-#else
-struct __sbuf {
- unsigned char *_base;
- int _size;
-};
-#endif
-
-/*
- * stdio state variables.
- *
- * The following always hold:
- *
- * if (_flags&(__SLBF|__SWR)) == (__SLBF|__SWR),
- * _lbfsize is -_bf._size, else _lbfsize is 0
- * if _flags&__SRD, _w is 0
- * if _flags&__SWR, _r is 0
- *
- * This ensures that the getc and putc macros (or inline functions) never
- * try to write or read from a file that is in `read' or `write' mode.
- * (Moreover, they can, and do, automatically switch from read mode to
- * write mode, and back, on "r+" and "w+" files.)
- *
- * _lbfsize is used only to make the inline line-buffered output stream
- * code as compact as possible.
- *
- * _ub, _up, and _ur are used when ungetc() pushes back more characters
- * than fit in the current _bf, or when ungetc() pushes back a character
- * that does not match the previous one in _bf. When this happens,
- * _ub._base becomes non-nil (i.e., a stream has ungetc() data iff
- * _ub._base!=NULL) and _up and _ur save the current values of _p and _r.
- *
- * NOTE: if you change this structure, you also need to update the
- * std() initializer in findfp.c.
- */
-typedef struct __sFILE {
- unsigned char *_p; /* current position in (some) buffer */
- int _r; /* read space left for getc() */
- int _w; /* write space left for putc() */
-#if defined(__LP64__)
- int _flags; /* flags, below; this FILE is free if 0 */
- int _file; /* fileno, if Unix descriptor, else -1 */
-#else
- short _flags; /* flags, below; this FILE is free if 0 */
- short _file; /* fileno, if Unix descriptor, else -1 */
-#endif
- struct __sbuf _bf; /* the buffer (at least 1 byte, if !NULL) */
- int _lbfsize; /* 0 or -_bf._size, for inline putc */
-
- /* operations */
- void *_cookie; /* cookie passed to io functions */
- int (*_close)(void *);
- int (*_read)(void *, char *, int);
- fpos_t (*_seek)(void *, fpos_t, int);
- int (*_write)(void *, const char *, int);
-
- /* extension data, to avoid further ABI breakage */
- struct __sbuf _ext;
- /* data for long sequences of ungetc() */
- unsigned char *_up; /* saved _p when _p is doing ungetc data */
- int _ur; /* saved _r when _r is counting ungetc data */
-
- /* tricks to meet minimum requirements even when malloc() fails */
- unsigned char _ubuf[3]; /* guarantee an ungetc() buffer */
- unsigned char _nbuf[1]; /* guarantee a getc() buffer */
-
- /* separate buffer for fgetln() when line crosses buffer boundary */
- struct __sbuf _lb; /* buffer for fgetln() */
-
- /* Unix stdio files get aligned to block boundaries on fseek() */
- int _blksize; /* stat.st_blksize (may be != _bf._size) */
- fpos_t _offset; /* current lseek offset */
-} FILE;
-
-/* Legacy BSD implementation of stdin/stdout/stderr. */
-extern FILE __sF[];
-/* More obvious implementation. */
extern FILE* stdin;
extern FILE* stdout;
extern FILE* stderr;
diff --git a/libc/include/utmp.h b/libc/include/utmp.h
index ebf2372..7eeea41 100644
--- a/libc/include/utmp.h
+++ b/libc/include/utmp.h
@@ -46,7 +46,16 @@
#define UT_HOSTSIZE 16
#endif
-#define USER_PROCESS 7
+#define EMPTY 0
+#define RUN_LVL 1
+#define BOOT_TIME 2
+#define NEW_TIME 3
+#define OLD_TIME 4
+#define INIT_PROCESS 5
+#define LOGIN_PROCESS 6
+#define USER_PROCESS 7
+#define DEAD_PROCESS 8
+#define ACCOUNTING 9
struct lastlog
{
diff --git a/libc/private/WriteProtected.h b/libc/private/WriteProtected.h
new file mode 100644
index 0000000..1133e2a
--- /dev/null
+++ b/libc/private/WriteProtected.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _PRIVATE_WRITEPROTECTED_H
+#define _PRIVATE_WRITEPROTECTED_H
+
+#include <errno.h>
+#include <string.h>
+#include <sys/cdefs.h>
+#include <sys/mman.h>
+#include <sys/user.h>
+
+#include "private/bionic_macros.h"
+#include "private/bionic_prctl.h"
+#include "private/libc_logging.h"
+
+template <typename T>
+union WriteProtectedContents {
+ T value;
+ char padding[PAGE_SIZE];
+
+ WriteProtectedContents() = default;
+ DISALLOW_COPY_AND_ASSIGN(WriteProtectedContents);
+} __attribute__((aligned(PAGE_SIZE)));
+
+// Write protected wrapper class that aligns its contents to a page boundary,
+// and sets the memory protection to be non-writable, except when being modified
+// explicitly.
+template <typename T>
+class WriteProtected {
+ static_assert(sizeof(T) < PAGE_SIZE,
+ "WriteProtected only supports contents up to PAGE_SIZE");
+ static_assert(__is_pod(T), "WriteProtected only supports POD contents");
+
+ WriteProtectedContents<T> contents;
+
+ public:
+ WriteProtected() = default;
+ DISALLOW_COPY_AND_ASSIGN(WriteProtected);
+
+ void initialize() {
+ // Not strictly necessary, but this will hopefully segfault if we initialize
+ // multiple times by accident.
+ memset(&contents, 0, sizeof(contents));
+
+ if (mprotect(&contents, PAGE_SIZE, PROT_READ)) {
+ __libc_fatal("failed to make WriteProtected nonwritable in initialize");
+ }
+ }
+
+ const T* operator->() {
+ return &contents.value;
+ }
+
+ const T& operator*() {
+ return contents.value;
+ }
+
+ template <typename Mutator>
+ void mutate(Mutator mutator) {
+ if (mprotect(&contents, PAGE_SIZE, PROT_READ | PROT_WRITE) != 0) {
+ __libc_fatal("failed to make WriteProtected writable in mutate: %s",
+ strerror(errno));
+ }
+ mutator(&contents.value);
+ if (mprotect(&contents, PAGE_SIZE, PROT_READ) != 0) {
+ __libc_fatal("failed to make WriteProtected nonwritable in mutate: %s",
+ strerror(errno));
+ }
+ }
+};
+
+#endif
diff --git a/libc/private/bionic_globals.h b/libc/private/bionic_globals.h
new file mode 100644
index 0000000..644b5a4
--- /dev/null
+++ b/libc/private/bionic_globals.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ */
+#ifndef _PRIVATE_BIONIC_GLOBALS_H
+#define _PRIVATE_BIONIC_GLOBALS_H
+
+#include <sys/cdefs.h>
+#include "private/bionic_vdso.h"
+#include "private/WriteProtected.h"
+
+struct libc_globals {
+ vdso_entry vdso[VDSO_END];
+ long setjmp_cookie;
+};
+
+__LIBC_HIDDEN__ extern WriteProtected<libc_globals> __libc_globals;
+
+class KernelArgumentBlock;
+__LIBC_HIDDEN__ void __libc_init_vdso(libc_globals* globals,
+ KernelArgumentBlock& args);
+__LIBC_HIDDEN__ void __libc_init_setjmp_cookie(libc_globals* globals,
+ KernelArgumentBlock& args);
+
+#endif
diff --git a/libc/private/bionic_vdso.h b/libc/private/bionic_vdso.h
new file mode 100644
index 0000000..5400de5
--- /dev/null
+++ b/libc/private/bionic_vdso.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ */
+#ifndef _PRIVATE_BIONIC_VDSO_H
+#define _PRIVATE_BIONIC_VDSO_H
+
+#include <time.h>
+
+#if defined(__aarch64__)
+#define VDSO_CLOCK_GETTIME_SYMBOL "__kernel_clock_gettime"
+#define VDSO_GETTIMEOFDAY_SYMBOL "__kernel_gettimeofday"
+#elif defined(__x86_64__) || defined(__i386__)
+#define VDSO_CLOCK_GETTIME_SYMBOL "__vdso_clock_gettime"
+#define VDSO_GETTIMEOFDAY_SYMBOL "__vdso_gettimeofday"
+#endif
+
+extern "C" int __clock_gettime(int, timespec*);
+extern "C" int __gettimeofday(timeval*, struct timezone*);
+
+struct vdso_entry {
+ const char* name;
+ void* fn;
+};
+
+enum {
+ VDSO_CLOCK_GETTIME = 0,
+ VDSO_GETTIMEOFDAY,
+ VDSO_END
+};
+
+#endif // _PRIVATE_BIONIC_VDSO_H
diff --git a/libc/stdio/fileext.h b/libc/stdio/fileext.h
deleted file mode 100644
index 6cacc0f..0000000
--- a/libc/stdio/fileext.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/* $OpenBSD: fileext.h,v 1.2 2005/06/17 20:40:32 espie Exp $ */
-/* $NetBSD: fileext.h,v 1.5 2003/07/18 21:46:41 nathanw Exp $ */
-
-/*-
- * Copyright (c)2001 Citrus Project,
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
- *
- * $Citrus$
- */
-
-#ifndef _FILEEXT_H_
-#define _FILEEXT_H_
-
-#include <pthread.h>
-#include <stdbool.h>
-
-__BEGIN_DECLS
-
-/*
- * file extension
- */
-struct __sfileext {
- struct __sbuf _ub; /* ungetc buffer */
- struct wchar_io_data _wcio; /* wide char io status */
- pthread_mutex_t _lock; /* file lock */
- bool _stdio_handles_locking; /* __fsetlocking support */
-};
-
-#if defined(__cplusplus)
-#define _EXT(fp) reinterpret_cast<__sfileext*>((fp)->_ext._base)
-#else
-#define _EXT(fp) ((struct __sfileext *)((fp)->_ext._base))
-#endif
-
-#define _UB(fp) _EXT(fp)->_ub
-#define _FLOCK(fp) _EXT(fp)->_lock
-
-#define _FILEEXT_INIT(fp) \
-do { \
- _UB(fp)._base = NULL; \
- _UB(fp)._size = 0; \
- WCIO_INIT(fp); \
- pthread_mutexattr_t attr; \
- pthread_mutexattr_init(&attr); \
- pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \
- pthread_mutex_init(&_FLOCK(fp), &attr); \
- pthread_mutexattr_destroy(&attr); \
- _EXT(fp)->_stdio_handles_locking = true; \
-} while (0)
-
-#define _FILEEXT_SETUP(f, fext) \
-do { \
- (f)->_ext._base = (unsigned char *)(fext); \
- _FILEEXT_INIT(f); \
-} while (0)
-
-__END_DECLS
-
-#endif /* _FILEEXT_H_ */
diff --git a/libc/stdio/findfp.c b/libc/stdio/findfp.c
index 5e51198..2696cfd 100644
--- a/libc/stdio/findfp.c
+++ b/libc/stdio/findfp.c
@@ -60,15 +60,20 @@
_THREAD_PRIVATE_MUTEX(__sfp_mutex);
static struct __sfileext __sFext[3];
+
+// __sF is exported for backwards compatibility. Until M, we didn't have symbols
+// for stdin/stdout/stderr; they were macros accessing __sF.
FILE __sF[3] = {
std(__SRD, STDIN_FILENO), /* stdin */
std(__SWR, STDOUT_FILENO), /* stdout */
std(__SWR|__SNBF, STDERR_FILENO) /* stderr */
};
+
+struct glue __sglue = { &uglue, 3, __sF };
+
FILE* stdin = &__sF[0];
FILE* stdout = &__sF[1];
FILE* stderr = &__sF[2];
-struct glue __sglue = { &uglue, 3, __sF };
static struct glue *
moreglue(int n)
diff --git a/libc/stdio/local.h b/libc/stdio/local.h
index ce04141..749de7b 100644
--- a/libc/stdio/local.h
+++ b/libc/stdio/local.h
@@ -32,17 +32,114 @@
* SUCH DAMAGE.
*/
+#ifndef __BIONIC_STDIO_LOCAL_H__
+#define __BIONIC_STDIO_LOCAL_H__
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <wchar.h>
+#include "wcio.h"
+
/*
* Information local to this implementation of stdio,
* in particular, macros and private variables.
*/
-#include <wchar.h>
-#include "wcio.h"
-#include "fileext.h"
-
__BEGIN_DECLS
+struct __sbuf {
+ unsigned char* _base;
+#if defined(__LP64__)
+ size_t _size;
+#else
+ int _size;
+#endif
+};
+
+struct __sFILE {
+ unsigned char *_p; /* current position in (some) buffer */
+ int _r; /* read space left for getc() */
+ int _w; /* write space left for putc() */
+#if defined(__LP64__)
+ int _flags; /* flags, below; this FILE is free if 0 */
+ int _file; /* fileno, if Unix descriptor, else -1 */
+#else
+ short _flags; /* flags, below; this FILE is free if 0 */
+ short _file; /* fileno, if Unix descriptor, else -1 */
+#endif
+ struct __sbuf _bf; /* the buffer (at least 1 byte, if !NULL) */
+ int _lbfsize; /* 0 or -_bf._size, for inline putc */
+
+ /* operations */
+ void *_cookie; /* cookie passed to io functions */
+ int (*_close)(void *);
+ int (*_read)(void *, char *, int);
+ fpos_t (*_seek)(void *, fpos_t, int);
+ int (*_write)(void *, const char *, int);
+
+ /* extension data, to avoid further ABI breakage */
+ struct __sbuf _ext;
+ /* data for long sequences of ungetc() */
+ unsigned char *_up; /* saved _p when _p is doing ungetc data */
+ int _ur; /* saved _r when _r is counting ungetc data */
+
+ /* tricks to meet minimum requirements even when malloc() fails */
+ unsigned char _ubuf[3]; /* guarantee an ungetc() buffer */
+ unsigned char _nbuf[1]; /* guarantee a getc() buffer */
+
+ /* separate buffer for fgetln() when line crosses buffer boundary */
+ struct __sbuf _lb; /* buffer for fgetln() */
+
+ /* Unix stdio files get aligned to block boundaries on fseek() */
+ int _blksize; /* stat.st_blksize (may be != _bf._size) */
+ fpos_t _offset; /* current lseek offset */
+};
+
+/*
+ * file extension
+ */
+struct __sfileext {
+ /* ungetc buffer */
+ struct __sbuf _ub;
+
+ /* wide char io status */
+ struct wchar_io_data _wcio;
+
+ /* file lock */
+ pthread_mutex_t _lock;
+
+ /* __fsetlocking support */
+ bool _stdio_handles_locking;
+};
+
+#if defined(__cplusplus)
+#define _EXT(fp) reinterpret_cast<__sfileext*>((fp)->_ext._base)
+#else
+#define _EXT(fp) ((struct __sfileext *)((fp)->_ext._base))
+#endif
+
+#define _UB(fp) _EXT(fp)->_ub
+#define _FLOCK(fp) _EXT(fp)->_lock
+
+#define _FILEEXT_INIT(fp) \
+do { \
+ _UB(fp)._base = NULL; \
+ _UB(fp)._size = 0; \
+ WCIO_INIT(fp); \
+ pthread_mutexattr_t attr; \
+ pthread_mutexattr_init(&attr); \
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \
+ pthread_mutex_init(&_FLOCK(fp), &attr); \
+ pthread_mutexattr_destroy(&attr); \
+ _EXT(fp)->_stdio_handles_locking = true; \
+} while (0)
+
+#define _FILEEXT_SETUP(f, fext) \
+do { \
+ (f)->_ext._base = (unsigned char *)(fext); \
+ _FILEEXT_INIT(f); \
+} while (0)
+
/*
* Android <= KitKat had getc/putc macros in <stdio.h> that referred
* to __srget/__swbuf, so those symbols need to be public for LP32
@@ -143,3 +240,5 @@
#pragma GCC visibility pop
__END_DECLS
+
+#endif
diff --git a/libc/upstream-openbsd/lib/libc/stdio/refill.c b/libc/stdio/refill.c
similarity index 98%
rename from libc/upstream-openbsd/lib/libc/stdio/refill.c
rename to libc/stdio/refill.c
index 165c72a..e87c7b9 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/refill.c
+++ b/libc/stdio/refill.c
@@ -58,9 +58,11 @@
fp->_r = 0; /* largely a convenience for callers */
+#if !defined(__ANDROID__)
/* SysV does not make this test; take it out for compatibility */
if (fp->_flags & __SEOF)
return (EOF);
+#endif
/* if not already reading, have to be reading and writing */
if ((fp->_flags & __SRD) == 0) {
diff --git a/libc/tzcode/asctime.c b/libc/tzcode/asctime.c
index fea24e4..337a313 100644
--- a/libc/tzcode/asctime.c
+++ b/libc/tzcode/asctime.c
@@ -55,7 +55,7 @@
** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n
** (two three-character abbreviations, five strings denoting integers,
** seven explicit spaces, two explicit colons, a newline,
-** and a trailing ASCII nul).
+** and a trailing NUL byte).
** The values above are for systems where an int is 32 bits and are provided
** as an example; the define below calculates the maximum for the system at
** hand.
@@ -99,11 +99,11 @@
** Assume that strftime is unaffected by other out-of-range members
** (e.g., timeptr->tm_mday) when processing "%Y".
*/
- (void) strftime(year, sizeof year, "%Y", timeptr);
+ strftime(year, sizeof year, "%Y", timeptr);
/*
** We avoid using snprintf since it's not available on all systems.
*/
- (void) snprintf(result, sizeof(result), /* Android change: use snprintf. */
+ snprintf(result, sizeof(result), /* Android change: use snprintf. */
((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B),
wn, mn,
timeptr->tm_mday, timeptr->tm_hour,
@@ -112,11 +112,7 @@
if (strlen(result) < STD_ASCTIME_BUF_SIZE || buf == buf_asctime)
return strcpy(buf, result);
else {
-#ifdef EOVERFLOW
errno = EOVERFLOW;
-#else /* !defined EOVERFLOW */
- errno = EINVAL;
-#endif /* !defined EOVERFLOW */
return NULL;
}
}
diff --git a/libc/tzcode/difftime.c b/libc/tzcode/difftime.c
index 449cdf0..ba2fd03 100644
--- a/libc/tzcode/difftime.c
+++ b/libc/tzcode/difftime.c
@@ -7,42 +7,52 @@
#include "private.h" /* for time_t and TYPE_SIGNED */
+/* Return -X as a double. Using this avoids casting to 'double'. */
+static double
+dminus(double x)
+{
+ return -x;
+}
+
double ATTRIBUTE_CONST
-difftime(const time_t time1, const time_t time0)
+difftime(time_t time1, time_t time0)
{
/*
- ** If (sizeof (double) > sizeof (time_t)) simply convert and subtract
+ ** If double is large enough, simply convert and subtract
** (assuming that the larger type has more precision).
*/
- if (sizeof (double) > sizeof (time_t))
- return (double) time1 - (double) time0;
- if (!TYPE_SIGNED(time_t)) {
- /*
- ** The difference of two unsigned values can't overflow
- ** if the minuend is greater than or equal to the subtrahend.
- */
- if (time1 >= time0)
- return time1 - time0;
- else return -(double) (time0 - time1);
+ if (sizeof (time_t) < sizeof (double)) {
+ double t1 = time1, t0 = time0;
+ return t1 - t0;
}
+
+ /*
+ ** The difference of two unsigned values can't overflow
+ ** if the minuend is greater than or equal to the subtrahend.
+ */
+ if (!TYPE_SIGNED(time_t))
+ return time0 <= time1 ? time1 - time0 : dminus(time0 - time1);
+
+ /* Use uintmax_t if wide enough. */
+ if (sizeof (time_t) <= sizeof (uintmax_t)) {
+ uintmax_t t1 = time1, t0 = time0;
+ return time0 <= time1 ? t1 - t0 : dminus(t0 - t1);
+ }
+
/*
** Handle cases where both time1 and time0 have the same sign
** (meaning that their difference cannot overflow).
*/
if ((time1 < 0) == (time0 < 0))
- return time1 - time0;
+ return time1 - time0;
+
/*
- ** time1 and time0 have opposite signs.
- ** Punt if uintmax_t is too narrow.
+ ** The values have opposite signs and uintmax_t is too narrow.
** This suffers from double rounding; attempt to lessen that
** by using long double temporaries.
*/
- if (sizeof (uintmax_t) < sizeof (time_t))
- return (long double) time1 - (long double) time0;
- /*
- ** Stay calm...decent optimizers will eliminate the complexity below.
- */
- if (time1 >= 0 /* && time0 < 0 */)
- return (uintmax_t) time1 + (uintmax_t) (-1 - time0) + 1;
- return -(double) ((uintmax_t) time0 + (uintmax_t) (-1 - time1) + 1);
+ {
+ long double t1 = time1, t0 = time0;
+ return t1 - t0;
+ }
}
diff --git a/libc/tzcode/localtime.c b/libc/tzcode/localtime.c
index 3a8a367..79c4a9a 100644
--- a/libc/tzcode/localtime.c
+++ b/libc/tzcode/localtime.c
@@ -10,10 +10,30 @@
/*LINTLIBRARY*/
+#define LOCALTIME_IMPLEMENTATION
#include "private.h"
+
#include "tzfile.h"
#include "fcntl.h"
+#if THREAD_SAFE
+# include <pthread.h>
+static pthread_mutex_t locallock = PTHREAD_MUTEX_INITIALIZER;
+static int lock(void) { return pthread_mutex_lock(&locallock); }
+static void unlock(void) { pthread_mutex_unlock(&locallock); }
+#else
+static int lock(void) { return 0; }
+static void unlock(void) { }
+#endif
+
+/* NETBSD_INSPIRED_EXTERN functions are exported to callers if
+ NETBSD_INSPIRED is defined, and are private otherwise. */
+#if NETBSD_INSPIRED
+# define NETBSD_INSPIRED_EXTERN
+#else
+# define NETBSD_INSPIRED_EXTERN static
+#endif
+
#ifndef TZ_ABBR_MAX_LEN
#define TZ_ABBR_MAX_LEN 16
#endif /* !defined TZ_ABBR_MAX_LEN */
@@ -38,19 +58,6 @@
#define OPEN_MODE O_RDONLY
#endif /* !defined O_BINARY */
-#if 0
-# define XLOG(xx) printf xx , fflush(stdout)
-#else
-# define XLOG(x) do{}while (0)
-#endif
-
-/* BEGIN android-added: thread-safety. */
-#include <pthread.h>
-static pthread_mutex_t _tzMutex = PTHREAD_MUTEX_INITIALIZER;
-static inline void _tzLock(void) { pthread_mutex_lock(&_tzMutex); }
-static inline void _tzUnlock(void) { pthread_mutex_unlock(&_tzMutex); }
-/* END android-added */
-
#ifndef WILDABBR
/*
** Someone might make incorrect use of a time zone abbreviation:
@@ -91,10 +98,10 @@
struct ttinfo { /* time type information */
int_fast32_t tt_gmtoff; /* UT offset in seconds */
- int tt_isdst; /* used to set tm_isdst */
+ bool tt_isdst; /* used to set tm_isdst */
int tt_abbrind; /* abbreviation list index */
- int tt_ttisstd; /* TRUE if transition is std time */
- int tt_ttisgmt; /* TRUE if transition is UT */
+ bool tt_ttisstd; /* transition is std time */
+ bool tt_ttisgmt; /* transition is UT */
};
struct lsinfo { /* leap second information */
@@ -102,6 +109,7 @@
int_fast64_t ls_corr; /* correction to apply */
};
+#define SMALLEST(a, b) (((a) < (b)) ? (a) : (b))
#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b))
#ifdef TZNAME_MAX
@@ -116,8 +124,8 @@
int timecnt;
int typecnt;
int charcnt;
- int goback;
- int goahead;
+ bool goback;
+ bool goahead;
time_t ats[TZ_MAX_TIMES];
unsigned char types[TZ_MAX_TIMES];
struct ttinfo ttis[TZ_MAX_TYPES];
@@ -127,74 +135,29 @@
int defaulttype; /* for early times or if no transitions */
};
+enum r_type {
+ JULIAN_DAY, /* Jn = Julian day */
+ DAY_OF_YEAR, /* n = day of year */
+ MONTH_NTH_DAY_OF_WEEK /* Mm.n.d = month, week, day of week */
+};
+
struct rule {
- int r_type; /* type of rule; see below */
+ enum r_type r_type; /* type of rule */
int r_day; /* day number of rule */
int r_week; /* week number of rule */
int r_mon; /* month number of rule */
int_fast32_t r_time; /* transition time of rule */
};
-#define JULIAN_DAY 0 /* Jn = Julian day */
-#define DAY_OF_YEAR 1 /* n = day of year */
-#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d = month, week, day of week */
-
-/*
-** Prototypes for static functions.
-*/
-
-/* NOTE: all internal functions assume that _tzLock() was already called */
-
-static int __bionic_open_tzdata(const char*, int*);
-static int_fast32_t detzcode(const char * codep);
-static int_fast64_t detzcode64(const char * codep);
-static int differ_by_repeat(time_t t1, time_t t0);
-static const char * getzname(const char * strp) ATTRIBUTE_PURE;
-static const char * getqzname(const char * strp, const int delim)
- ATTRIBUTE_PURE;
-static const char * getnum(const char * strp, int * nump, int min,
- int max);
-static const char * getsecs(const char * strp, int_fast32_t * secsp);
-static const char * getoffset(const char * strp, int_fast32_t * offsetp);
-static const char * getrule(const char * strp, struct rule * rulep);
-static void gmtload(struct state * sp);
-static struct tm * gmtsub(const time_t * timep, int_fast32_t offset,
- struct tm * tmp, struct state * sp); // android-changed: added sp.
-static struct tm * localsub(const time_t * timep, int_fast32_t offset,
- struct tm * tmp, struct state * sp); // android-changed: added sp.
-static int increment_overflow(int * number, int delta);
-static int leaps_thru_end_of(int y) ATTRIBUTE_PURE;
-static int increment_overflow32(int_fast32_t * number, int delta);
-static int increment_overflow_time(time_t *t, int_fast32_t delta);
-static int normalize_overflow32(int_fast32_t * tensptr,
- int * unitsptr, int base);
-static int normalize_overflow(int * tensptr, int * unitsptr,
- int base);
-static void settzname(void);
-static time_t time1(struct tm * tmp,
- struct tm * (*funcp)(const time_t *,
- int_fast32_t, struct tm *, struct state *), // android-changed: added state*.
- int_fast32_t, struct state * sp); // android-changed: added sp.
-static time_t time2(struct tm * tmp,
- struct tm * (*funcp)(const time_t *,
- int_fast32_t, struct tm*, struct state *), // android-changed: added state*.
- int_fast32_t offset, int * okayp, struct state * sp); // android-changed: added sp.
-static time_t time2sub(struct tm *tmp,
- struct tm * (*funcp) (const time_t *,
- int_fast32_t, struct tm*, struct state *), // android-changed: added state*.
- int_fast32_t offset, int * okayp, int do_norm_secs, struct state * sp); // android-change: added sp.
-static struct tm * timesub(const time_t * timep, int_fast32_t offset,
- const struct state * sp, struct tm * tmp);
-static int tmcomp(const struct tm * atmp,
- const struct tm * btmp);
-static int_fast32_t transtime(int year, const struct rule * rulep,
- int_fast32_t offset)
- ATTRIBUTE_PURE;
-static int typesequiv(const struct state * sp, int a, int b);
-static int tzload(const char * name, struct state * sp,
- int doextend);
-static int tzparse(const char * name, struct state * sp,
- int lastditch);
+static struct tm *gmtsub(struct state const *, time_t const *, int_fast32_t,
+ struct tm *);
+static bool increment_overflow(int *, int);
+static bool increment_overflow_time(time_t *, int_fast32_t);
+static bool normalize_overflow32(int_fast32_t *, int *, int);
+static struct tm *timesub(time_t const *, int_fast32_t, struct state const *,
+ struct tm *);
+static bool typesequiv(struct state const *, int, int);
+static bool tzparse(char const *, struct state *, bool);
#ifdef ALL_STATE
static struct state * lclptr;
@@ -214,7 +177,6 @@
static char lcl_TZname[TZ_STRLEN_MAX + 1];
static int lcl_is_set;
-static int gmt_is_set;
char * tzname[2] = {
(char *) wildabbr,
@@ -229,107 +191,150 @@
** Thanks to Paul Eggert for noting this.
*/
-static struct tm tmGlobal;
+static struct tm tm;
#ifdef USG_COMPAT
-long timezone = 0;
-int daylight = 0;
+long timezone;
+int daylight;
#endif /* defined USG_COMPAT */
#ifdef ALTZONE
-long altzone = 0;
+long altzone;
#endif /* defined ALTZONE */
+/* Initialize *S to a value based on GMTOFF, ISDST, and ABBRIND. */
+static void
+init_ttinfo(struct ttinfo *s, int_fast32_t gmtoff, bool isdst, int abbrind)
+{
+ s->tt_gmtoff = gmtoff;
+ s->tt_isdst = isdst;
+ s->tt_abbrind = abbrind;
+ s->tt_ttisstd = false;
+ s->tt_ttisgmt = false;
+}
+
static int_fast32_t
detzcode(const char *const codep)
{
- register int_fast32_t result;
- register int i;
+ register int_fast32_t result;
+ register int i;
+ int_fast32_t one = 1;
+ int_fast32_t halfmaxval = one << (32 - 2);
+ int_fast32_t maxval = halfmaxval - 1 + halfmaxval;
+ int_fast32_t minval = -1 - maxval;
- result = (codep[0] & 0x80) ? -1 : 0;
- for (i = 0; i < 4; ++i)
- result = (result << 8) | (codep[i] & 0xff);
- return result;
+ result = codep[0] & 0x7f;
+ for (i = 1; i < 4; ++i)
+ result = (result << 8) | (codep[i] & 0xff);
+
+ if (codep[0] & 0x80) {
+ /* Do two's-complement negation even on non-two's-complement machines.
+ If the result would be minval - 1, return minval. */
+ result -= !TWOS_COMPLEMENT(int_fast32_t) && result != 0;
+ result += minval;
+ }
+ return result;
}
static int_fast64_t
detzcode64(const char *const codep)
{
- register int_fast64_t result;
- register int i;
+ register uint_fast64_t result;
+ register int i;
+ int_fast64_t one = 1;
+ int_fast64_t halfmaxval = one << (64 - 2);
+ int_fast64_t maxval = halfmaxval - 1 + halfmaxval;
+ int_fast64_t minval = -TWOS_COMPLEMENT(int_fast64_t) - maxval;
- result = (codep[0] & 0x80) ? -1 : 0;
- for (i = 0; i < 8; ++i)
- result = (result << 8) | (codep[i] & 0xff);
- return result;
+ result = codep[0] & 0x7f;
+ for (i = 1; i < 8; ++i)
+ result = (result << 8) | (codep[i] & 0xff);
+
+ if (codep[0] & 0x80) {
+ /* Do two's-complement negation even on non-two's-complement machines.
+ If the result would be minval - 1, return minval. */
+ result -= !TWOS_COMPLEMENT(int_fast64_t) && result != 0;
+ result += minval;
+ }
+ return result;
+}
+
+static void
+update_tzname_etc(struct state const *sp, struct ttinfo const *ttisp)
+{
+ tzname[ttisp->tt_isdst] = (char *) &sp->chars[ttisp->tt_abbrind];
+#ifdef USG_COMPAT
+ if (!ttisp->tt_isdst)
+ timezone = - ttisp->tt_gmtoff;
+#endif
+#ifdef ALTZONE
+ if (ttisp->tt_isdst)
+ altzone = - ttisp->tt_gmtoff;
+#endif
}
static void
settzname(void)
{
- register struct state * const sp = lclptr;
- register int i;
+ register struct state * const sp = lclptr;
+ register int i;
- tzname[0] = tzname[1] = (char *) wildabbr;
+ tzname[0] = tzname[1] = (char *) wildabbr;
#ifdef USG_COMPAT
- daylight = 0;
- timezone = 0;
+ daylight = 0;
+ timezone = 0;
#endif /* defined USG_COMPAT */
#ifdef ALTZONE
- altzone = 0;
+ altzone = 0;
#endif /* defined ALTZONE */
- if (sp == NULL) {
- tzname[0] = tzname[1] = (char *) gmt;
- return;
- }
- /*
- ** And to get the latest zone names into tzname. . .
- */
- for (i = 0; i < sp->typecnt; ++i) {
- register const struct ttinfo * const ttisp = &sp->ttis[i];
-
- tzname[ttisp->tt_isdst] = &sp->chars[ttisp->tt_abbrind];
- }
- for (i = 0; i < sp->timecnt; ++i) {
- register const struct ttinfo * const ttisp =
- &sp->ttis[
- sp->types[i]];
-
- tzname[ttisp->tt_isdst] =
- &sp->chars[ttisp->tt_abbrind];
+ if (sp == NULL) {
+ tzname[0] = tzname[1] = (char *) gmt;
+ return;
+ }
+ /*
+ ** And to get the latest zone names into tzname. . .
+ */
+ for (i = 0; i < sp->typecnt; ++i) {
+ register const struct ttinfo * const ttisp = &sp->ttis[i];
+ update_tzname_etc(sp, ttisp);
+ }
+ for (i = 0; i < sp->timecnt; ++i) {
+ register const struct ttinfo * const ttisp =
+ &sp->ttis[
+ sp->types[i]];
+ update_tzname_etc(sp, ttisp);
#ifdef USG_COMPAT
- if (ttisp->tt_isdst)
- daylight = 1;
- if (!ttisp->tt_isdst)
- timezone = -(ttisp->tt_gmtoff);
+ if (ttisp->tt_isdst)
+ daylight = 1;
#endif /* defined USG_COMPAT */
-#ifdef ALTZONE
- if (ttisp->tt_isdst)
- altzone = -(ttisp->tt_gmtoff);
-#endif /* defined ALTZONE */
- }
- /*
- ** Finally, scrub the abbreviations.
- ** First, replace bogus characters.
- */
- for (i = 0; i < sp->charcnt; ++i)
- if (strchr(TZ_ABBR_CHAR_SET, sp->chars[i]) == NULL)
- sp->chars[i] = TZ_ABBR_ERR_CHAR;
- /*
- ** Second, truncate long abbreviations.
- */
- for (i = 0; i < sp->typecnt; ++i) {
- register const struct ttinfo * const ttisp = &sp->ttis[i];
- register char * cp = &sp->chars[ttisp->tt_abbrind];
-
- if (strlen(cp) > TZ_ABBR_MAX_LEN &&
- strcmp(cp, GRANDPARENTED) != 0)
- *(cp + TZ_ABBR_MAX_LEN) = '\0';
- }
+ }
}
-static int
-differ_by_repeat(const time_t t1 __unused, const time_t t0 __unused)
+static void
+scrub_abbrs(struct state *sp)
+{
+ int i;
+ /*
+ ** First, replace bogus characters.
+ */
+ for (i = 0; i < sp->charcnt; ++i)
+ if (strchr(TZ_ABBR_CHAR_SET, sp->chars[i]) == NULL)
+ sp->chars[i] = TZ_ABBR_ERR_CHAR;
+ /*
+ ** Second, truncate long abbreviations.
+ */
+ for (i = 0; i < sp->typecnt; ++i) {
+ register const struct ttinfo * const ttisp = &sp->ttis[i];
+ register char * cp = &sp->chars[ttisp->tt_abbrind];
+
+ if (strlen(cp) > TZ_ABBR_MAX_LEN &&
+ strcmp(cp, GRANDPARENTED) != 0)
+ *(cp + TZ_ABBR_MAX_LEN) = '\0';
+ }
+}
+
+static bool
+differ_by_repeat(const time_t t1, const time_t t0)
{
if (TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS)
return 0;
@@ -338,318 +343,387 @@
#endif
}
-static int
-tzload(register const char* name, register struct state* const sp,
- register const int doextend)
-{
- register const char * p;
- register int i;
- register int fid;
- register int stored;
- register int nread;
- typedef union {
- struct tzhead tzhead;
- char buf[2 * sizeof(struct tzhead) +
- 2 * sizeof *sp +
- 4 * TZ_MAX_TIMES];
- } u_t;
- union local_storage {
- /*
- ** Section 4.9.1 of the C standard says that
- ** "FILENAME_MAX expands to an integral constant expression
- ** that is the size needed for an array of char large enough
- ** to hold the longest file name string that the implementation
- ** guarantees can be opened."
- */
- //char fullname[FILENAME_MAX + 1];
+/* Input buffer for data read from a compiled tz file. */
+union input_buffer {
+ /* The first part of the buffer, interpreted as a header. */
+ struct tzhead tzhead;
- /* The main part of the storage for this function. */
- struct {
- u_t u;
- struct state st;
- } u;
- };
- //register char *fullname;
- register u_t *up;
- register union local_storage *lsp;
-#ifdef ALL_STATE
- lsp = malloc(sizeof *lsp);
- if (!lsp)
- return -1;
-#else /* !defined ALL_STATE */
- union local_storage ls;
- lsp = &ls;
-#endif /* !defined ALL_STATE */
- //fullname = lsp->fullname;
- up = &lsp->u.u;
-
- sp->goback = sp->goahead = FALSE;
-
- if (! name) {
- name = TZDEFAULT;
- if (! name)
- goto oops;
- }
-
- int toread;
- fid = __bionic_open_tzdata(name, &toread);
- if (fid < 0)
- goto oops;
-
- nread = read(fid, up->buf, sizeof up->buf);
- if (close(fid) < 0 || nread <= 0)
- goto oops;
- for (stored = 4; stored <= 8; stored *= 2) {
- int ttisstdcnt;
- int ttisgmtcnt;
- int timecnt;
-
- ttisstdcnt = (int) detzcode(up->tzhead.tzh_ttisstdcnt);
- ttisgmtcnt = (int) detzcode(up->tzhead.tzh_ttisgmtcnt);
- sp->leapcnt = (int) detzcode(up->tzhead.tzh_leapcnt);
- sp->timecnt = (int) detzcode(up->tzhead.tzh_timecnt);
- sp->typecnt = (int) detzcode(up->tzhead.tzh_typecnt);
- sp->charcnt = (int) detzcode(up->tzhead.tzh_charcnt);
- p = up->tzhead.tzh_charcnt + sizeof up->tzhead.tzh_charcnt;
- if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
- sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
- sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
- sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
- (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
- (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
- goto oops;
- if (nread - (p - up->buf) <
- sp->timecnt * stored + /* ats */
- sp->timecnt + /* types */
- sp->typecnt * 6 + /* ttinfos */
- sp->charcnt + /* chars */
- sp->leapcnt * (stored + 4) + /* lsinfos */
- ttisstdcnt + /* ttisstds */
- ttisgmtcnt) /* ttisgmts */
- goto oops;
- timecnt = 0;
- for (i = 0; i < sp->timecnt; ++i) {
- int_fast64_t at
- = stored == 4 ? detzcode(p) : detzcode64(p);
- sp->types[i] = ((TYPE_SIGNED(time_t)
- ? time_t_min <= at
- : 0 <= at)
- && at <= time_t_max);
- if (sp->types[i]) {
- if (i && !timecnt && at != time_t_min) {
- /*
- ** Keep the earlier record, but tweak
- ** it so that it starts with the
- ** minimum time_t value.
- */
- sp->types[i - 1] = 1;
- sp->ats[timecnt++] = time_t_min;
- }
- sp->ats[timecnt++] = at;
- }
- p += stored;
- }
- timecnt = 0;
- for (i = 0; i < sp->timecnt; ++i) {
- unsigned char typ = *p++;
- if (sp->typecnt <= typ)
- goto oops;
- if (sp->types[i])
- sp->types[timecnt++] = typ;
- }
- sp->timecnt = timecnt;
- for (i = 0; i < sp->typecnt; ++i) {
- register struct ttinfo * ttisp;
-
- ttisp = &sp->ttis[i];
- ttisp->tt_gmtoff = detzcode(p);
- p += 4;
- ttisp->tt_isdst = (unsigned char) *p++;
- if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
- goto oops;
- ttisp->tt_abbrind = (unsigned char) *p++;
- if (ttisp->tt_abbrind < 0 ||
- ttisp->tt_abbrind > sp->charcnt)
- goto oops;
- }
- for (i = 0; i < sp->charcnt; ++i)
- sp->chars[i] = *p++;
- sp->chars[i] = '\0'; /* ensure '\0' at end */
- for (i = 0; i < sp->leapcnt; ++i) {
- register struct lsinfo * lsisp;
-
- lsisp = &sp->lsis[i];
- lsisp->ls_trans = (stored == 4) ?
- detzcode(p) : detzcode64(p);
- p += stored;
- lsisp->ls_corr = detzcode(p);
- p += 4;
- }
- for (i = 0; i < sp->typecnt; ++i) {
- register struct ttinfo * ttisp;
-
- ttisp = &sp->ttis[i];
- if (ttisstdcnt == 0)
- ttisp->tt_ttisstd = FALSE;
- else {
- ttisp->tt_ttisstd = *p++;
- if (ttisp->tt_ttisstd != TRUE &&
- ttisp->tt_ttisstd != FALSE)
- goto oops;
- }
- }
- for (i = 0; i < sp->typecnt; ++i) {
- register struct ttinfo * ttisp;
-
- ttisp = &sp->ttis[i];
- if (ttisgmtcnt == 0)
- ttisp->tt_ttisgmt = FALSE;
- else {
- ttisp->tt_ttisgmt = *p++;
- if (ttisp->tt_ttisgmt != TRUE &&
- ttisp->tt_ttisgmt != FALSE)
- goto oops;
- }
- }
- /*
- ** If this is an old file, we're done.
- */
- if (up->tzhead.tzh_version[0] == '\0')
- break;
- nread -= p - up->buf;
- for (i = 0; i < nread; ++i)
- up->buf[i] = p[i];
- /*
- ** If this is a signed narrow time_t system, we're done.
- */
- if (TYPE_SIGNED(time_t) && stored >= (int) sizeof(time_t))
- break;
- }
- if (doextend && nread > 2 &&
- up->buf[0] == '\n' && up->buf[nread - 1] == '\n' &&
- sp->typecnt + 2 <= TZ_MAX_TYPES) {
- struct state *ts = &lsp->u.st;
- register int result;
-
- up->buf[nread - 1] = '\0';
- result = tzparse(&up->buf[1], ts, FALSE);
- if (result == 0 && ts->typecnt == 2 &&
- sp->charcnt + ts->charcnt <= TZ_MAX_CHARS) {
- for (i = 0; i < 2; ++i)
- ts->ttis[i].tt_abbrind +=
- sp->charcnt;
- for (i = 0; i < ts->charcnt; ++i)
- sp->chars[sp->charcnt++] =
- ts->chars[i];
- i = 0;
- while (i < ts->timecnt &&
- ts->ats[i] <=
- sp->ats[sp->timecnt - 1])
- ++i;
- while (i < ts->timecnt &&
- sp->timecnt < TZ_MAX_TIMES) {
- sp->ats[sp->timecnt] =
- ts->ats[i];
- sp->types[sp->timecnt] =
- sp->typecnt +
- ts->types[i];
- ++sp->timecnt;
- ++i;
- }
- sp->ttis[sp->typecnt++] = ts->ttis[0];
- sp->ttis[sp->typecnt++] = ts->ttis[1];
- }
- }
- if (sp->timecnt > 1) {
- for (i = 1; i < sp->timecnt; ++i)
- if (typesequiv(sp, sp->types[i], sp->types[0]) &&
- differ_by_repeat(sp->ats[i], sp->ats[0])) {
- sp->goback = TRUE;
- break;
- }
- for (i = sp->timecnt - 2; i >= 0; --i)
- if (typesequiv(sp, sp->types[sp->timecnt - 1],
- sp->types[i]) &&
- differ_by_repeat(sp->ats[sp->timecnt - 1],
- sp->ats[i])) {
- sp->goahead = TRUE;
- break;
- }
- }
- /*
- ** If type 0 is is unused in transitions,
- ** it's the type to use for early times.
- */
- for (i = 0; i < sp->typecnt; ++i)
- if (sp->types[i] == 0)
- break;
- i = (i >= sp->typecnt) ? 0 : -1;
- /*
- ** Absent the above,
- ** if there are transition times
- ** and the first transition is to a daylight time
- ** find the standard type less than and closest to
- ** the type of the first transition.
- */
- if (i < 0 && sp->timecnt > 0 && sp->ttis[sp->types[0]].tt_isdst) {
- i = sp->types[0];
- while (--i >= 0)
- if (!sp->ttis[i].tt_isdst)
- break;
- }
- /*
- ** If no result yet, find the first standard type.
- ** If there is none, punt to type zero.
- */
- if (i < 0) {
- i = 0;
- while (sp->ttis[i].tt_isdst)
- if (++i >= sp->typecnt) {
- i = 0;
- break;
- }
- }
- sp->defaulttype = i;
-#ifdef ALL_STATE
- free(up);
-#endif /* defined ALL_STATE */
- return 0;
-oops:
-#ifdef ALL_STATE
- free(up);
-#endif /* defined ALL_STATE */
- return -1;
-}
-
-static int
-typesequiv(const struct state *const sp, const int a, const int b)
-{
- register int result;
-
- if (sp == NULL ||
- a < 0 || a >= sp->typecnt ||
- b < 0 || b >= sp->typecnt)
- result = FALSE;
- else {
- register const struct ttinfo * ap = &sp->ttis[a];
- register const struct ttinfo * bp = &sp->ttis[b];
- result = ap->tt_gmtoff == bp->tt_gmtoff &&
- ap->tt_isdst == bp->tt_isdst &&
- ap->tt_ttisstd == bp->tt_ttisstd &&
- ap->tt_ttisgmt == bp->tt_ttisgmt &&
- strcmp(&sp->chars[ap->tt_abbrind],
- &sp->chars[bp->tt_abbrind]) == 0;
- }
- return result;
-}
-
-static const int mon_lengths[2][MONSPERYEAR] = {
- { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
- { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+ /* The entire buffer. */
+ char buf[2 * sizeof(struct tzhead) + 2 * sizeof (struct state)
+ + 4 * TZ_MAX_TIMES];
};
-static const int year_lengths[2] = {
- DAYSPERNYEAR, DAYSPERLYEAR
+/* Local storage needed for 'tzloadbody'. */
+union local_storage {
+ /* The file name to be opened. */
+ char fullname[FILENAME_MAX + 1];
+
+ /* The results of analyzing the file's contents after it is opened. */
+ struct {
+ /* The input buffer. */
+ union input_buffer u;
+
+ /* A temporary state used for parsing a TZ string in the file. */
+ struct state st;
+ } u;
+};
+
+static int __bionic_open_tzdata(const char*);
+
+/* Load tz data from the file named NAME into *SP. Read extended
+ format if DOEXTEND. Use *LSP for temporary storage. Return 0 on
+ success, an errno value on failure. */
+static int
+tzloadbody(char const *name, struct state *sp, bool doextend,
+ union local_storage *lsp)
+{
+ register int i;
+ register int fid;
+ register int stored;
+ register ssize_t nread;
+#if !defined(__ANDROID__)
+ register bool doaccess;
+ register char *fullname = lsp->fullname;
+#endif
+ register union input_buffer *up = &lsp->u.u;
+ register int tzheadsize = sizeof (struct tzhead);
+
+ sp->goback = sp->goahead = false;
+
+ if (! name) {
+ name = TZDEFAULT;
+ if (! name)
+ return EINVAL;
+ }
+
+#if defined(__ANDROID__)
+ fid = __bionic_open_tzdata(name);
+#else
+ if (name[0] == ':')
+ ++name;
+ doaccess = name[0] == '/';
+ if (!doaccess) {
+ char const *p = TZDIR;
+ if (! p)
+ return EINVAL;
+ if (sizeof lsp->fullname - 1 <= strlen(p) + strlen(name))
+ return ENAMETOOLONG;
+ strcpy(fullname, p);
+ strcat(fullname, "/");
+ strcat(fullname, name);
+ /* Set doaccess if '.' (as in "../") shows up in name. */
+ if (strchr(name, '.'))
+ doaccess = true;
+ name = fullname;
+ }
+ if (doaccess && access(name, R_OK) != 0)
+ return errno;
+ fid = open(name, OPEN_MODE);
+#endif
+ if (fid < 0)
+ return errno;
+
+ nread = read(fid, up->buf, sizeof up->buf);
+ if (nread < tzheadsize) {
+ int err = nread < 0 ? errno : EINVAL;
+ close(fid);
+ return err;
+ }
+ if (close(fid) < 0)
+ return errno;
+ for (stored = 4; stored <= 8; stored *= 2) {
+ int_fast32_t ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt);
+ int_fast32_t ttisgmtcnt = detzcode(up->tzhead.tzh_ttisgmtcnt);
+ int_fast32_t leapcnt = detzcode(up->tzhead.tzh_leapcnt);
+ int_fast32_t timecnt = detzcode(up->tzhead.tzh_timecnt);
+ int_fast32_t typecnt = detzcode(up->tzhead.tzh_typecnt);
+ int_fast32_t charcnt = detzcode(up->tzhead.tzh_charcnt);
+ char const *p = up->buf + tzheadsize;
+ if (! (0 <= leapcnt && leapcnt < TZ_MAX_LEAPS
+ && 0 < typecnt && typecnt < TZ_MAX_TYPES
+ && 0 <= timecnt && timecnt < TZ_MAX_TIMES
+ && 0 <= charcnt && charcnt < TZ_MAX_CHARS
+ && (ttisstdcnt == typecnt || ttisstdcnt == 0)
+ && (ttisgmtcnt == typecnt || ttisgmtcnt == 0)))
+ return EINVAL;
+ if (nread
+ < (tzheadsize /* struct tzhead */
+ + timecnt * stored /* ats */
+ + timecnt /* types */
+ + typecnt * 6 /* ttinfos */
+ + charcnt /* chars */
+ + leapcnt * (stored + 4) /* lsinfos */
+ + ttisstdcnt /* ttisstds */
+ + ttisgmtcnt)) /* ttisgmts */
+ return EINVAL;
+ sp->leapcnt = leapcnt;
+ sp->timecnt = timecnt;
+ sp->typecnt = typecnt;
+ sp->charcnt = charcnt;
+
+ /* Read transitions, discarding those out of time_t range.
+ But pretend the last transition before time_t_min
+ occurred at time_t_min. */
+ timecnt = 0;
+ for (i = 0; i < sp->timecnt; ++i) {
+ int_fast64_t at
+ = stored == 4 ? detzcode(p) : detzcode64(p);
+ sp->types[i] = at <= time_t_max;
+ if (sp->types[i]) {
+ time_t attime
+ = ((TYPE_SIGNED(time_t) ? at < time_t_min : at < 0)
+ ? time_t_min : at);
+ if (timecnt && attime <= sp->ats[timecnt - 1]) {
+ if (attime < sp->ats[timecnt - 1])
+ return EINVAL;
+ sp->types[i - 1] = 0;
+ timecnt--;
+ }
+ sp->ats[timecnt++] = attime;
+ }
+ p += stored;
+ }
+
+ timecnt = 0;
+ for (i = 0; i < sp->timecnt; ++i) {
+ unsigned char typ = *p++;
+ if (sp->typecnt <= typ)
+ return EINVAL;
+ if (sp->types[i])
+ sp->types[timecnt++] = typ;
+ }
+ sp->timecnt = timecnt;
+ for (i = 0; i < sp->typecnt; ++i) {
+ register struct ttinfo * ttisp;
+ unsigned char isdst, abbrind;
+
+ ttisp = &sp->ttis[i];
+ ttisp->tt_gmtoff = detzcode(p);
+ p += 4;
+ isdst = *p++;
+ if (! (isdst < 2))
+ return EINVAL;
+ ttisp->tt_isdst = isdst;
+ abbrind = *p++;
+ if (! (abbrind < sp->charcnt))
+ return EINVAL;
+ ttisp->tt_abbrind = abbrind;
+ }
+ for (i = 0; i < sp->charcnt; ++i)
+ sp->chars[i] = *p++;
+ sp->chars[i] = '\0'; /* ensure '\0' at end */
+
+ /* Read leap seconds, discarding those out of time_t range. */
+ leapcnt = 0;
+ for (i = 0; i < sp->leapcnt; ++i) {
+ int_fast64_t tr = stored == 4 ? detzcode(p) : detzcode64(p);
+ int_fast32_t corr = detzcode(p + stored);
+ p += stored + 4;
+ if (tr <= time_t_max) {
+ time_t trans
+ = ((TYPE_SIGNED(time_t) ? tr < time_t_min : tr < 0)
+ ? time_t_min : tr);
+ if (leapcnt && trans <= sp->lsis[leapcnt - 1].ls_trans) {
+ if (trans < sp->lsis[leapcnt - 1].ls_trans)
+ return EINVAL;
+ leapcnt--;
+ }
+ sp->lsis[leapcnt].ls_trans = trans;
+ sp->lsis[leapcnt].ls_corr = corr;
+ leapcnt++;
+ }
+ }
+ sp->leapcnt = leapcnt;
+
+ for (i = 0; i < sp->typecnt; ++i) {
+ register struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ if (ttisstdcnt == 0)
+ ttisp->tt_ttisstd = false;
+ else {
+ if (*p != true && *p != false)
+ return EINVAL;
+ ttisp->tt_ttisstd = *p++;
+ }
+ }
+ for (i = 0; i < sp->typecnt; ++i) {
+ register struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ if (ttisgmtcnt == 0)
+ ttisp->tt_ttisgmt = false;
+ else {
+ if (*p != true && *p != false)
+ return EINVAL;
+ ttisp->tt_ttisgmt = *p++;
+ }
+ }
+ /*
+ ** If this is an old file, we're done.
+ */
+ if (up->tzhead.tzh_version[0] == '\0')
+ break;
+ nread -= p - up->buf;
+ memmove(up->buf, p, nread);
+ }
+ if (doextend && nread > 2 &&
+ up->buf[0] == '\n' && up->buf[nread - 1] == '\n' &&
+ sp->typecnt + 2 <= TZ_MAX_TYPES) {
+ struct state *ts = &lsp->u.st;
+
+ up->buf[nread - 1] = '\0';
+ if (tzparse(&up->buf[1], ts, false)
+ && ts->typecnt == 2) {
+
+ /* Attempt to reuse existing abbreviations.
+ Without this, America/Anchorage would stop
+ working after 2037 when TZ_MAX_CHARS is 50, as
+ sp->charcnt equals 42 (for LMT CAT CAWT CAPT AHST
+ AHDT YST AKDT AKST) and ts->charcnt equals 10
+ (for AKST AKDT). Reusing means sp->charcnt can
+ stay 42 in this example. */
+ int gotabbr = 0;
+ int charcnt = sp->charcnt;
+ for (i = 0; i < 2; i++) {
+ char *tsabbr = ts->chars + ts->ttis[i].tt_abbrind;
+ int j;
+ for (j = 0; j < charcnt; j++)
+ if (strcmp(sp->chars + j, tsabbr) == 0) {
+ ts->ttis[i].tt_abbrind = j;
+ gotabbr++;
+ break;
+ }
+ if (! (j < charcnt)) {
+ int tsabbrlen = strlen(tsabbr);
+ if (j + tsabbrlen < TZ_MAX_CHARS) {
+ strcpy(sp->chars + j, tsabbr);
+ charcnt = j + tsabbrlen + 1;
+ ts->ttis[i].tt_abbrind = j;
+ gotabbr++;
+ }
+ }
+ }
+ if (gotabbr == 2) {
+ sp->charcnt = charcnt;
+ for (i = 0; i < ts->timecnt; i++)
+ if (sp->ats[sp->timecnt - 1] < ts->ats[i])
+ break;
+ while (i < ts->timecnt
+ && sp->timecnt < TZ_MAX_TIMES) {
+ sp->ats[sp->timecnt] = ts->ats[i];
+ sp->types[sp->timecnt] = (sp->typecnt
+ + ts->types[i]);
+ sp->timecnt++;
+ i++;
+ }
+ sp->ttis[sp->typecnt++] = ts->ttis[0];
+ sp->ttis[sp->typecnt++] = ts->ttis[1];
+ }
+ }
+ }
+ if (sp->timecnt > 1) {
+ for (i = 1; i < sp->timecnt; ++i)
+ if (typesequiv(sp, sp->types[i], sp->types[0]) &&
+ differ_by_repeat(sp->ats[i], sp->ats[0])) {
+ sp->goback = true;
+ break;
+ }
+ for (i = sp->timecnt - 2; i >= 0; --i)
+ if (typesequiv(sp, sp->types[sp->timecnt - 1],
+ sp->types[i]) &&
+ differ_by_repeat(sp->ats[sp->timecnt - 1],
+ sp->ats[i])) {
+ sp->goahead = true;
+ break;
+ }
+ }
+ /*
+ ** If type 0 is is unused in transitions,
+ ** it's the type to use for early times.
+ */
+ for (i = 0; i < sp->timecnt; ++i)
+ if (sp->types[i] == 0)
+ break;
+ i = i < sp->timecnt ? -1 : 0;
+ /*
+ ** Absent the above,
+ ** if there are transition times
+ ** and the first transition is to a daylight time
+ ** find the standard type less than and closest to
+ ** the type of the first transition.
+ */
+ if (i < 0 && sp->timecnt > 0 && sp->ttis[sp->types[0]].tt_isdst) {
+ i = sp->types[0];
+ while (--i >= 0)
+ if (!sp->ttis[i].tt_isdst)
+ break;
+ }
+ /*
+ ** If no result yet, find the first standard type.
+ ** If there is none, punt to type zero.
+ */
+ if (i < 0) {
+ i = 0;
+ while (sp->ttis[i].tt_isdst)
+ if (++i >= sp->typecnt) {
+ i = 0;
+ break;
+ }
+ }
+ sp->defaulttype = i;
+ return 0;
+}
+
+/* Load tz data from the file named NAME into *SP. Read extended
+ format if DOEXTEND. Return 0 on success, an errno value on failure. */
+static int
+tzload(char const *name, struct state *sp, bool doextend)
+{
+#ifdef ALL_STATE
+ union local_storage *lsp = malloc(sizeof *lsp);
+ if (!lsp)
+ return errno;
+ else {
+ int err = tzloadbody(name, sp, doextend, lsp);
+ free(lsp);
+ return err;
+ }
+#else
+ union local_storage ls;
+ return tzloadbody(name, sp, doextend, &ls);
+#endif
+}
+
+static bool
+typesequiv(const struct state *sp, int a, int b)
+{
+ register bool result;
+
+ if (sp == NULL ||
+ a < 0 || a >= sp->typecnt ||
+ b < 0 || b >= sp->typecnt)
+ result = false;
+ else {
+ register const struct ttinfo * ap = &sp->ttis[a];
+ register const struct ttinfo * bp = &sp->ttis[b];
+ result = ap->tt_gmtoff == bp->tt_gmtoff &&
+ ap->tt_isdst == bp->tt_isdst &&
+ ap->tt_ttisstd == bp->tt_ttisstd &&
+ ap->tt_ttisgmt == bp->tt_ttisgmt &&
+ strcmp(&sp->chars[ap->tt_abbrind],
+ &sp->chars[bp->tt_abbrind]) == 0;
+ }
+ return result;
+}
+
+static const int mon_lengths[2][MONSPERYEAR] = {
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+static const int year_lengths[2] = {
+ DAYSPERNYEAR, DAYSPERLYEAR
};
/*
@@ -658,15 +732,15 @@
** character.
*/
-static const char *
-getzname(register const char * strp)
+static const char * ATTRIBUTE_PURE
+getzname(register const char *strp)
{
- register char c;
+ register char c;
- while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&
- c != '+')
- ++strp;
- return strp;
+ while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&
+ c != '+')
+ ++strp;
+ return strp;
}
/*
@@ -678,14 +752,14 @@
** We don't do any checking here; checking is done later in common-case code.
*/
-static const char *
+static const char * ATTRIBUTE_PURE
getqzname(register const char *strp, const int delim)
{
- register int c;
+ register int c;
- while ((c = *strp) != '\0' && c != delim)
- ++strp;
- return strp;
+ while ((c = *strp) != '\0' && c != delim)
+ ++strp;
+ return strp;
}
/*
@@ -696,24 +770,24 @@
*/
static const char *
-getnum(register const char * strp, int * const nump, const int min, const int max)
+getnum(register const char *strp, int *const nump, const int min, const int max)
{
- register char c;
- register int num;
+ register char c;
+ register int num;
- if (strp == NULL || !is_digit(c = *strp))
- return NULL;
- num = 0;
- do {
- num = num * 10 + (c - '0');
- if (num > max)
- return NULL; /* illegal value */
- c = *++strp;
- } while (is_digit(c));
- if (num < min)
- return NULL; /* illegal value */
- *nump = num;
- return strp;
+ if (strp == NULL || !is_digit(c = *strp))
+ return NULL;
+ num = 0;
+ do {
+ num = num * 10 + (c - '0');
+ if (num > max)
+ return NULL; /* illegal value */
+ c = *++strp;
+ } while (is_digit(c));
+ if (num < min)
+ return NULL; /* illegal value */
+ *nump = num;
+ return strp;
}
/*
@@ -727,34 +801,34 @@
static const char *
getsecs(register const char *strp, int_fast32_t *const secsp)
{
- int num;
+ int num;
- /*
- ** 'HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
- ** "M10.4.6/26", which does not conform to Posix,
- ** but which specifies the equivalent of
- ** "02:00 on the first Sunday on or after 23 Oct".
- */
- strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
- if (strp == NULL)
- return NULL;
- *secsp = num * (int_fast32_t) SECSPERHOUR;
- if (*strp == ':') {
- ++strp;
- strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
- if (strp == NULL)
- return NULL;
- *secsp += num * SECSPERMIN;
- if (*strp == ':') {
- ++strp;
- /* 'SECSPERMIN' allows for leap seconds. */
- strp = getnum(strp, &num, 0, SECSPERMIN);
- if (strp == NULL)
- return NULL;
- *secsp += num;
- }
- }
- return strp;
+ /*
+ ** 'HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
+ ** "M10.4.6/26", which does not conform to Posix,
+ ** but which specifies the equivalent of
+ ** "02:00 on the first Sunday on or after 23 Oct".
+ */
+ strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
+ if (strp == NULL)
+ return NULL;
+ *secsp = num * (int_fast32_t) SECSPERHOUR;
+ if (*strp == ':') {
+ ++strp;
+ strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
+ if (strp == NULL)
+ return NULL;
+ *secsp += num * SECSPERMIN;
+ if (*strp == ':') {
+ ++strp;
+ /* 'SECSPERMIN' allows for leap seconds. */
+ strp = getnum(strp, &num, 0, SECSPERMIN);
+ if (strp == NULL)
+ return NULL;
+ *secsp += num;
+ }
+ }
+ return strp;
}
/*
@@ -767,19 +841,19 @@
static const char *
getoffset(register const char *strp, int_fast32_t *const offsetp)
{
- register int neg = 0;
+ register bool neg = false;
- if (*strp == '-') {
- neg = 1;
- ++strp;
- } else if (*strp == '+')
- ++strp;
- strp = getsecs(strp, offsetp);
- if (strp == NULL)
- return NULL; /* illegal time */
- if (neg)
- *offsetp = -*offsetp;
- return strp;
+ if (*strp == '-') {
+ neg = true;
+ ++strp;
+ } else if (*strp == '+')
+ ++strp;
+ strp = getsecs(strp, offsetp);
+ if (strp == NULL)
+ return NULL; /* illegal time */
+ if (neg)
+ *offsetp = -*offsetp;
+ return strp;
}
/*
@@ -790,49 +864,49 @@
*/
static const char *
-getrule(const char * strp, register struct rule * const rulep)
+getrule(const char *strp, register struct rule *const rulep)
{
- if (*strp == 'J') {
- /*
- ** Julian day.
- */
- rulep->r_type = JULIAN_DAY;
- ++strp;
- strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
- } else if (*strp == 'M') {
- /*
- ** Month, week, day.
- */
- rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
- ++strp;
- strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
- if (strp == NULL)
- return NULL;
- if (*strp++ != '.')
- return NULL;
- strp = getnum(strp, &rulep->r_week, 1, 5);
- if (strp == NULL)
- return NULL;
- if (*strp++ != '.')
- return NULL;
- strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
- } else if (is_digit(*strp)) {
- /*
- ** Day of year.
- */
- rulep->r_type = DAY_OF_YEAR;
- strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
- } else return NULL; /* invalid format */
- if (strp == NULL)
- return NULL;
- if (*strp == '/') {
- /*
- ** Time specified.
- */
- ++strp;
- strp = getoffset(strp, &rulep->r_time);
- } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */
- return strp;
+ if (*strp == 'J') {
+ /*
+ ** Julian day.
+ */
+ rulep->r_type = JULIAN_DAY;
+ ++strp;
+ strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
+ } else if (*strp == 'M') {
+ /*
+ ** Month, week, day.
+ */
+ rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
+ ++strp;
+ strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
+ if (strp == NULL)
+ return NULL;
+ if (*strp++ != '.')
+ return NULL;
+ strp = getnum(strp, &rulep->r_week, 1, 5);
+ if (strp == NULL)
+ return NULL;
+ if (*strp++ != '.')
+ return NULL;
+ strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
+ } else if (is_digit(*strp)) {
+ /*
+ ** Day of year.
+ */
+ rulep->r_type = DAY_OF_YEAR;
+ strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
+ } else return NULL; /* invalid format */
+ if (strp == NULL)
+ return NULL;
+ if (*strp == '/') {
+ /*
+ ** Time specified.
+ */
+ ++strp;
+ strp = getoffset(strp, &rulep->r_time);
+ } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */
+ return strp;
}
/*
@@ -840,11 +914,11 @@
** effect, calculate the year-relative time that rule takes effect.
*/
-static int_fast32_t
+static int_fast32_t ATTRIBUTE_PURE
transtime(const int year, register const struct rule *const rulep,
const int_fast32_t offset)
{
- register int leapyear;
+ register bool leapyear;
register int_fast32_t value;
register int i;
int d, m1, yy0, yy1, yy2, dow;
@@ -931,477 +1005,537 @@
** appropriate.
*/
-static int
-tzparse(const char * name, register struct state * const sp,
- const int lastditch)
+static bool
+tzparse(const char *name, struct state *sp, bool lastditch)
{
- const char * stdname;
- const char * dstname;
- size_t stdlen;
- size_t dstlen;
- int_fast32_t stdoffset;
- int_fast32_t dstoffset;
- register char * cp;
- register int load_result;
- static struct ttinfo zttinfo;
+ const char * stdname;
+ const char * dstname;
+ size_t stdlen;
+ size_t dstlen;
+ size_t charcnt;
+ int_fast32_t stdoffset;
+ int_fast32_t dstoffset;
+ register char * cp;
+ register bool load_ok;
- stdname = name;
- if (lastditch) {
- stdlen = strlen(name); /* length of standard zone name */
- name += stdlen;
- if (stdlen >= sizeof sp->chars)
- stdlen = (sizeof sp->chars) - 1;
- stdoffset = 0;
- } else {
- if (*name == '<') {
- name++;
- stdname = name;
- name = getqzname(name, '>');
- if (*name != '>')
- return (-1);
- stdlen = name - stdname;
- name++;
- } else {
- name = getzname(name);
- stdlen = name - stdname;
- }
- if (*name == '\0')
- return -1;
- name = getoffset(name, &stdoffset);
- if (name == NULL)
- return -1;
- }
- load_result = tzload(TZDEFRULES, sp, FALSE);
- if (load_result != 0)
- sp->leapcnt = 0; /* so, we're off a little */
- if (*name != '\0') {
- if (*name == '<') {
- dstname = ++name;
- name = getqzname(name, '>');
- if (*name != '>')
- return -1;
- dstlen = name - dstname;
- name++;
- } else {
- dstname = name;
- name = getzname(name);
- dstlen = name - dstname; /* length of DST zone name */
- }
- if (*name != '\0' && *name != ',' && *name != ';') {
- name = getoffset(name, &dstoffset);
- if (name == NULL)
- return -1;
- } else dstoffset = stdoffset - SECSPERHOUR;
- if (*name == '\0' && load_result != 0)
- name = TZDEFRULESTRING;
- if (*name == ',' || *name == ';') {
- struct rule start;
- struct rule end;
- register int year;
- register int yearlim;
- register int timecnt;
- time_t janfirst;
+ stdname = name;
+ if (lastditch) {
+ stdlen = sizeof gmt - 1;
+ name += stdlen;
+ stdoffset = 0;
+ } else {
+ if (*name == '<') {
+ name++;
+ stdname = name;
+ name = getqzname(name, '>');
+ if (*name != '>')
+ return false;
+ stdlen = name - stdname;
+ name++;
+ } else {
+ name = getzname(name);
+ stdlen = name - stdname;
+ }
+ if (!stdlen)
+ return false;
+ name = getoffset(name, &stdoffset);
+ if (name == NULL)
+ return false;
+ }
+ charcnt = stdlen + 1;
+ if (sizeof sp->chars < charcnt)
+ return false;
+ load_ok = tzload(TZDEFRULES, sp, false) == 0;
+ if (!load_ok)
+ sp->leapcnt = 0; /* so, we're off a little */
+ if (*name != '\0') {
+ if (*name == '<') {
+ dstname = ++name;
+ name = getqzname(name, '>');
+ if (*name != '>')
+ return false;
+ dstlen = name - dstname;
+ name++;
+ } else {
+ dstname = name;
+ name = getzname(name);
+ dstlen = name - dstname; /* length of DST zone name */
+ }
+ if (!dstlen)
+ return false;
+ charcnt += dstlen + 1;
+ if (sizeof sp->chars < charcnt)
+ return false;
+ if (*name != '\0' && *name != ',' && *name != ';') {
+ name = getoffset(name, &dstoffset);
+ if (name == NULL)
+ return false;
+ } else dstoffset = stdoffset - SECSPERHOUR;
+ if (*name == '\0' && !load_ok)
+ name = TZDEFRULESTRING;
+ if (*name == ',' || *name == ';') {
+ struct rule start;
+ struct rule end;
+ register int year;
+ register int yearlim;
+ register int timecnt;
+ time_t janfirst;
- ++name;
- if ((name = getrule(name, &start)) == NULL)
- return -1;
- if (*name++ != ',')
- return -1;
- if ((name = getrule(name, &end)) == NULL)
- return -1;
- if (*name != '\0')
- return -1;
- sp->typecnt = 2; /* standard time and DST */
- /*
- ** Two transitions per year, from EPOCH_YEAR forward.
- */
- sp->ttis[0] = sp->ttis[1] = zttinfo;
- sp->ttis[0].tt_gmtoff = -dstoffset;
- sp->ttis[0].tt_isdst = 1;
- sp->ttis[0].tt_abbrind = stdlen + 1;
- sp->ttis[1].tt_gmtoff = -stdoffset;
- sp->ttis[1].tt_isdst = 0;
- sp->ttis[1].tt_abbrind = 0;
- sp->defaulttype = 0;
- timecnt = 0;
- janfirst = 0;
- yearlim = EPOCH_YEAR + YEARSPERREPEAT;
- for (year = EPOCH_YEAR; year < yearlim; year++) {
- int_fast32_t
- starttime = transtime(year, &start, stdoffset),
- endtime = transtime(year, &end, dstoffset);
- int_fast32_t
- yearsecs = (year_lengths[isleap(year)]
- * SECSPERDAY);
- int reversed = endtime < starttime;
- if (reversed) {
- int_fast32_t swap = starttime;
- starttime = endtime;
- endtime = swap;
- }
- if (reversed
- || (starttime < endtime
- && (endtime - starttime
- < (yearsecs
- + (stdoffset - dstoffset))))) {
- if (TZ_MAX_TIMES - 2 < timecnt)
- break;
- yearlim = year + YEARSPERREPEAT + 1;
- sp->ats[timecnt] = janfirst;
- if (increment_overflow_time
- (&sp->ats[timecnt], starttime))
- break;
- sp->types[timecnt++] = reversed;
- sp->ats[timecnt] = janfirst;
- if (increment_overflow_time
- (&sp->ats[timecnt], endtime))
- break;
- sp->types[timecnt++] = !reversed;
- }
- if (increment_overflow_time(&janfirst, yearsecs))
- break;
- }
- sp->timecnt = timecnt;
- if (!timecnt)
- sp->typecnt = 1; /* Perpetual DST. */
- } else {
- register int_fast32_t theirstdoffset;
- register int_fast32_t theirdstoffset;
- register int_fast32_t theiroffset;
- register int isdst;
- register int i;
- register int j;
+ ++name;
+ if ((name = getrule(name, &start)) == NULL)
+ return false;
+ if (*name++ != ',')
+ return false;
+ if ((name = getrule(name, &end)) == NULL)
+ return false;
+ if (*name != '\0')
+ return false;
+ sp->typecnt = 2; /* standard time and DST */
+ /*
+ ** Two transitions per year, from EPOCH_YEAR forward.
+ */
+ init_ttinfo(&sp->ttis[0], -dstoffset, true, stdlen + 1);
+ init_ttinfo(&sp->ttis[1], -stdoffset, false, 0);
+ sp->defaulttype = 0;
+ timecnt = 0;
+ janfirst = 0;
+ yearlim = EPOCH_YEAR + YEARSPERREPEAT;
+ for (year = EPOCH_YEAR; year < yearlim; year++) {
+ int_fast32_t
+ starttime = transtime(year, &start, stdoffset),
+ endtime = transtime(year, &end, dstoffset);
+ int_fast32_t
+ yearsecs = (year_lengths[isleap(year)]
+ * SECSPERDAY);
+ bool reversed = endtime < starttime;
+ if (reversed) {
+ int_fast32_t swap = starttime;
+ starttime = endtime;
+ endtime = swap;
+ }
+ if (reversed
+ || (starttime < endtime
+ && (endtime - starttime
+ < (yearsecs
+ + (stdoffset - dstoffset))))) {
+ if (TZ_MAX_TIMES - 2 < timecnt)
+ break;
+ yearlim = year + YEARSPERREPEAT + 1;
+ sp->ats[timecnt] = janfirst;
+ if (increment_overflow_time
+ (&sp->ats[timecnt], starttime))
+ break;
+ sp->types[timecnt++] = reversed;
+ sp->ats[timecnt] = janfirst;
+ if (increment_overflow_time
+ (&sp->ats[timecnt], endtime))
+ break;
+ sp->types[timecnt++] = !reversed;
+ }
+ if (increment_overflow_time(&janfirst, yearsecs))
+ break;
+ }
+ sp->timecnt = timecnt;
+ if (!timecnt)
+ sp->typecnt = 1; /* Perpetual DST. */
+ } else {
+ register int_fast32_t theirstdoffset;
+ register int_fast32_t theirdstoffset;
+ register int_fast32_t theiroffset;
+ register bool isdst;
+ register int i;
+ register int j;
- if (*name != '\0')
- return -1;
- /*
- ** Initial values of theirstdoffset and theirdstoffset.
- */
- theirstdoffset = 0;
- for (i = 0; i < sp->timecnt; ++i) {
- j = sp->types[i];
- if (!sp->ttis[j].tt_isdst) {
- theirstdoffset =
- -sp->ttis[j].tt_gmtoff;
- break;
- }
- }
- theirdstoffset = 0;
- for (i = 0; i < sp->timecnt; ++i) {
- j = sp->types[i];
- if (sp->ttis[j].tt_isdst) {
- theirdstoffset =
- -sp->ttis[j].tt_gmtoff;
- break;
- }
- }
- /*
- ** Initially we're assumed to be in standard time.
- */
- isdst = FALSE;
- theiroffset = theirstdoffset;
- /*
- ** Now juggle transition times and types
- ** tracking offsets as you do.
- */
- for (i = 0; i < sp->timecnt; ++i) {
- j = sp->types[i];
- sp->types[i] = sp->ttis[j].tt_isdst;
- if (sp->ttis[j].tt_ttisgmt) {
- /* No adjustment to transition time */
- } else {
- /*
- ** If summer time is in effect, and the
- ** transition time was not specified as
- ** standard time, add the summer time
- ** offset to the transition time;
- ** otherwise, add the standard time
- ** offset to the transition time.
- */
- /*
- ** Transitions from DST to DDST
- ** will effectively disappear since
- ** POSIX provides for only one DST
- ** offset.
- */
- if (isdst && !sp->ttis[j].tt_ttisstd) {
- sp->ats[i] += dstoffset -
- theirdstoffset;
- } else {
- sp->ats[i] += stdoffset -
- theirstdoffset;
- }
- }
- theiroffset = -sp->ttis[j].tt_gmtoff;
- if (sp->ttis[j].tt_isdst)
- theirdstoffset = theiroffset;
- else theirstdoffset = theiroffset;
- }
- /*
- ** Finally, fill in ttis.
- */
- sp->ttis[0] = sp->ttis[1] = zttinfo;
- sp->ttis[0].tt_gmtoff = -stdoffset;
- sp->ttis[0].tt_isdst = FALSE;
- sp->ttis[0].tt_abbrind = 0;
- sp->ttis[1].tt_gmtoff = -dstoffset;
- sp->ttis[1].tt_isdst = TRUE;
- sp->ttis[1].tt_abbrind = stdlen + 1;
- sp->typecnt = 2;
- sp->defaulttype = 0;
- }
- } else {
- dstlen = 0;
- sp->typecnt = 1; /* only standard time */
- sp->timecnt = 0;
- sp->ttis[0] = zttinfo;
- sp->ttis[0].tt_gmtoff = -stdoffset;
- sp->ttis[0].tt_isdst = 0;
- sp->ttis[0].tt_abbrind = 0;
- sp->defaulttype = 0;
- }
- sp->charcnt = stdlen + 1;
- if (dstlen != 0)
- sp->charcnt += dstlen + 1;
- if ((size_t) sp->charcnt > sizeof sp->chars)
- return -1;
- cp = sp->chars;
- (void) strncpy(cp, stdname, stdlen);
- cp += stdlen;
- *cp++ = '\0';
- if (dstlen != 0) {
- (void) strncpy(cp, dstname, dstlen);
- *(cp + dstlen) = '\0';
- }
- return 0;
+ if (*name != '\0')
+ return false;
+ /*
+ ** Initial values of theirstdoffset and theirdstoffset.
+ */
+ theirstdoffset = 0;
+ for (i = 0; i < sp->timecnt; ++i) {
+ j = sp->types[i];
+ if (!sp->ttis[j].tt_isdst) {
+ theirstdoffset =
+ -sp->ttis[j].tt_gmtoff;
+ break;
+ }
+ }
+ theirdstoffset = 0;
+ for (i = 0; i < sp->timecnt; ++i) {
+ j = sp->types[i];
+ if (sp->ttis[j].tt_isdst) {
+ theirdstoffset =
+ -sp->ttis[j].tt_gmtoff;
+ break;
+ }
+ }
+ /*
+ ** Initially we're assumed to be in standard time.
+ */
+ isdst = false;
+ theiroffset = theirstdoffset;
+ /*
+ ** Now juggle transition times and types
+ ** tracking offsets as you do.
+ */
+ for (i = 0; i < sp->timecnt; ++i) {
+ j = sp->types[i];
+ sp->types[i] = sp->ttis[j].tt_isdst;
+ if (sp->ttis[j].tt_ttisgmt) {
+ /* No adjustment to transition time */
+ } else {
+ /*
+ ** If summer time is in effect, and the
+ ** transition time was not specified as
+ ** standard time, add the summer time
+ ** offset to the transition time;
+ ** otherwise, add the standard time
+ ** offset to the transition time.
+ */
+ /*
+ ** Transitions from DST to DDST
+ ** will effectively disappear since
+ ** POSIX provides for only one DST
+ ** offset.
+ */
+ if (isdst && !sp->ttis[j].tt_ttisstd) {
+ sp->ats[i] += dstoffset -
+ theirdstoffset;
+ } else {
+ sp->ats[i] += stdoffset -
+ theirstdoffset;
+ }
+ }
+ theiroffset = -sp->ttis[j].tt_gmtoff;
+ if (sp->ttis[j].tt_isdst)
+ theirdstoffset = theiroffset;
+ else theirstdoffset = theiroffset;
+ }
+ /*
+ ** Finally, fill in ttis.
+ */
+ init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
+ init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1);
+ sp->typecnt = 2;
+ sp->defaulttype = 0;
+ }
+ } else {
+ dstlen = 0;
+ sp->typecnt = 1; /* only standard time */
+ sp->timecnt = 0;
+ init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
+ sp->defaulttype = 0;
+ }
+ sp->charcnt = charcnt;
+ cp = sp->chars;
+ memcpy(cp, stdname, stdlen);
+ cp += stdlen;
+ *cp++ = '\0';
+ if (dstlen != 0) {
+ memcpy(cp, dstname, dstlen);
+ *(cp + dstlen) = '\0';
+ }
+ return true;
}
static void
-gmtload(struct state * const sp)
+gmtload(struct state *const sp)
{
- if (tzload(gmt, sp, TRUE) != 0)
- (void) tzparse(gmt, sp, TRUE);
+ if (tzload(gmt, sp, true) != 0)
+ tzparse(gmt, sp, true);
}
-#ifndef STD_INSPIRED
-/*
-** A non-static declaration of tzsetwall in a system header file
-** may cause a warning about this upcoming static declaration...
-*/
-static
-#endif /* !defined STD_INSPIRED */
+/* Initialize *SP to a value appropriate for the TZ setting NAME.
+ Return 0 on success, an errno value on failure. */
+static int
+zoneinit(struct state *sp, char const *name)
+{
+ if (name && ! name[0]) {
+ /*
+ ** User wants it fast rather than right.
+ */
+ sp->leapcnt = 0; /* so, we're off a little */
+ sp->timecnt = 0;
+ sp->typecnt = 0;
+ sp->charcnt = 0;
+ sp->goback = sp->goahead = false;
+ init_ttinfo(&sp->ttis[0], 0, false, 0);
+ strcpy(sp->chars, gmt);
+ sp->defaulttype = 0;
+ return 0;
+ } else {
+ int err = tzload(name, sp, true);
+ if (err != 0 && name && name[0] != ':' && tzparse(name, sp, false))
+ err = 0;
+ if (err == 0)
+ scrub_abbrs(sp);
+ return err;
+ }
+}
+
+static void
+tzsetlcl(char const *name)
+{
+ struct state *sp = lclptr;
+ int lcl = name ? strlen(name) < sizeof lcl_TZname : -1;
+ if (lcl < 0
+ ? lcl_is_set < 0
+ : 0 < lcl_is_set && strcmp(lcl_TZname, name) == 0)
+ return;
+#ifdef ALL_STATE
+ if (! sp)
+ lclptr = sp = malloc(sizeof *lclptr);
+#endif /* defined ALL_STATE */
+ if (sp) {
+ if (zoneinit(sp, name) != 0)
+ zoneinit(sp, "");
+ if (0 < lcl)
+ strcpy(lcl_TZname, name);
+ }
+ settzname();
+ lcl_is_set = lcl;
+}
+
+#ifdef STD_INSPIRED
void
tzsetwall(void)
{
- if (lcl_is_set < 0)
- return;
- lcl_is_set = -1;
-
-#ifdef ALL_STATE
- if (lclptr == NULL) {
- lclptr = malloc(sizeof *lclptr);
- if (lclptr == NULL) {
- settzname(); /* all we can do */
- return;
- }
- }
-#endif /* defined ALL_STATE */
- if (tzload(NULL, lclptr, TRUE) != 0)
- gmtload(lclptr);
- settzname();
+ if (lock() != 0)
+ return;
+ tzsetlcl(NULL);
+ unlock();
}
+#endif
-#include <stdbool.h>
+#if defined(__ANDROID__)
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h> // For __system_property_serial.
+#endif
static void
-tzset_locked(void)
+tzset_unlocked(void)
{
- register const char * name;
+#if defined(__ANDROID__)
+ const char * name = getenv("TZ");
- name = getenv("TZ");
+ // Try the "persist.sys.timezone" system property.
+ if (name == NULL) {
+ static const prop_info *pi;
- // try the "persist.sys.timezone" system property first
- if (name == NULL) {
- static const prop_info *pi;
-
- if (!pi) {
- pi = __system_property_find("persist.sys.timezone");
- }
- if (pi) {
- static char buf[PROP_VALUE_MAX];
- static uint32_t s = -1;
- static bool ok = false;
- uint32_t serial;
-
- serial = __system_property_serial(pi);
- if (serial != s) {
- ok = __system_property_read(pi, 0, buf) > 0;
- s = serial;
- }
- if (ok) {
- name = buf;
- }
- }
+ if (!pi) {
+ pi = __system_property_find("persist.sys.timezone");
}
-
- if (name == NULL) {
- tzsetwall();
- return;
+ if (pi) {
+ static char buf[PROP_VALUE_MAX];
+ static uint32_t s = -1;
+ static bool ok = false;
+ uint32_t serial = __system_property_serial(pi);
+ if (serial != s) {
+ ok = __system_property_read(pi, 0, buf) > 0;
+ s = serial;
+ }
+ if (ok) {
+ name = buf;
+ }
}
+ }
- if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0)
- return;
- lcl_is_set = strlen(name) < sizeof lcl_TZname;
- if (lcl_is_set)
- (void) strcpy(lcl_TZname, name);
-
-#ifdef ALL_STATE
- if (lclptr == NULL) {
- lclptr = malloc(sizeof *lclptr);
- if (lclptr == NULL) {
- settzname(); /* all we can do */
- return;
- }
- }
-#endif /* defined ALL_STATE */
- if (*name == '\0') {
- /*
- ** User wants it fast rather than right.
- */
- lclptr->leapcnt = 0; /* so, we're off a little */
- lclptr->timecnt = 0;
- lclptr->typecnt = 0;
- lclptr->ttis[0].tt_isdst = 0;
- lclptr->ttis[0].tt_gmtoff = 0;
- lclptr->ttis[0].tt_abbrind = 0;
- (void) strcpy(lclptr->chars, gmt);
- lclptr->defaulttype = 0;
- } else if (tzload(name, lclptr, TRUE) != 0)
- if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
- (void) gmtload(lclptr);
- settzname();
+ tzsetlcl(name);
+#else
+ tzsetlcl(getenv("TZ"));
+#endif
}
void
tzset(void)
{
- _tzLock();
- tzset_locked();
- _tzUnlock();
+ if (lock() != 0)
+ return;
+ tzset_unlocked();
+ unlock();
+}
+
+static void
+gmtcheck(void)
+{
+ static bool gmt_is_set;
+ if (lock() != 0)
+ return;
+ if (! gmt_is_set) {
+#ifdef ALL_STATE
+ gmtptr = malloc(sizeof *gmtptr);
+#endif
+ if (gmtptr)
+ gmtload(gmtptr);
+ gmt_is_set = true;
+ }
+ unlock();
+}
+
+#if NETBSD_INSPIRED
+
+timezone_t
+tzalloc(char const *name)
+{
+ timezone_t sp = malloc(sizeof *sp);
+ if (sp) {
+ int err = zoneinit(sp, name);
+ if (err != 0) {
+ free(sp);
+ errno = err;
+ return NULL;
+ }
+ }
+ return sp;
+}
+
+void
+tzfree(timezone_t sp)
+{
+ free(sp);
}
/*
+** NetBSD 6.1.4 has ctime_rz, but omit it because POSIX says ctime and
+** ctime_r are obsolescent and have potential security problems that
+** ctime_rz would share. Callers can instead use localtime_rz + strftime.
+**
+** NetBSD 6.1.4 has tzgetname, but omit it because it doesn't work
+** in zones with three or more time zone abbreviations.
+** Callers can instead use localtime_rz + strftime.
+*/
+
+#endif
+
+/*
** The easy way to behave "as if no library function calls" localtime
-** is to not call it--so we drop its guts into "localsub", which can be
-** freely called. (And no, the PANS doesn't require the above behavior--
+** is to not call it, so we drop its guts into "localsub", which can be
+** freely called. (And no, the PANS doesn't require the above behavior,
** but it *is* desirable.)
**
-** The unused offset argument is for the benefit of mktime variants.
+** If successful and SETNAME is nonzero,
+** set the applicable parts of tzname, timezone and altzone;
+** however, it's OK to omit this step if the time zone is POSIX-compatible,
+** since in that case tzset should have already done this step correctly.
+** SETNAME's type is intfast32_t for compatibility with gmtsub,
+** but it is actually a boolean and its value should be 0 or 1.
*/
/*ARGSUSED*/
static struct tm *
-localsub(const time_t * const timep, const int_fast32_t offset,
- struct tm * const tmp, struct state * sp) // android-changed: added sp.
+localsub(struct state const *sp, time_t const *timep, int_fast32_t setname,
+ struct tm *const tmp)
{
- register const struct ttinfo * ttisp;
- register int i;
- register struct tm * result;
- const time_t t = *timep;
+ register const struct ttinfo * ttisp;
+ register int i;
+ register struct tm * result;
+ const time_t t = *timep;
- // BEGIN android-changed: support user-supplied sp.
- if (sp == NULL) {
- sp = lclptr;
- }
- // END android-changed
- if (sp == NULL)
- return gmtsub(timep, offset, tmp, sp); // android-changed: added sp.
- if ((sp->goback && t < sp->ats[0]) ||
- (sp->goahead && t > sp->ats[sp->timecnt - 1])) {
- time_t newt = t;
- register time_t seconds;
- register time_t years;
+ if (sp == NULL) {
+ /* Don't bother to set tzname etc.; tzset has already done it. */
+ return gmtsub(gmtptr, timep, 0, tmp);
+ }
+ if ((sp->goback && t < sp->ats[0]) ||
+ (sp->goahead && t > sp->ats[sp->timecnt - 1])) {
+ time_t newt = t;
+ register time_t seconds;
+ register time_t years;
- if (t < sp->ats[0])
- seconds = sp->ats[0] - t;
- else seconds = t - sp->ats[sp->timecnt - 1];
- --seconds;
- years = (seconds / SECSPERREPEAT + 1) * YEARSPERREPEAT;
- seconds = years * AVGSECSPERYEAR;
- if (t < sp->ats[0])
- newt += seconds;
- else newt -= seconds;
- if (newt < sp->ats[0] ||
- newt > sp->ats[sp->timecnt - 1])
- return NULL; /* "cannot happen" */
- result = localsub(&newt, offset, tmp, sp); // android-changed: added sp.
- if (result == tmp) {
- register time_t newy;
+ if (t < sp->ats[0])
+ seconds = sp->ats[0] - t;
+ else seconds = t - sp->ats[sp->timecnt - 1];
+ --seconds;
+ years = (seconds / SECSPERREPEAT + 1) * YEARSPERREPEAT;
+ seconds = years * AVGSECSPERYEAR;
+ if (t < sp->ats[0])
+ newt += seconds;
+ else newt -= seconds;
+ if (newt < sp->ats[0] ||
+ newt > sp->ats[sp->timecnt - 1])
+ return NULL; /* "cannot happen" */
+ result = localsub(sp, &newt, setname, tmp);
+ if (result) {
+ register int_fast64_t newy;
- newy = tmp->tm_year;
- if (t < sp->ats[0])
- newy -= years;
- else newy += years;
- tmp->tm_year = newy;
- if (tmp->tm_year != newy)
- return NULL;
- }
- return result;
- }
- if (sp->timecnt == 0 || t < sp->ats[0]) {
- i = sp->defaulttype;
- } else {
- register int lo = 1;
- register int hi = sp->timecnt;
+ newy = result->tm_year;
+ if (t < sp->ats[0])
+ newy -= years;
+ else newy += years;
+ if (! (INT_MIN <= newy && newy <= INT_MAX))
+ return NULL;
+ result->tm_year = newy;
+ }
+ return result;
+ }
+ if (sp->timecnt == 0 || t < sp->ats[0]) {
+ i = sp->defaulttype;
+ } else {
+ register int lo = 1;
+ register int hi = sp->timecnt;
- while (lo < hi) {
- register int mid = (lo + hi) >> 1;
+ while (lo < hi) {
+ register int mid = (lo + hi) >> 1;
- if (t < sp->ats[mid])
- hi = mid;
- else lo = mid + 1;
- }
- i = (int) sp->types[lo - 1];
- }
- ttisp = &sp->ttis[i];
- /*
- ** To get (wrong) behavior that's compatible with System V Release 2.0
- ** you'd replace the statement below with
- ** t += ttisp->tt_gmtoff;
- ** timesub(&t, 0L, sp, tmp);
- */
- result = timesub(&t, ttisp->tt_gmtoff, sp, tmp);
- tmp->tm_isdst = ttisp->tt_isdst;
- tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind];
+ if (t < sp->ats[mid])
+ hi = mid;
+ else lo = mid + 1;
+ }
+ i = (int) sp->types[lo - 1];
+ }
+ ttisp = &sp->ttis[i];
+ /*
+ ** To get (wrong) behavior that's compatible with System V Release 2.0
+ ** you'd replace the statement below with
+ ** t += ttisp->tt_gmtoff;
+ ** timesub(&t, 0L, sp, tmp);
+ */
+ result = timesub(&t, ttisp->tt_gmtoff, sp, tmp);
+ if (result) {
+ result->tm_isdst = ttisp->tt_isdst;
#ifdef TM_ZONE
- tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
+ result->TM_ZONE = (char *) &sp->chars[ttisp->tt_abbrind];
#endif /* defined TM_ZONE */
- return result;
+ if (setname)
+ update_tzname_etc(sp, ttisp);
+ }
+ return result;
+}
+
+#if NETBSD_INSPIRED
+
+struct tm *
+localtime_rz(struct state *sp, time_t const *timep, struct tm *tmp)
+{
+ return localsub(sp, timep, 0, tmp);
+}
+
+#endif
+
+static struct tm *
+localtime_tzset(time_t const *timep, struct tm *tmp, bool setname)
+{
+ int err = lock();
+ if (err) {
+ errno = err;
+ return NULL;
+ }
+ if (setname || !lcl_is_set)
+ tzset_unlocked();
+ tmp = localsub(lclptr, timep, setname, tmp);
+ unlock();
+ return tmp;
}
struct tm *
-localtime(const time_t * const timep)
+localtime(const time_t *timep)
{
- return localtime_r(timep, &tmGlobal);
+ return localtime_tzset(timep, &tm, true);
}
-/*
-** Re-entrant version of localtime.
-*/
-
struct tm *
-localtime_r(const time_t * const timep, struct tm * tmp)
+localtime_r(const time_t *timep, struct tm *tmp)
{
- struct tm* result;
-
- _tzLock();
- tzset_locked();
- result = localsub(timep, 0L, tmp, NULL); // android-changed: extra parameter.
- _tzUnlock();
-
- return result;
+ return localtime_tzset(timep, tmp, false);
}
/*
@@ -1409,37 +1543,22 @@
*/
static struct tm *
-gmtsub(const time_t * const timep, const int_fast32_t offset,
- struct tm *const tmp, struct state * sp __unused) // android-changed: added sp.
+gmtsub(struct state const *sp, time_t const *timep, int_fast32_t offset,
+ struct tm *tmp)
{
- register struct tm * result;
+ register struct tm * result;
- if (!gmt_is_set) {
-#ifdef ALL_STATE
- gmtptr = malloc(sizeof *gmtptr);
- gmt_is_set = gmtptr != NULL;
-#else
- gmt_is_set = TRUE;
-#endif /* defined ALL_STATE */
- if (gmt_is_set)
- gmtload(gmtptr);
- }
- result = timesub(timep, offset, gmtptr, tmp);
+ result = timesub(timep, offset, gmtptr, tmp);
#ifdef TM_ZONE
- /*
- ** Could get fancy here and deliver something such as
- ** "UT+xxxx" or "UT-xxxx" if offset is non-zero,
- ** but this is no time for a treasure hunt.
- */
- tmp->TM_ZONE = offset ? wildabbr : gmtptr ? gmtptr->chars : gmt;
+ /*
+ ** Could get fancy here and deliver something such as
+ ** "UT+xxxx" or "UT-xxxx" if offset is non-zero,
+ ** but this is no time for a treasure hunt.
+ */
+ tmp->TM_ZONE = ((char *)
+ (offset ? wildabbr : gmtptr ? gmtptr->chars : gmt));
#endif /* defined TM_ZONE */
- return result;
-}
-
-struct tm *
-gmtime(const time_t * const timep)
-{
- return gmtime_r(timep, &tmGlobal);
+ return result;
}
/*
@@ -1447,23 +1566,25 @@
*/
struct tm *
-gmtime_r(const time_t * const timep, struct tm * tmp)
+gmtime_r(const time_t *timep, struct tm *tmp)
{
- struct tm* result;
+ gmtcheck();
+ return gmtsub(gmtptr, timep, 0, tmp);
+}
- _tzLock();
- result = gmtsub(timep, 0L, tmp, NULL); // android-changed: extra parameter.
- _tzUnlock();
-
- return result;
+struct tm *
+gmtime(const time_t *timep)
+{
+ return gmtime_r(timep, &tm);
}
#ifdef STD_INSPIRED
struct tm *
-offtime(const time_t *const timep, const long offset)
+offtime(const time_t *timep, long offset)
{
- return gmtsub(timep, offset, &tmGlobal, NULL); // android-changed: extra parameter.
+ gmtcheck();
+ return gmtsub(gmtptr, timep, offset, &tm);
}
#endif /* defined STD_INSPIRED */
@@ -1473,597 +1594,610 @@
** where, to make the math easy, the answer for year zero is defined as zero.
*/
-static int
+static int ATTRIBUTE_PURE
leaps_thru_end_of(register const int y)
{
- return (y >= 0) ? (y / 4 - y / 100 + y / 400) :
- -(leaps_thru_end_of(-(y + 1)) + 1);
+ return (y >= 0) ? (y / 4 - y / 100 + y / 400) :
+ -(leaps_thru_end_of(-(y + 1)) + 1);
}
static struct tm *
-timesub(const time_t *const timep, const int_fast32_t offset,
- register const struct state *const sp,
- register struct tm *const tmp)
+timesub(const time_t *timep, int_fast32_t offset,
+ const struct state *sp, struct tm *tmp)
{
- register const struct lsinfo * lp;
- register time_t tdays;
- register int idays; /* unsigned would be so 2003 */
- register int_fast64_t rem;
- int y;
- register const int * ip;
- register int_fast64_t corr;
- register int hit;
- register int i;
+ register const struct lsinfo * lp;
+ register time_t tdays;
+ register int idays; /* unsigned would be so 2003 */
+ register int_fast64_t rem;
+ int y;
+ register const int * ip;
+ register int_fast64_t corr;
+ register bool hit;
+ register int i;
- corr = 0;
- hit = 0;
- i = (sp == NULL) ? 0 : sp->leapcnt;
- while (--i >= 0) {
- lp = &sp->lsis[i];
- if (*timep >= lp->ls_trans) {
- if (*timep == lp->ls_trans) {
- hit = ((i == 0 && lp->ls_corr > 0) ||
- lp->ls_corr > sp->lsis[i - 1].ls_corr);
- if (hit)
- while (i > 0 &&
- sp->lsis[i].ls_trans ==
- sp->lsis[i - 1].ls_trans + 1 &&
- sp->lsis[i].ls_corr ==
- sp->lsis[i - 1].ls_corr + 1) {
- ++hit;
- --i;
- }
- }
- corr = lp->ls_corr;
- break;
- }
- }
- y = EPOCH_YEAR;
- tdays = *timep / SECSPERDAY;
- rem = *timep - tdays * SECSPERDAY;
- while (tdays < 0 || tdays >= year_lengths[isleap(y)]) {
- int newy;
- register time_t tdelta;
- register int idelta;
- register int leapdays;
+ corr = 0;
+ hit = false;
+ i = (sp == NULL) ? 0 : sp->leapcnt;
+ while (--i >= 0) {
+ lp = &sp->lsis[i];
+ if (*timep >= lp->ls_trans) {
+ if (*timep == lp->ls_trans) {
+ hit = ((i == 0 && lp->ls_corr > 0) ||
+ lp->ls_corr > sp->lsis[i - 1].ls_corr);
+ if (hit)
+ while (i > 0 &&
+ sp->lsis[i].ls_trans ==
+ sp->lsis[i - 1].ls_trans + 1 &&
+ sp->lsis[i].ls_corr ==
+ sp->lsis[i - 1].ls_corr + 1) {
+ ++hit;
+ --i;
+ }
+ }
+ corr = lp->ls_corr;
+ break;
+ }
+ }
+ y = EPOCH_YEAR;
+ tdays = *timep / SECSPERDAY;
+ rem = *timep % SECSPERDAY;
+ while (tdays < 0 || tdays >= year_lengths[isleap(y)]) {
+ int newy;
+ register time_t tdelta;
+ register int idelta;
+ register int leapdays;
- tdelta = tdays / DAYSPERLYEAR;
- if (! ((! TYPE_SIGNED(time_t) || INT_MIN <= tdelta)
- && tdelta <= INT_MAX))
- return NULL;
- idelta = tdelta;
- if (idelta == 0)
- idelta = (tdays < 0) ? -1 : 1;
- newy = y;
- if (increment_overflow(&newy, idelta))
- return NULL;
- leapdays = leaps_thru_end_of(newy - 1) -
- leaps_thru_end_of(y - 1);
- tdays -= ((time_t) newy - y) * DAYSPERNYEAR;
- tdays -= leapdays;
- y = newy;
- }
- {
- register int_fast32_t seconds;
-
- seconds = tdays * SECSPERDAY;
- tdays = seconds / SECSPERDAY;
- rem += seconds - tdays * SECSPERDAY;
- }
- /*
- ** Given the range, we can now fearlessly cast...
- */
- idays = tdays;
- rem += offset - corr;
- while (rem < 0) {
- rem += SECSPERDAY;
- --idays;
- }
- while (rem >= SECSPERDAY) {
- rem -= SECSPERDAY;
- ++idays;
- }
- while (idays < 0) {
- if (increment_overflow(&y, -1))
- return NULL;
- idays += year_lengths[isleap(y)];
- }
- while (idays >= year_lengths[isleap(y)]) {
- idays -= year_lengths[isleap(y)];
- if (increment_overflow(&y, 1))
- return NULL;
- }
- tmp->tm_year = y;
- if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))
- return NULL;
- tmp->tm_yday = idays;
- /*
- ** The "extra" mods below avoid overflow problems.
- */
- tmp->tm_wday = EPOCH_WDAY +
- ((y - EPOCH_YEAR) % DAYSPERWEEK) *
- (DAYSPERNYEAR % DAYSPERWEEK) +
- leaps_thru_end_of(y - 1) -
- leaps_thru_end_of(EPOCH_YEAR - 1) +
- idays;
- tmp->tm_wday %= DAYSPERWEEK;
- if (tmp->tm_wday < 0)
- tmp->tm_wday += DAYSPERWEEK;
- tmp->tm_hour = (int) (rem / SECSPERHOUR);
- rem %= SECSPERHOUR;
- tmp->tm_min = (int) (rem / SECSPERMIN);
- /*
- ** A positive leap second requires a special
- ** representation. This uses "... ??:59:60" et seq.
- */
- tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
- ip = mon_lengths[isleap(y)];
- for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon))
- idays -= ip[tmp->tm_mon];
- tmp->tm_mday = (int) (idays + 1);
- tmp->tm_isdst = 0;
+ tdelta = tdays / DAYSPERLYEAR;
+ if (! ((! TYPE_SIGNED(time_t) || INT_MIN <= tdelta)
+ && tdelta <= INT_MAX))
+ goto out_of_range;
+ idelta = tdelta;
+ if (idelta == 0)
+ idelta = (tdays < 0) ? -1 : 1;
+ newy = y;
+ if (increment_overflow(&newy, idelta))
+ goto out_of_range;
+ leapdays = leaps_thru_end_of(newy - 1) -
+ leaps_thru_end_of(y - 1);
+ tdays -= ((time_t) newy - y) * DAYSPERNYEAR;
+ tdays -= leapdays;
+ y = newy;
+ }
+ /*
+ ** Given the range, we can now fearlessly cast...
+ */
+ idays = tdays;
+ rem += offset - corr;
+ while (rem < 0) {
+ rem += SECSPERDAY;
+ --idays;
+ }
+ while (rem >= SECSPERDAY) {
+ rem -= SECSPERDAY;
+ ++idays;
+ }
+ while (idays < 0) {
+ if (increment_overflow(&y, -1))
+ goto out_of_range;
+ idays += year_lengths[isleap(y)];
+ }
+ while (idays >= year_lengths[isleap(y)]) {
+ idays -= year_lengths[isleap(y)];
+ if (increment_overflow(&y, 1))
+ goto out_of_range;
+ }
+ tmp->tm_year = y;
+ if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))
+ goto out_of_range;
+ tmp->tm_yday = idays;
+ /*
+ ** The "extra" mods below avoid overflow problems.
+ */
+ tmp->tm_wday = EPOCH_WDAY +
+ ((y - EPOCH_YEAR) % DAYSPERWEEK) *
+ (DAYSPERNYEAR % DAYSPERWEEK) +
+ leaps_thru_end_of(y - 1) -
+ leaps_thru_end_of(EPOCH_YEAR - 1) +
+ idays;
+ tmp->tm_wday %= DAYSPERWEEK;
+ if (tmp->tm_wday < 0)
+ tmp->tm_wday += DAYSPERWEEK;
+ tmp->tm_hour = (int) (rem / SECSPERHOUR);
+ rem %= SECSPERHOUR;
+ tmp->tm_min = (int) (rem / SECSPERMIN);
+ /*
+ ** A positive leap second requires a special
+ ** representation. This uses "... ??:59:60" et seq.
+ */
+ tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
+ ip = mon_lengths[isleap(y)];
+ for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon))
+ idays -= ip[tmp->tm_mon];
+ tmp->tm_mday = (int) (idays + 1);
+ tmp->tm_isdst = 0;
#ifdef TM_GMTOFF
- tmp->TM_GMTOFF = offset;
+ tmp->TM_GMTOFF = offset;
#endif /* defined TM_GMTOFF */
- return tmp;
+ return tmp;
+
+ out_of_range:
+ errno = EOVERFLOW;
+ return NULL;
}
char *
-ctime(const time_t * const timep)
+ctime(const time_t *timep)
{
/*
** Section 4.12.3.2 of X3.159-1989 requires that
-** The ctime function converts the calendar time pointed to by timer
-** to local time in the form of a string. It is equivalent to
-** asctime(localtime(timer))
+** The ctime function converts the calendar time pointed to by timer
+** to local time in the form of a string. It is equivalent to
+** asctime(localtime(timer))
*/
- return asctime(localtime(timep));
+ struct tm *tmp = localtime(timep);
+ return tmp ? asctime(tmp) : NULL;
}
char *
-ctime_r(const time_t * const timep, char * buf)
+ctime_r(const time_t *timep, char *buf)
{
- struct tm mytm;
-
- return asctime_r(localtime_r(timep, &mytm), buf);
+ struct tm mytm;
+ struct tm *tmp = localtime_r(timep, &mytm);
+ return tmp ? asctime_r(tmp, buf) : NULL;
}
/*
** Adapted from code provided by Robert Elz, who writes:
-** The "best" way to do mktime I think is based on an idea of Bob
-** Kridle's (so its said...) from a long time ago.
-** It does a binary search of the time_t space. Since time_t's are
-** just 32 bits, its a max of 32 iterations (even at 64 bits it
-** would still be very reasonable).
+** The "best" way to do mktime I think is based on an idea of Bob
+** Kridle's (so its said...) from a long time ago.
+** It does a binary search of the time_t space. Since time_t's are
+** just 32 bits, its a max of 32 iterations (even at 64 bits it
+** would still be very reasonable).
*/
#ifndef WRONG
-#define WRONG (-1)
+#define WRONG (-1)
#endif /* !defined WRONG */
/*
** Normalize logic courtesy Paul Eggert.
*/
-static int
-increment_overflow(int *const ip, int j)
+static bool
+increment_overflow(int *ip, int j)
{
- register int const i = *ip;
+ register int const i = *ip;
- /*
- ** If i >= 0 there can only be overflow if i + j > INT_MAX
- ** or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow.
- ** If i < 0 there can only be overflow if i + j < INT_MIN
- ** or if j < INT_MIN - i; given i < 0, INT_MIN - i cannot overflow.
- */
- if ((i >= 0) ? (j > INT_MAX - i) : (j < INT_MIN - i))
- return TRUE;
- *ip += j;
- return FALSE;
+ /*
+ ** If i >= 0 there can only be overflow if i + j > INT_MAX
+ ** or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow.
+ ** If i < 0 there can only be overflow if i + j < INT_MIN
+ ** or if j < INT_MIN - i; given i < 0, INT_MIN - i cannot overflow.
+ */
+ if ((i >= 0) ? (j > INT_MAX - i) : (j < INT_MIN - i))
+ return true;
+ *ip += j;
+ return false;
}
-static int
+static bool
increment_overflow32(int_fast32_t *const lp, int const m)
{
- register int_fast32_t const l = *lp;
+ register int_fast32_t const l = *lp;
- if ((l >= 0) ? (m > INT_FAST32_MAX - l) : (m < INT_FAST32_MIN - l))
- return TRUE;
- *lp += m;
- return FALSE;
+ if ((l >= 0) ? (m > INT_FAST32_MAX - l) : (m < INT_FAST32_MIN - l))
+ return true;
+ *lp += m;
+ return false;
}
-static int
+static bool
increment_overflow_time(time_t *tp, int_fast32_t j)
{
- /*
- ** This is like
- ** 'if (! (time_t_min <= *tp + j && *tp + j <= time_t_max)) ...',
- ** except that it does the right thing even if *tp + j would overflow.
- */
- if (! (j < 0
- ? (TYPE_SIGNED(time_t) ? time_t_min - j <= *tp : -1 - j < *tp)
- : *tp <= time_t_max - j))
- return TRUE;
- *tp += j;
- return FALSE;
+ /*
+ ** This is like
+ ** 'if (! (time_t_min <= *tp + j && *tp + j <= time_t_max)) ...',
+ ** except that it does the right thing even if *tp + j would overflow.
+ */
+ if (! (j < 0
+ ? (TYPE_SIGNED(time_t) ? time_t_min - j <= *tp : -1 - j < *tp)
+ : *tp <= time_t_max - j))
+ return true;
+ *tp += j;
+ return false;
}
-static int
+static bool
normalize_overflow(int *const tensptr, int *const unitsptr, const int base)
{
- register int tensdelta;
+ register int tensdelta;
- tensdelta = (*unitsptr >= 0) ?
- (*unitsptr / base) :
- (-1 - (-1 - *unitsptr) / base);
- *unitsptr -= tensdelta * base;
- return increment_overflow(tensptr, tensdelta);
+ tensdelta = (*unitsptr >= 0) ?
+ (*unitsptr / base) :
+ (-1 - (-1 - *unitsptr) / base);
+ *unitsptr -= tensdelta * base;
+ return increment_overflow(tensptr, tensdelta);
+}
+
+static bool
+normalize_overflow32(int_fast32_t *tensptr, int *unitsptr, int base)
+{
+ register int tensdelta;
+
+ tensdelta = (*unitsptr >= 0) ?
+ (*unitsptr / base) :
+ (-1 - (-1 - *unitsptr) / base);
+ *unitsptr -= tensdelta * base;
+ return increment_overflow32(tensptr, tensdelta);
}
static int
-normalize_overflow32(int_fast32_t *const tensptr, int *const unitsptr,
- const int base)
+tmcomp(register const struct tm *const atmp,
+ register const struct tm *const btmp)
{
- register int tensdelta;
+ register int result;
- tensdelta = (*unitsptr >= 0) ?
- (*unitsptr / base) :
- (-1 - (-1 - *unitsptr) / base);
- *unitsptr -= tensdelta * base;
- return increment_overflow32(tensptr, tensdelta);
-}
-
-static int
-tmcomp(register const struct tm * const atmp,
- register const struct tm * const btmp)
-{
- register int result;
-
- if (atmp->tm_year != btmp->tm_year)
- return atmp->tm_year < btmp->tm_year ? -1 : 1;
- if ((result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
- (result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
- (result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
- (result = (atmp->tm_min - btmp->tm_min)) == 0)
- result = atmp->tm_sec - btmp->tm_sec;
- return result;
+ if (atmp->tm_year != btmp->tm_year)
+ return atmp->tm_year < btmp->tm_year ? -1 : 1;
+ if ((result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
+ (result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
+ (result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
+ (result = (atmp->tm_min - btmp->tm_min)) == 0)
+ result = atmp->tm_sec - btmp->tm_sec;
+ return result;
}
static time_t
-time2sub(struct tm * const tmp,
- struct tm *(*const funcp)(const time_t*, int_fast32_t, struct tm*, struct state*),
- const int_fast32_t offset,
- int * const okayp,
- const int do_norm_secs, struct state * sp) // android-changed: added sp
+time2sub(struct tm *const tmp,
+ struct tm *(*funcp)(struct state const *, time_t const *,
+ int_fast32_t, struct tm *),
+ struct state const *sp,
+ const int_fast32_t offset,
+ bool *okayp,
+ bool do_norm_secs)
{
- register int dir;
- register int i, j;
- register int saved_seconds;
- register int_fast32_t li;
- register time_t lo;
- register time_t hi;
- int_fast32_t y;
- time_t newt;
- time_t t;
- struct tm yourtm, mytm;
+ register int dir;
+ register int i, j;
+ register int saved_seconds;
+ register int_fast32_t li;
+ register time_t lo;
+ register time_t hi;
+ int_fast32_t y;
+ time_t newt;
+ time_t t;
+ struct tm yourtm, mytm;
- *okayp = FALSE;
- yourtm = *tmp;
- if (do_norm_secs) {
- if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
- SECSPERMIN))
- return WRONG;
- }
- if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
- return WRONG;
- if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
- return WRONG;
- y = yourtm.tm_year;
- if (normalize_overflow32(&y, &yourtm.tm_mon, MONSPERYEAR))
- return WRONG;
- /*
- ** Turn y into an actual year number for now.
- ** It is converted back to an offset from TM_YEAR_BASE later.
- */
- if (increment_overflow32(&y, TM_YEAR_BASE))
- return WRONG;
- while (yourtm.tm_mday <= 0) {
- if (increment_overflow32(&y, -1))
- return WRONG;
- li = y + (1 < yourtm.tm_mon);
- yourtm.tm_mday += year_lengths[isleap(li)];
- }
- while (yourtm.tm_mday > DAYSPERLYEAR) {
- li = y + (1 < yourtm.tm_mon);
- yourtm.tm_mday -= year_lengths[isleap(li)];
- if (increment_overflow32(&y, 1))
- return WRONG;
- }
- for ( ; ; ) {
- i = mon_lengths[isleap(y)][yourtm.tm_mon];
- if (yourtm.tm_mday <= i)
- break;
- yourtm.tm_mday -= i;
- if (++yourtm.tm_mon >= MONSPERYEAR) {
- yourtm.tm_mon = 0;
- if (increment_overflow32(&y, 1))
- return WRONG;
- }
- }
- if (increment_overflow32(&y, -TM_YEAR_BASE))
- return WRONG;
- yourtm.tm_year = y;
- if (yourtm.tm_year != y)
- return WRONG;
- if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
- saved_seconds = 0;
- else if (y + TM_YEAR_BASE < EPOCH_YEAR) {
- /*
- ** We can't set tm_sec to 0, because that might push the
- ** time below the minimum representable time.
- ** Set tm_sec to 59 instead.
- ** This assumes that the minimum representable time is
- ** not in the same minute that a leap second was deleted from,
- ** which is a safer assumption than using 58 would be.
- */
- if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
- return WRONG;
- saved_seconds = yourtm.tm_sec;
- yourtm.tm_sec = SECSPERMIN - 1;
- } else {
- saved_seconds = yourtm.tm_sec;
- yourtm.tm_sec = 0;
- }
- /*
- ** Do a binary search (this works whatever time_t's type is).
- */
- if (!TYPE_SIGNED(time_t)) {
- lo = 0;
- hi = lo - 1;
- } else {
- lo = 1;
- for (i = 0; i < (int) TYPE_BIT(time_t) - 1; ++i)
- lo *= 2;
- hi = -(lo + 1);
- }
- for ( ; ; ) {
- t = lo / 2 + hi / 2;
- if (t < lo)
- t = lo;
- else if (t > hi)
- t = hi;
- if ((*funcp)(&t, offset, &mytm, sp) == NULL) { // android-changed: added sp.
- /*
- ** Assume that t is too extreme to be represented in
- ** a struct tm; arrange things so that it is less
- ** extreme on the next pass.
- */
- dir = (t > 0) ? 1 : -1;
- } else dir = tmcomp(&mytm, &yourtm);
- if (dir != 0) {
- if (t == lo) {
- if (t == time_t_max)
- return WRONG;
- ++t;
- ++lo;
- } else if (t == hi) {
- if (t == time_t_min)
- return WRONG;
- --t;
- --hi;
- }
- if (lo > hi)
- return WRONG;
- if (dir > 0)
- hi = t;
- else lo = t;
- continue;
- }
- if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
- break;
- /*
- ** Right time, wrong type.
- ** Hunt for right time, right type.
- ** It's okay to guess wrong since the guess
- ** gets checked.
- */
- // BEGIN android-changed: support user-supplied sp
- if (sp == NULL) {
- sp = (struct state *)
- ((funcp == localsub) ? lclptr : gmtptr);
- }
- // END android-changed
- if (sp == NULL)
- return WRONG;
- for (i = sp->typecnt - 1; i >= 0; --i) {
- if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
- continue;
- for (j = sp->typecnt - 1; j >= 0; --j) {
- if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
- continue;
- newt = t + sp->ttis[j].tt_gmtoff -
- sp->ttis[i].tt_gmtoff;
- if ((*funcp)(&newt, offset, &mytm, sp) == NULL) // android-changed: added sp.
- continue;
- if (tmcomp(&mytm, &yourtm) != 0)
- continue;
- if (mytm.tm_isdst != yourtm.tm_isdst)
- continue;
- /*
- ** We have a match.
- */
- t = newt;
- goto label;
- }
- }
- return WRONG;
- }
+ *okayp = false;
+ yourtm = *tmp;
+ if (do_norm_secs) {
+ if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
+ SECSPERMIN))
+ return WRONG;
+ }
+ if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
+ return WRONG;
+ if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
+ return WRONG;
+ y = yourtm.tm_year;
+ if (normalize_overflow32(&y, &yourtm.tm_mon, MONSPERYEAR))
+ return WRONG;
+ /*
+ ** Turn y into an actual year number for now.
+ ** It is converted back to an offset from TM_YEAR_BASE later.
+ */
+ if (increment_overflow32(&y, TM_YEAR_BASE))
+ return WRONG;
+ while (yourtm.tm_mday <= 0) {
+ if (increment_overflow32(&y, -1))
+ return WRONG;
+ li = y + (1 < yourtm.tm_mon);
+ yourtm.tm_mday += year_lengths[isleap(li)];
+ }
+ while (yourtm.tm_mday > DAYSPERLYEAR) {
+ li = y + (1 < yourtm.tm_mon);
+ yourtm.tm_mday -= year_lengths[isleap(li)];
+ if (increment_overflow32(&y, 1))
+ return WRONG;
+ }
+ for ( ; ; ) {
+ i = mon_lengths[isleap(y)][yourtm.tm_mon];
+ if (yourtm.tm_mday <= i)
+ break;
+ yourtm.tm_mday -= i;
+ if (++yourtm.tm_mon >= MONSPERYEAR) {
+ yourtm.tm_mon = 0;
+ if (increment_overflow32(&y, 1))
+ return WRONG;
+ }
+ }
+ if (increment_overflow32(&y, -TM_YEAR_BASE))
+ return WRONG;
+ if (! (INT_MIN <= y && y <= INT_MAX))
+ return WRONG;
+ yourtm.tm_year = y;
+ if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
+ saved_seconds = 0;
+ else if (y + TM_YEAR_BASE < EPOCH_YEAR) {
+ /*
+ ** We can't set tm_sec to 0, because that might push the
+ ** time below the minimum representable time.
+ ** Set tm_sec to 59 instead.
+ ** This assumes that the minimum representable time is
+ ** not in the same minute that a leap second was deleted from,
+ ** which is a safer assumption than using 58 would be.
+ */
+ if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
+ return WRONG;
+ saved_seconds = yourtm.tm_sec;
+ yourtm.tm_sec = SECSPERMIN - 1;
+ } else {
+ saved_seconds = yourtm.tm_sec;
+ yourtm.tm_sec = 0;
+ }
+ /*
+ ** Do a binary search (this works whatever time_t's type is).
+ */
+ lo = time_t_min;
+ hi = time_t_max;
+ for ( ; ; ) {
+ t = lo / 2 + hi / 2;
+ if (t < lo)
+ t = lo;
+ else if (t > hi)
+ t = hi;
+ if (! funcp(sp, &t, offset, &mytm)) {
+ /*
+ ** Assume that t is too extreme to be represented in
+ ** a struct tm; arrange things so that it is less
+ ** extreme on the next pass.
+ */
+ dir = (t > 0) ? 1 : -1;
+ } else dir = tmcomp(&mytm, &yourtm);
+ if (dir != 0) {
+ if (t == lo) {
+ if (t == time_t_max)
+ return WRONG;
+ ++t;
+ ++lo;
+ } else if (t == hi) {
+ if (t == time_t_min)
+ return WRONG;
+ --t;
+ --hi;
+ }
+ if (lo > hi)
+ return WRONG;
+ if (dir > 0)
+ hi = t;
+ else lo = t;
+ continue;
+ }
+#if defined TM_GMTOFF && ! UNINIT_TRAP
+ if (mytm.TM_GMTOFF != yourtm.TM_GMTOFF
+ && (yourtm.TM_GMTOFF < 0
+ ? (-SECSPERDAY <= yourtm.TM_GMTOFF
+ && (mytm.TM_GMTOFF <=
+ (SMALLEST (INT_FAST32_MAX, LONG_MAX)
+ + yourtm.TM_GMTOFF)))
+ : (yourtm.TM_GMTOFF <= SECSPERDAY
+ && ((BIGGEST (INT_FAST32_MIN, LONG_MIN)
+ + yourtm.TM_GMTOFF)
+ <= mytm.TM_GMTOFF)))) {
+ /* MYTM matches YOURTM except with the wrong UTC offset.
+ YOURTM.TM_GMTOFF is plausible, so try it instead.
+ It's OK if YOURTM.TM_GMTOFF contains uninitialized data,
+ since the guess gets checked. */
+ time_t altt = t;
+ int_fast32_t diff = mytm.TM_GMTOFF - yourtm.TM_GMTOFF;
+ if (!increment_overflow_time(&altt, diff)) {
+ struct tm alttm;
+ if (funcp(sp, &altt, offset, &alttm)
+ && alttm.tm_isdst == mytm.tm_isdst
+ && alttm.TM_GMTOFF == yourtm.TM_GMTOFF
+ && tmcomp(&alttm, &yourtm) == 0) {
+ t = altt;
+ mytm = alttm;
+ }
+ }
+ }
+#endif
+ if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
+ break;
+ /*
+ ** Right time, wrong type.
+ ** Hunt for right time, right type.
+ ** It's okay to guess wrong since the guess
+ ** gets checked.
+ */
+ if (sp == NULL)
+ return WRONG;
+ for (i = sp->typecnt - 1; i >= 0; --i) {
+ if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
+ continue;
+ for (j = sp->typecnt - 1; j >= 0; --j) {
+ if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
+ continue;
+ newt = t + sp->ttis[j].tt_gmtoff -
+ sp->ttis[i].tt_gmtoff;
+ if (! funcp(sp, &newt, offset, &mytm))
+ continue;
+ if (tmcomp(&mytm, &yourtm) != 0)
+ continue;
+ if (mytm.tm_isdst != yourtm.tm_isdst)
+ continue;
+ /*
+ ** We have a match.
+ */
+ t = newt;
+ goto label;
+ }
+ }
+ return WRONG;
+ }
label:
- newt = t + saved_seconds;
- if ((newt < t) != (saved_seconds < 0))
- return WRONG;
- t = newt;
- if ((*funcp)(&t, offset, tmp, sp)) // android-changed: added sp.
- *okayp = TRUE;
- return t;
+ newt = t + saved_seconds;
+ if ((newt < t) != (saved_seconds < 0))
+ return WRONG;
+ t = newt;
+ if (funcp(sp, &t, offset, tmp))
+ *okayp = true;
+ return t;
}
static time_t
-time2(struct tm * const tmp,
- struct tm * (*const funcp)(const time_t *, int_fast32_t, struct tm *, struct state *), // android-changed: added sp.
+time2(struct tm * const tmp,
+ struct tm *(*funcp)(struct state const *, time_t const *,
+ int_fast32_t, struct tm *),
+ struct state const *sp,
const int_fast32_t offset,
- int *const okayp, struct state* sp) // android-changed: added sp.
+ bool *okayp)
{
- time_t t;
+ time_t t;
- /*
- ** First try without normalization of seconds
- ** (in case tm_sec contains a value associated with a leap second).
- ** If that fails, try with normalization of seconds.
- */
- t = time2sub(tmp, funcp, offset, okayp, FALSE, sp);
- return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE, sp);
+ /*
+ ** First try without normalization of seconds
+ ** (in case tm_sec contains a value associated with a leap second).
+ ** If that fails, try with normalization of seconds.
+ */
+ t = time2sub(tmp, funcp, sp, offset, okayp, false);
+ return *okayp ? t : time2sub(tmp, funcp, sp, offset, okayp, true);
}
static time_t
-time1(struct tm * const tmp,
- struct tm * (* const funcp) (const time_t *, int_fast32_t, struct tm *, struct state *), // android-changed: added sp.
- const int_fast32_t offset, struct state * sp) // android-changed: added sp.
+time1(struct tm *const tmp,
+ struct tm *(*funcp) (struct state const *, time_t const *,
+ int_fast32_t, struct tm *),
+ struct state const *sp,
+ const int_fast32_t offset)
{
- register time_t t;
- register int samei, otheri;
- register int sameind, otherind;
- register int i;
- register int nseen;
- char seen[TZ_MAX_TYPES];
- unsigned char types[TZ_MAX_TYPES];
- int okay;
+ register time_t t;
+ register int samei, otheri;
+ register int sameind, otherind;
+ register int i;
+ register int nseen;
+ char seen[TZ_MAX_TYPES];
+ unsigned char types[TZ_MAX_TYPES];
+ bool okay;
- if (tmp == NULL) {
- errno = EINVAL;
- return WRONG;
- }
- if (tmp->tm_isdst > 1)
- tmp->tm_isdst = 1;
- t = time2(tmp, funcp, offset, &okay, sp); // android-changed: added sp.
- if (okay)
- return t;
- if (tmp->tm_isdst < 0)
+ if (tmp == NULL) {
+ errno = EINVAL;
+ return WRONG;
+ }
+ if (tmp->tm_isdst > 1)
+ tmp->tm_isdst = 1;
+ t = time2(tmp, funcp, sp, offset, &okay);
+ if (okay)
+ return t;
+ if (tmp->tm_isdst < 0)
#ifdef PCTS
- /*
- ** POSIX Conformance Test Suite code courtesy Grant Sullivan.
- */
- tmp->tm_isdst = 0; /* reset to std and try again */
+ /*
+ ** POSIX Conformance Test Suite code courtesy Grant Sullivan.
+ */
+ tmp->tm_isdst = 0; /* reset to std and try again */
#else
- return t;
+ return t;
#endif /* !defined PCTS */
- /*
- ** We're supposed to assume that somebody took a time of one type
- ** and did some math on it that yielded a "struct tm" that's bad.
- ** We try to divine the type they started from and adjust to the
- ** type they need.
- */
- // BEGIN android-changed: support user-supplied sp.
- if (sp == NULL) {
- sp = (struct state *) ((funcp == localsub) ? lclptr : gmtptr);
- }
- // BEGIN android-changed
- if (sp == NULL)
- return WRONG;
- for (i = 0; i < sp->typecnt; ++i)
- seen[i] = FALSE;
- nseen = 0;
- for (i = sp->timecnt - 1; i >= 0; --i)
- if (!seen[sp->types[i]]) {
- seen[sp->types[i]] = TRUE;
- types[nseen++] = sp->types[i];
- }
- for (sameind = 0; sameind < nseen; ++sameind) {
- samei = types[sameind];
- if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
- continue;
- for (otherind = 0; otherind < nseen; ++otherind) {
- otheri = types[otherind];
- if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst)
- continue;
- tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
- sp->ttis[samei].tt_gmtoff;
- tmp->tm_isdst = !tmp->tm_isdst;
- t = time2(tmp, funcp, offset, &okay, sp); // android-changed: added sp.
- if (okay)
- return t;
- tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
- sp->ttis[samei].tt_gmtoff;
- tmp->tm_isdst = !tmp->tm_isdst;
- }
- }
- return WRONG;
+ /*
+ ** We're supposed to assume that somebody took a time of one type
+ ** and did some math on it that yielded a "struct tm" that's bad.
+ ** We try to divine the type they started from and adjust to the
+ ** type they need.
+ */
+ if (sp == NULL)
+ return WRONG;
+ for (i = 0; i < sp->typecnt; ++i)
+ seen[i] = false;
+ nseen = 0;
+ for (i = sp->timecnt - 1; i >= 0; --i)
+ if (!seen[sp->types[i]]) {
+ seen[sp->types[i]] = true;
+ types[nseen++] = sp->types[i];
+ }
+ for (sameind = 0; sameind < nseen; ++sameind) {
+ samei = types[sameind];
+ if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
+ continue;
+ for (otherind = 0; otherind < nseen; ++otherind) {
+ otheri = types[otherind];
+ if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst)
+ continue;
+ tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
+ sp->ttis[samei].tt_gmtoff;
+ tmp->tm_isdst = !tmp->tm_isdst;
+ t = time2(tmp, funcp, sp, offset, &okay);
+ if (okay)
+ return t;
+ tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
+ sp->ttis[samei].tt_gmtoff;
+ tmp->tm_isdst = !tmp->tm_isdst;
+ }
+ }
+ return WRONG;
}
+static time_t
+mktime_tzname(struct state *sp, struct tm *tmp, bool setname)
+{
+ if (sp)
+ return time1(tmp, localsub, sp, setname);
+ else {
+ gmtcheck();
+ return time1(tmp, gmtsub, gmtptr, 0);
+ }
+}
+
+#if NETBSD_INSPIRED
+
time_t
-mktime(struct tm * const tmp)
+mktime_z(struct state *sp, struct tm *tmp)
{
- _tzLock();
- tzset_locked();
- time_t result = time1(tmp, localsub, 0L, NULL); // android-changed: extra parameter.
- _tzUnlock();
- return result;
+ return mktime_tzname(sp, tmp, false);
+}
+
+#endif
+
+time_t
+mktime(struct tm *tmp)
+{
+ time_t t;
+ int err = lock();
+ if (err) {
+ errno = err;
+ return -1;
+ }
+ tzset_unlocked();
+ t = mktime_tzname(lclptr, tmp, true);
+ unlock();
+ return t;
}
#ifdef STD_INSPIRED
time_t
-timelocal(struct tm * const tmp)
+timelocal(struct tm *tmp)
{
- if (tmp != NULL)
- tmp->tm_isdst = -1; /* in case it wasn't initialized */
- return mktime(tmp);
+ if (tmp != NULL)
+ tmp->tm_isdst = -1; /* in case it wasn't initialized */
+ return mktime(tmp);
}
time_t
-timegm(struct tm * const tmp)
+timegm(struct tm *tmp)
{
- time_t result;
-
- if (tmp != NULL)
- tmp->tm_isdst = 0;
- _tzLock();
- result = time1(tmp, gmtsub, 0L, NULL); // android-changed: extra parameter.
- _tzUnlock();
-
- return result;
+ return timeoff(tmp, 0);
}
time_t
-timeoff(struct tm *const tmp, const long offset)
+timeoff(struct tm *tmp, long offset)
{
- if (tmp != NULL)
- tmp->tm_isdst = 0;
- return time1(tmp, gmtsub, offset, NULL); // android-changed: extra parameter.
+ if (tmp)
+ tmp->tm_isdst = 0;
+ gmtcheck();
+ return time1(tmp, gmtsub, gmtptr, offset);
}
#endif /* defined STD_INSPIRED */
-#ifdef CMUCS
-
-/*
-** The following is supplied for compatibility with
-** previous versions of the CMUCS runtime library.
-*/
-
-long
-gtime(struct tm * const tmp)
-{
- const time_t t = mktime(tmp);
-
- if (t == WRONG)
- return -1;
- return t;
-}
-
-#endif /* defined CMUCS */
-
/*
** XXX--is the below the right way to conditionalize??
*/
@@ -2079,64 +2213,105 @@
*/
static int_fast64_t
-leapcorr(time_t * timep)
+leapcorr(struct state const *sp, time_t t)
{
- register struct state * sp;
- register struct lsinfo * lp;
- register int i;
+ register struct lsinfo const * lp;
+ register int i;
- sp = lclptr;
- i = sp->leapcnt;
- while (--i >= 0) {
- lp = &sp->lsis[i];
- if (*timep >= lp->ls_trans)
- return lp->ls_corr;
- }
- return 0;
+ i = sp->leapcnt;
+ while (--i >= 0) {
+ lp = &sp->lsis[i];
+ if (t >= lp->ls_trans)
+ return lp->ls_corr;
+ }
+ return 0;
+}
+
+NETBSD_INSPIRED_EXTERN time_t ATTRIBUTE_PURE
+time2posix_z(struct state *sp, time_t t)
+{
+ return t - leapcorr(sp, t);
}
time_t
time2posix(time_t t)
{
- tzset();
- return t - leapcorr(&t);
+ int err = lock();
+ if (err) {
+ errno = err;
+ return -1;
+ }
+ if (!lcl_is_set)
+ tzset_unlocked();
+ if (lclptr)
+ t = time2posix_z(lclptr, t);
+ unlock();
+ return t;
+}
+
+NETBSD_INSPIRED_EXTERN time_t ATTRIBUTE_PURE
+posix2time_z(struct state *sp, time_t t)
+{
+ time_t x;
+ time_t y;
+ /*
+ ** For a positive leap second hit, the result
+ ** is not unique. For a negative leap second
+ ** hit, the corresponding time doesn't exist,
+ ** so we return an adjacent second.
+ */
+ x = t + leapcorr(sp, t);
+ y = x - leapcorr(sp, x);
+ if (y < t) {
+ do {
+ x++;
+ y = x - leapcorr(sp, x);
+ } while (y < t);
+ x -= y != t;
+ } else if (y > t) {
+ do {
+ --x;
+ y = x - leapcorr(sp, x);
+ } while (y > t);
+ x += y != t;
+ }
+ return x;
}
time_t
posix2time(time_t t)
{
- time_t x;
- time_t y;
-
- tzset();
- /*
- ** For a positive leap second hit, the result
- ** is not unique. For a negative leap second
- ** hit, the corresponding time doesn't exist,
- ** so we return an adjacent second.
- */
- x = t + leapcorr(&t);
- y = x - leapcorr(&x);
- if (y < t) {
- do {
- x++;
- y = x - leapcorr(&x);
- } while (y < t);
- if (t != y)
- return x - 1;
- } else if (y > t) {
- do {
- --x;
- y = x - leapcorr(&x);
- } while (y > t);
- if (t != y)
- return x + 1;
- }
- return x;
+ int err = lock();
+ if (err) {
+ errno = err;
+ return -1;
+ }
+ if (!lcl_is_set)
+ tzset_unlocked();
+ if (lclptr)
+ t = posix2time_z(lclptr, t);
+ unlock();
+ return t;
}
#endif /* defined STD_INSPIRED */
+#ifdef time_tz
+
+/* Convert from the underlying system's time_t to the ersatz time_tz,
+ which is called 'time_t' in this file. */
+
+time_t
+time(time_t *p)
+{
+ time_t r = sys_time(0);
+ if (p)
+ *p = r;
+ return r;
+}
+
+#endif
+
// BEGIN android-added
#include <assert.h>
@@ -2144,7 +2319,7 @@
#include <arpa/inet.h> // For ntohl(3).
static int __bionic_open_tzdata_path(const char* path_prefix_variable, const char* path_suffix,
- const char* olson_id, int* data_size) {
+ const char* olson_id) {
const char* path_prefix = getenv(path_prefix_variable);
if (path_prefix == NULL) {
fprintf(stderr, "%s: %s not set!\n", __FUNCTION__, path_prefix_variable);
@@ -2159,7 +2334,6 @@
snprintf(path, path_length, "%s/%s", path_prefix, path_suffix);
int fd = TEMP_FAILURE_RETRY(open(path, OPEN_MODE));
if (fd == -1) {
- XLOG(("%s: could not open \"%s\": %s\n", __FUNCTION__, path, strerror(errno)));
free(path);
return -2; // Distinguish failure to find any data from failure to find a specific id.
}
@@ -2243,7 +2417,6 @@
if (strcmp(this_id, olson_id) == 0) {
specific_zone_offset = ntohl(entry->start) + ntohl(header.data_offset);
- *data_size = ntohl(entry->length);
break;
}
@@ -2252,7 +2425,6 @@
free(index);
if (specific_zone_offset == -1) {
- XLOG(("%s: couldn't find zone \"%s\"\n", __FUNCTION__, olson_id));
free(path);
close(fd);
return -1;
@@ -2272,10 +2444,10 @@
return fd;
}
-static int __bionic_open_tzdata(const char* olson_id, int* data_size) {
- int fd = __bionic_open_tzdata_path("ANDROID_DATA", "/misc/zoneinfo/current/tzdata", olson_id, data_size);
+static int __bionic_open_tzdata(const char* olson_id) {
+ int fd = __bionic_open_tzdata_path("ANDROID_DATA", "/misc/zoneinfo/current/tzdata", olson_id);
if (fd < 0) {
- fd = __bionic_open_tzdata_path("ANDROID_ROOT", "/usr/share/zoneinfo/tzdata", olson_id, data_size);
+ fd = __bionic_open_tzdata_path("ANDROID_ROOT", "/usr/share/zoneinfo/tzdata", olson_id);
if (fd == -2) {
// The first thing that 'recovery' does is try to format the current time. It doesn't have
// any tzdata available, so we must not abort here --- doing so breaks the recovery image!
@@ -2287,7 +2459,7 @@
// Caches the most recent timezone (http://b/8270865).
static int __bionic_tzload_cached(const char* name, struct state* const sp, const int doextend) {
- _tzLock();
+ lock();
// Our single-item cache.
static char* g_cached_time_zone_name;
@@ -2296,7 +2468,7 @@
// Do we already have this timezone cached?
if (g_cached_time_zone_name != NULL && strcmp(name, g_cached_time_zone_name) == 0) {
*sp = g_cached_time_zone;
- _tzUnlock();
+ unlock();
return 0;
}
@@ -2309,7 +2481,7 @@
g_cached_time_zone = *sp;
}
- _tzUnlock();
+ unlock();
return rc;
}
@@ -2321,12 +2493,12 @@
if (st == NULL)
return 0;
- if (__bionic_tzload_cached(tz, st, TRUE) != 0) {
+ if (__bionic_tzload_cached(tz, st, true) != 0) {
// TODO: not sure what's best here, but for now, we fall back to gmt.
gmtload(st);
}
- return_value = time1(tmp, localsub, 0L, st);
+ return_value = time1(tmp, localsub, st, 0L);
free(st);
return return_value;
}
diff --git a/libc/tzcode/private.h b/libc/tzcode/private.h
index 494e142..1c176e6 100644
--- a/libc/tzcode/private.h
+++ b/libc/tzcode/private.h
@@ -19,13 +19,9 @@
/*
** Defaults for preprocessor symbols.
-** You can override these in your C compiler options, e.g. '-DHAVE_ADJTIME=0'.
+** You can override these in your C compiler options, e.g. '-DHAVE_GETTEXT=1'.
*/
-#ifndef HAVE_ADJTIME
-#define HAVE_ADJTIME 1
-#endif /* !defined HAVE_ADJTIME */
-
#ifndef HAVE_GETTEXT
#define HAVE_GETTEXT 0
#endif /* !defined HAVE_GETTEXT */
@@ -38,9 +34,9 @@
#define HAVE_LINK 1
#endif /* !defined HAVE_LINK */
-#ifndef HAVE_SETTIMEOFDAY
-#define HAVE_SETTIMEOFDAY 3
-#endif /* !defined HAVE_SETTIMEOFDAY */
+#ifndef HAVE_STRDUP
+#define HAVE_STRDUP 1
+#endif
#ifndef HAVE_SYMLINK
#define HAVE_SYMLINK 1
@@ -59,32 +55,61 @@
#endif /* !defined HAVE_UNISTD_H */
#ifndef HAVE_UTMPX_H
-#define HAVE_UTMPX_H 0
+#define HAVE_UTMPX_H 1
#endif /* !defined HAVE_UTMPX_H */
-#if !defined(__ANDROID__)
-#ifndef LOCALE_HOME
-#define LOCALE_HOME "/usr/lib/locale"
-#endif /* !defined LOCALE_HOME */
-#endif // __ANDROID__
+#ifndef NETBSD_INSPIRED
+# define NETBSD_INSPIRED 1
+#endif
#if HAVE_INCOMPATIBLE_CTIME_R
#define asctime_r _incompatible_asctime_r
#define ctime_r _incompatible_ctime_r
#endif /* HAVE_INCOMPATIBLE_CTIME_R */
+/* Enable tm_gmtoff and tm_zone on GNUish systems. */
+#define _GNU_SOURCE 1
+/* Fix asctime_r on Solaris 10. */
+#define _POSIX_PTHREAD_SEMANTICS 1
+/* Enable strtoimax on Solaris 10. */
+#define __EXTENSIONS__ 1
+
/*
** Nested includes
*/
+/* Avoid clashes with NetBSD by renaming NetBSD's declarations. */
+#define localtime_rz sys_localtime_rz
+#define mktime_z sys_mktime_z
+#define posix2time_z sys_posix2time_z
+#define time2posix_z sys_time2posix_z
+#define timezone_t sys_timezone_t
+#define tzalloc sys_tzalloc
+#define tzfree sys_tzfree
+#include <time.h>
+#undef localtime_rz
+#undef mktime_z
+#undef posix2time_z
+#undef time2posix_z
+#undef timezone_t
+#undef tzalloc
+#undef tzfree
+
#include "sys/types.h" /* for time_t */
#include "stdio.h"
-#include "errno.h"
#include "string.h"
#include "limits.h" /* for CHAR_BIT et al. */
-#include "time.h"
#include "stdlib.h"
+#include "errno.h"
+
+#ifndef ENAMETOOLONG
+# define ENAMETOOLONG EINVAL
+#endif
+#ifndef EOVERFLOW
+# define EOVERFLOW EINVAL
+#endif
+
#if HAVE_GETTEXT
#include "libintl.h"
#endif /* HAVE_GETTEXT */
@@ -104,6 +129,14 @@
#include "unistd.h" /* for F_OK, R_OK, and other POSIX goodness */
#endif /* HAVE_UNISTD_H */
+#ifndef HAVE_STRFTIME_L
+# if _POSIX_VERSION < 200809
+# define HAVE_STRFTIME_L 0
+# else
+# define HAVE_STRFTIME_L 1
+# endif
+#endif
+
#ifndef F_OK
#define F_OK 0
#endif /* !defined F_OK */
@@ -138,65 +171,98 @@
# include <inttypes.h>
#endif
-#ifndef INT_FAST64_MAX
/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */
-#if defined LLONG_MAX || defined __LONG_LONG_MAX__
-typedef long long int_fast64_t;
+#ifdef __LONG_LONG_MAX__
+# ifndef LLONG_MAX
+# define LLONG_MAX __LONG_LONG_MAX__
+# endif
+# ifndef LLONG_MIN
+# define LLONG_MIN (-1 - LLONG_MAX)
+# endif
+#endif
+
+#ifndef INT_FAST64_MAX
# ifdef LLONG_MAX
+typedef long long int_fast64_t;
# define INT_FAST64_MIN LLONG_MIN
# define INT_FAST64_MAX LLONG_MAX
# else
-# define INT_FAST64_MIN __LONG_LONG_MIN__
-# define INT_FAST64_MAX __LONG_LONG_MAX__
-# endif
-# define SCNdFAST64 "lld"
-#else /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
-#if (LONG_MAX >> 31) < 0xffffffff
+# if LONG_MAX >> 31 < 0xffffffff
Please use a compiler that supports a 64-bit integer type (or wider);
you may need to compile with "-DHAVE_STDINT_H".
-#endif /* (LONG_MAX >> 31) < 0xffffffff */
+# endif
typedef long int_fast64_t;
-# define INT_FAST64_MIN LONG_MIN
-# define INT_FAST64_MAX LONG_MAX
-# define SCNdFAST64 "ld"
-#endif /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
-#endif /* !defined INT_FAST64_MAX */
+# define INT_FAST64_MIN LONG_MIN
+# define INT_FAST64_MAX LONG_MAX
+# endif
+#endif
+
+#ifndef SCNdFAST64
+# if INT_FAST64_MAX == LLONG_MAX
+# define SCNdFAST64 "lld"
+# else
+# define SCNdFAST64 "ld"
+# endif
+#endif
#ifndef INT_FAST32_MAX
# if INT_MAX >> 31 == 0
typedef long int_fast32_t;
+# define INT_FAST32_MAX LONG_MAX
+# define INT_FAST32_MIN LONG_MIN
# else
typedef int int_fast32_t;
+# define INT_FAST32_MAX INT_MAX
+# define INT_FAST32_MIN INT_MIN
# endif
#endif
#ifndef INTMAX_MAX
-# if defined LLONG_MAX || defined __LONG_LONG_MAX__
+# ifdef LLONG_MAX
typedef long long intmax_t;
# define strtoimax strtoll
-# define PRIdMAX "lld"
-# ifdef LLONG_MAX
-# define INTMAX_MAX LLONG_MAX
-# define INTMAX_MIN LLONG_MIN
-# else
-# define INTMAX_MAX __LONG_LONG_MAX__
-# define INTMAX_MIN __LONG_LONG_MIN__
-# endif
+# define INTMAX_MAX LLONG_MAX
+# define INTMAX_MIN LLONG_MIN
# else
typedef long intmax_t;
# define strtoimax strtol
-# define PRIdMAX "ld"
# define INTMAX_MAX LONG_MAX
# define INTMAX_MIN LONG_MIN
# endif
#endif
+#ifndef PRIdMAX
+# if INTMAX_MAX == LLONG_MAX
+# define PRIdMAX "lld"
+# else
+# define PRIdMAX "ld"
+# endif
+#endif
+
+#ifndef UINT_FAST64_MAX
+# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
+typedef unsigned long long uint_fast64_t;
+# else
+# if ULONG_MAX >> 31 >> 1 < 0xffffffff
+Please use a compiler that supports a 64-bit integer type (or wider);
+you may need to compile with "-DHAVE_STDINT_H".
+# endif
+typedef unsigned long uint_fast64_t;
+# endif
+#endif
+
#ifndef UINTMAX_MAX
# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
typedef unsigned long long uintmax_t;
-# define PRIuMAX "llu"
# else
typedef unsigned long uintmax_t;
+# endif
+#endif
+
+#ifndef PRIuMAX
+# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
+# define PRIuMAX "llu"
+# else
# define PRIuMAX "lu"
# endif
#endif
@@ -239,16 +305,6 @@
*/
/*
-** Some time.h implementations don't declare asctime_r.
-** Others might define it as a macro.
-** Fix the former without affecting the latter.
-*/
-
-#ifndef asctime_r
-extern char * asctime_r(struct tm const *, char *);
-#endif
-
-/*
** Compile with -Dtime_tz=T to build the tz package with a private
** time_t type equivalent to T rather than the system-supplied time_t.
** This debugging feature can test unusual design decisions
@@ -256,7 +312,11 @@
** typical platforms.
*/
#ifdef time_tz
+# ifdef LOCALTIME_IMPLEMENTATION
static time_t sys_time(time_t *x) { return time(x); }
+# endif
+
+typedef time_tz tz_time_t;
# undef ctime
# define ctime tz_ctime
@@ -272,14 +332,40 @@
# define localtime tz_localtime
# undef localtime_r
# define localtime_r tz_localtime_r
+# undef localtime_rz
+# define localtime_rz tz_localtime_rz
# undef mktime
# define mktime tz_mktime
+# undef mktime_z
+# define mktime_z tz_mktime_z
+# undef offtime
+# define offtime tz_offtime
+# undef posix2time
+# define posix2time tz_posix2time
+# undef posix2time_z
+# define posix2time_z tz_posix2time_z
# undef time
# define time tz_time
+# undef time2posix
+# define time2posix tz_time2posix
+# undef time2posix_z
+# define time2posix_z tz_time2posix_z
# undef time_t
# define time_t tz_time_t
-
-typedef time_tz time_t;
+# undef timegm
+# define timegm tz_timegm
+# undef timelocal
+# define timelocal tz_timelocal
+# undef timeoff
+# define timeoff tz_timeoff
+# undef tzalloc
+# define tzalloc tz_tzalloc
+# undef tzfree
+# define tzfree tz_tzfree
+# undef tzset
+# define tzset tz_tzset
+# undef tzsetwall
+# define tzsetwall tz_tzsetwall
char *ctime(time_t const *);
char *ctime_r(time_t const *, char *);
@@ -289,36 +375,111 @@
struct tm *localtime(time_t const *);
struct tm *localtime_r(time_t const *restrict, struct tm *restrict);
time_t mktime(struct tm *);
-
-static time_t
-time(time_t *p)
-{
- time_t r = sys_time(0);
- if (p)
- *p = r;
- return r;
-}
+time_t time(time_t *);
+void tzset(void);
#endif
/*
-** Private function declarations.
+** Some time.h implementations don't declare asctime_r.
+** Others might define it as a macro.
+** Fix the former without affecting the latter.
+** Similarly for timezone, daylight, and altzone.
*/
-char * icatalloc(char * old, const char * new);
-char * icpyalloc(const char * string);
-const char * scheck(const char * string, const char * format);
+#ifndef asctime_r
+extern char * asctime_r(struct tm const *restrict, char *restrict);
+#endif
+
+#ifdef USG_COMPAT
+# ifndef timezone
+extern long timezone;
+# endif
+# ifndef daylight
+extern int daylight;
+# endif
+#endif
+#if defined ALTZONE && !defined altzone
+extern long altzone;
+#endif
+
+/*
+** The STD_INSPIRED functions are similar, but most also need
+** declarations if time_tz is defined.
+*/
+
+#ifdef STD_INSPIRED
+# if !defined tzsetwall || defined time_tz
+void tzsetwall(void);
+# endif
+# if !defined offtime || defined time_tz
+struct tm *offtime(time_t const *, long);
+# endif
+# if !defined timegm || defined time_tz
+time_t timegm(struct tm *);
+# endif
+# if !defined timelocal || defined time_tz
+time_t timelocal(struct tm *);
+# endif
+# if !defined timeoff || defined time_tz
+time_t timeoff(struct tm *, long);
+# endif
+# if !defined time2posix || defined time_tz
+time_t time2posix(time_t);
+# endif
+# if !defined posix2time || defined time_tz
+time_t posix2time(time_t);
+# endif
+#endif
+
+/* Infer TM_ZONE on systems where this information is known, but suppress
+ guessing if NO_TM_ZONE is defined. Similarly for TM_GMTOFF. */
+#if (defined __GLIBC__ \
+ || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \
+ || (defined __APPLE__ && defined __MACH__))
+# if !defined TM_GMTOFF && !defined NO_TM_GMTOFF
+# define TM_GMTOFF tm_gmtoff
+# endif
+# if !defined TM_ZONE && !defined NO_TM_ZONE
+# define TM_ZONE tm_zone
+# endif
+#endif
+
+/*
+** Define functions that are ABI compatible with NetBSD but have
+** better prototypes. NetBSD 6.1.4 defines a pointer type timezone_t
+** and labors under the misconception that 'const timezone_t' is a
+** pointer to a constant. This use of 'const' is ineffective, so it
+** is not done here. What we call 'struct state' NetBSD calls
+** 'struct __state', but this is a private name so it doesn't matter.
+*/
+#if NETBSD_INSPIRED
+typedef struct state *timezone_t;
+struct tm *localtime_rz(timezone_t restrict, time_t const *restrict,
+ struct tm *restrict);
+time_t mktime_z(timezone_t restrict, struct tm *restrict);
+timezone_t tzalloc(char const *);
+void tzfree(timezone_t);
+# ifdef STD_INSPIRED
+# if !defined posix2time_z || defined time_tz
+time_t posix2time_z(timezone_t, time_t) ATTRIBUTE_PURE;
+# endif
+# if !defined time2posix_z || defined time_tz
+time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE;
+# endif
+# endif
+#endif
/*
** Finally, some convenience items.
*/
-#ifndef TRUE
-#define TRUE 1
-#endif /* !defined TRUE */
-
-#ifndef FALSE
-#define FALSE 0
-#endif /* !defined FALSE */
+#if __STDC_VERSION__ < 199901
+# define true 1
+# define false 0
+# define bool int
+#else
+# include <stdbool.h>
+#endif
#ifndef TYPE_BIT
#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
@@ -330,14 +491,14 @@
#define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0)
- /* Max and min values of the integer type T, of which only the bottom
- * B bits are used, and where the highest-order used bit is considered
- * to be a sign bit if T is signed. */
-#define MAXVAL(t, b) \
- ((t) (((t) 1 << ((b) - 1 - TYPE_SIGNED(t))) \
- - 1 + ((t) 1 << ((b) - 1 - TYPE_SIGNED(t)))))
-#define MINVAL(t, b) \
- ((t) (TYPE_SIGNED(t) ? - TWOS_COMPLEMENT(t) - MAXVAL(t, b) : 0))
+/* Max and min values of the integer type T, of which only the bottom
+ B bits are used, and where the highest-order used bit is considered
+ to be a sign bit if T is signed. */
+#define MAXVAL(t, b) \
+ ((t) (((t) 1 << ((b) - 1 - TYPE_SIGNED(t))) \
+ - 1 + ((t) 1 << ((b) - 1 - TYPE_SIGNED(t)))))
+#define MINVAL(t, b) \
+ ((t) (TYPE_SIGNED(t) ? - TWOS_COMPLEMENT(t) - MAXVAL(t, b) : 0))
/* The minimum and maximum finite time values. This assumes no padding. */
static time_t const time_t_min = MINVAL(time_t, TYPE_BIT(time_t));
@@ -365,6 +526,10 @@
# define INITIALIZE(x)
#endif
+#ifndef UNINIT_TRAP
+# define UNINIT_TRAP 0
+#endif
+
/*
** For the benefit of GNU folk...
** '_(MSGID)' uses the current locale's message library string for MSGID.
@@ -379,7 +544,7 @@
#endif /* !HAVE_GETTEXT */
#endif /* !defined _ */
-#if !defined TZ_DOMAIN && defined TZ_DOMAINDIR
+#if !defined TZ_DOMAIN && defined HAVE_GETTEXT
# define TZ_DOMAIN "tz"
#endif
@@ -410,8 +575,4 @@
#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */
#endif /* !defined SECSPERREPEAT_BITS */
-/*
-** UNIX was a registered trademark of The Open Group in 2003.
-*/
-
#endif /* !defined PRIVATE_H */
diff --git a/libc/tzcode/strftime.c b/libc/tzcode/strftime.c
index 4328b4c..10dfb4b 100644
--- a/libc/tzcode/strftime.c
+++ b/libc/tzcode/strftime.c
@@ -55,15 +55,7 @@
const char * date_fmt;
};
-#ifdef LOCALE_HOME
-#include "sys/stat.h"
-static struct lc_time_T localebuf;
-static struct lc_time_T * _loc(void);
-#define Locale _loc()
-#endif /* defined LOCALE_HOME */
-#ifndef LOCALE_HOME
#define Locale (&C_time_locale)
-#endif /* !defined LOCALE_HOME */
static const struct lc_time_T C_time_locale = {
{
@@ -115,7 +107,7 @@
static char * _conv(int, const char *, char *, const char *);
static char * _fmt(const char *, const struct tm *, char *, const char *,
int *);
-static char * _yconv(int, int, int, int, char *, const char *, int);
+static char * _yconv(int, int, bool, bool, char *, const char *, int);
static char * getformat(int, char *, char *, char *, char *);
extern char * tzname[];
@@ -132,32 +124,28 @@
#define FORCE_LOWER_CASE 0x100
size_t
-strftime(char * const s, const size_t maxsize, const char *const format,
- const struct tm *const t)
+strftime(char *s, size_t maxsize, const char *format, const struct tm *t)
{
char * p;
int warn;
tzset();
-#ifdef LOCALE_HOME
- localebuf.mon[0] = 0;
-#endif /* defined LOCALE_HOME */
warn = IN_NONE;
p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn);
#ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) {
- (void) fprintf(stderr, "\n");
+ fprintf(stderr, "\n");
if (format == NULL)
- (void) fprintf(stderr, "NULL strftime format ");
- else (void) fprintf(stderr, "strftime format \"%s\" ",
+ fprintf(stderr, "NULL strftime format ");
+ else fprintf(stderr, "strftime format \"%s\" ",
format);
- (void) fprintf(stderr, "yields only two digits of years in ");
+ fprintf(stderr, "yields only two digits of years in ");
if (warn == IN_SOME)
- (void) fprintf(stderr, "some locales");
+ fprintf(stderr, "some locales");
else if (warn == IN_THIS)
- (void) fprintf(stderr, "the current locale");
- else (void) fprintf(stderr, "all locales");
- (void) fprintf(stderr, "\n");
+ fprintf(stderr, "the current locale");
+ else fprintf(stderr, "all locales");
+ fprintf(stderr, "\n");
}
#endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */
if (p == s + maxsize)
@@ -171,20 +159,17 @@
switch (modifier) {
case '_':
return underscore;
-
case '-':
return dash;
-
case '0':
return zero;
}
-
return normal;
}
static char *
-_fmt(const char *format, const struct tm *const t, char * pt,
- const char *const ptlim, int *warnp)
+_fmt(const char *format, const struct tm *t, char *pt,
+ const char *ptlim, int *warnp)
{
for ( ; *format; ++format) {
if (*format == '%') {
@@ -227,8 +212,8 @@
** something completely different.
** (ado, 1993-05-24)
*/
- pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0,
- pt, ptlim, modifier);
+ pt = _yconv(t->tm_year, TM_YEAR_BASE,
+ true, false, pt, ptlim, modifier);
continue;
case 'c':
{
@@ -245,10 +230,7 @@
pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
continue;
case 'd':
- pt = _conv(t->tm_mday,
- getformat(modifier, "%02d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
+ pt = _conv(t->tm_mday, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
continue;
case 'E':
case 'O':
@@ -270,31 +252,21 @@
modifier = *format;
goto label;
case 'e':
- pt = _conv(t->tm_mday,
- getformat(modifier, "%2d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
+ pt = _conv(t->tm_mday, getformat(modifier, "%2d", "%2d", "%d", "%02d"), pt, ptlim);
continue;
case 'F':
pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
continue;
case 'H':
- pt = _conv(t->tm_hour,
- getformat(modifier, "%02d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
+ pt = _conv(t->tm_hour, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
continue;
case 'I':
pt = _conv((t->tm_hour % 12) ?
(t->tm_hour % 12) : 12,
- getformat(modifier, "%02d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
+ getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
continue;
case 'j':
- pt = _conv(t->tm_yday + 1,
- getformat(modifier, "%03d", "%3d", "%d", "%03d"),
- pt, ptlim);
+ pt = _conv(t->tm_yday + 1, getformat(modifier, "%03d", "%3d", "%d", "%03d"), pt, ptlim);
continue;
case 'k':
/*
@@ -307,10 +279,7 @@
** "%l" have been swapped.
** (ado, 1993-05-24)
*/
- pt = _conv(t->tm_hour,
- getformat(modifier, "%2d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
+ pt = _conv(t->tm_hour, getformat(modifier, "%2d", "%2d", "%d", "%02d"), pt, ptlim);
continue;
#ifdef KITCHEN_SINK
case 'K':
@@ -332,36 +301,23 @@
*/
pt = _conv((t->tm_hour % 12) ?
(t->tm_hour % 12) : 12,
- getformat(modifier, "%2d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
+ getformat(modifier, "%2d", "%2d", "%d", "%02d"), pt, ptlim);
continue;
case 'M':
- pt = _conv(t->tm_min,
- getformat(modifier, "%02d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
+ pt = _conv(t->tm_min, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
continue;
case 'm':
- pt = _conv(t->tm_mon + 1,
- getformat(modifier, "%02d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
+ pt = _conv(t->tm_mon + 1, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
continue;
case 'n':
pt = _add("\n", pt, ptlim, modifier);
continue;
+ case 'P':
case 'p':
pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
Locale->pm :
Locale->am,
- pt, ptlim, modifier);
- continue;
- case 'P':
- pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
- Locale->pm :
- Locale->am,
- pt, ptlim, FORCE_LOWER_CASE);
+ pt, ptlim, (*format == 'P') ? FORCE_LOWER_CASE : modifier);
continue;
case 'R':
pt = _fmt("%H:%M", t, pt, ptlim, warnp);
@@ -370,10 +326,7 @@
pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
continue;
case 'S':
- pt = _conv(t->tm_sec,
- getformat(modifier, "%02d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
+ pt = _conv(t->tm_sec, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
continue;
case 's':
{
@@ -385,10 +338,10 @@
tm = *t;
mkt = mktime64(&tm);
if (TYPE_SIGNED(time64_t))
- (void) snprintf(buf, sizeof(buf), "%lld",
- (long long) mkt);
- else (void) snprintf(buf, sizeof(buf), "%llu",
- (unsigned long long) mkt);
+ snprintf(buf, sizeof(buf), "%"PRIdMAX,
+ (intmax_t) mkt);
+ else snprintf(buf, sizeof(buf), "%"PRIuMAX,
+ (uintmax_t) mkt);
pt = _add(buf, pt, ptlim, modifier);
}
continue;
@@ -401,9 +354,7 @@
case 'U':
pt = _conv((t->tm_yday + DAYSPERWEEK -
t->tm_wday) / DAYSPERWEEK,
- getformat(modifier, "%02d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
+ getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
continue;
case 'u':
/*
@@ -413,7 +364,8 @@
** (ado, 1993-05-24)
*/
pt = _conv((t->tm_wday == 0) ?
- DAYSPERWEEK : t->tm_wday, "%d", pt, ptlim);
+ DAYSPERWEEK : t->tm_wday,
+ "%d", pt, ptlim);
continue;
case 'V': /* ISO 8601 week number */
case 'G': /* ISO 8601 year (four digits) */
@@ -493,14 +445,15 @@
w = 53;
#endif /* defined XPG4_1994_04_09 */
if (*format == 'V')
- pt = _conv(w,
- getformat(modifier, "%02d", "%2d", "%d", "%02d"),
+ pt = _conv(w, getformat(modifier, "%02d", "%2d", "%d", "%02d"),
pt, ptlim);
else if (*format == 'g') {
*warnp = IN_ALL;
- pt = _yconv(year, base, 0, 1,
+ pt = _yconv(year, base,
+ false, true,
pt, ptlim, modifier);
- } else pt = _yconv(year, base, 1, 1,
+ } else pt = _yconv(year, base,
+ true, true,
pt, ptlim, modifier);
}
continue;
@@ -517,9 +470,7 @@
(t->tm_wday ?
(t->tm_wday - 1) :
(DAYSPERWEEK - 1))) / DAYSPERWEEK,
- getformat(modifier, "%02d",
- "%2d", "%d", "%02d"),
- pt, ptlim);
+ getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
continue;
case 'w':
pt = _conv(t->tm_wday, "%d", pt, ptlim);
@@ -540,23 +491,23 @@
continue;
case 'y':
*warnp = IN_ALL;
- pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1,
+ pt = _yconv(t->tm_year, TM_YEAR_BASE,
+ false, true,
pt, ptlim, modifier);
continue;
case 'Y':
- pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1,
+ pt = _yconv(t->tm_year, TM_YEAR_BASE,
+ true, true,
pt, ptlim, modifier);
continue;
case 'Z':
#ifdef TM_ZONE
- if (t->TM_ZONE != NULL)
- pt = _add(t->TM_ZONE, pt, ptlim,
- modifier);
- else
-#endif /* defined TM_ZONE */
+ pt = _add(t->TM_ZONE, pt, ptlim, modifier);
+#else
if (t->tm_isdst >= 0)
pt = _add(tzname[t->tm_isdst != 0],
- pt, ptlim, modifier);
+ pt, ptlim);
+#endif
/*
** C99 says that %Z must be replaced by the
** empty string if the time zone is not
@@ -613,10 +564,7 @@
diff /= SECSPERMIN;
diff = (diff / MINSPERHOUR) * 100 +
(diff % MINSPERHOUR);
- pt = _conv(diff,
- getformat(modifier, "%04d",
- "%4d", "%d", "%04d"),
- pt, ptlim);
+ pt = _conv(diff, getformat(modifier, "%04d", "%4d", "%d", "%04d"), pt, ptlim);
}
continue;
case '+':
@@ -641,13 +589,12 @@
}
static char *
-_conv(const int n, const char *const format, char *const pt,
- const char *const ptlim)
+_conv(int n, const char *format, char *pt, const char *ptlim)
{
- char buf[INT_STRLEN_MAXIMUM(int) + 1];
+ char buf[INT_STRLEN_MAXIMUM(int) + 1];
- (void) snprintf(buf, sizeof(buf), format, n);
- return _add(buf, pt, ptlim, 0);
+ snprintf(buf, sizeof(buf), format, n);
+ return _add(buf, pt, ptlim, 0);
}
static char *
@@ -699,8 +646,8 @@
*/
static char *
-_yconv(const int a, const int b, const int convert_top, const int convert_yy,
- char *pt, const char *const ptlim, int modifier)
+_yconv(int a, int b, bool convert_top, bool convert_yy,
+ char *pt, const char *ptlim, int modifier)
{
register int lead;
register int trail;
diff --git a/libc/tzcode/tzfile.h b/libc/tzcode/tzfile.h
index 529650d..ebecd68 100644
--- a/libc/tzcode/tzfile.h
+++ b/libc/tzcode/tzfile.h
@@ -40,7 +40,7 @@
struct tzhead {
char tzh_magic[4]; /* TZ_MAGIC */
char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */
- char tzh_reserved[15]; /* reserved--must be zero */
+ char tzh_reserved[15]; /* reserved; must be zero */
char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
char tzh_leapcnt[4]; /* coded number of leap seconds */
@@ -62,13 +62,13 @@
** tzh_leapcnt repetitions of
** one (char [4]) coded leap second transition times
** one (char [4]) total correction after above
-** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition
-** time is standard time, if FALSE,
+** tzh_ttisstdcnt (char)s indexed by type; if 1, transition
+** time is standard time, if 0,
** transition time is wall clock time
** if absent, transition times are
** assumed to be wall clock time
-** tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition
-** time is UT, if FALSE,
+** tzh_ttisgmtcnt (char)s indexed by type; if 1, transition
+** time is UT, if 0,
** transition time is local time
** if absent, transition times are
** assumed to be local time
@@ -97,7 +97,7 @@
*/
#ifndef TZ_MAX_TIMES
-#define TZ_MAX_TIMES 1200
+#define TZ_MAX_TIMES 2000
#endif /* !defined TZ_MAX_TIMES */
#ifndef TZ_MAX_TYPES
diff --git a/libc/upstream-openbsd/lib/libc/net/inet_addr.c b/libc/upstream-openbsd/lib/libc/net/inet_addr.c
deleted file mode 100644
index 18762ab..0000000
--- a/libc/upstream-openbsd/lib/libc/net/inet_addr.c
+++ /dev/null
@@ -1,175 +0,0 @@
-/* $OpenBSD: inet_addr.c,v 1.10 2013/11/24 23:51:28 deraadt Exp $ */
-
-/*
- * ++Copyright++ 1983, 1990, 1993
- * -
- * 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.
- * 3. 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--
- */
-
-#include <sys/types.h>
-#include <sys/param.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <ctype.h>
-
-/*
- * Ascii internet address interpretation routine.
- * The value returned is in network order.
- */
-in_addr_t
-inet_addr(const char *cp)
-{
- struct in_addr val;
-
- if (inet_aton(cp, &val))
- return (val.s_addr);
- return (INADDR_NONE);
-}
-
-/*
- * Check whether "cp" is a valid ascii representation
- * of an Internet address and convert to a binary address.
- * Returns 1 if the address is valid, 0 if not.
- * This replaces inet_addr, the return value from which
- * cannot distinguish between failure and a local broadcast address.
- */
-int
-inet_aton(const char *cp, struct in_addr *addr)
-{
- in_addr_t val;
- int base, n;
- char c;
- u_int parts[4];
- u_int *pp = parts;
-
- c = *cp;
- for (;;) {
- /*
- * Collect number up to ``.''.
- * Values are specified as for C:
- * 0x=hex, 0=octal, isdigit=decimal.
- */
- if (!isdigit((unsigned char)c))
- return (0);
- val = 0; base = 10;
- if (c == '0') {
- c = *++cp;
- if (c == 'x' || c == 'X')
- base = 16, c = *++cp;
- else
- base = 8;
- }
- for (;;) {
- if (isascii((unsigned char)c) &&
- isdigit((unsigned char)c)) {
- val = (val * base) + (c - '0');
- c = *++cp;
- } else if (base == 16 &&
- isascii((unsigned char)c) &&
- isxdigit((unsigned char)c)) {
- val = (val << 4) |
- (c + 10 - (islower((unsigned char)c) ? 'a' : 'A'));
- c = *++cp;
- } 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)
- return (0);
- *pp++ = val;
- c = *++cp;
- } else
- break;
- }
- /*
- * Check for trailing characters.
- */
- if (c != '\0' &&
- (!isascii((unsigned char)c) || !isspace((unsigned char)c)))
- return (0);
- /*
- * Concoct the address according to
- * the number of parts specified.
- */
- n = pp - parts + 1;
- switch (n) {
-
- case 0:
- return (0); /* initial nondigit */
-
- case 1: /* a -- 32 bits */
- break;
-
- case 2: /* a.b -- 8.24 bits */
- if ((val > 0xffffff) || (parts[0] > 0xff))
- return (0);
- val |= parts[0] << 24;
- break;
-
- case 3: /* a.b.c -- 8.8.16 bits */
- if ((val > 0xffff) || (parts[0] > 0xff) || (parts[1] > 0xff))
- return (0);
- val |= (parts[0] << 24) | (parts[1] << 16);
- break;
-
- case 4: /* a.b.c.d -- 8.8.8.8 bits */
- if ((val > 0xff) || (parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xff))
- return (0);
- val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
- break;
- }
- if (addr)
- addr->s_addr = htonl(val);
- return (1);
-}
diff --git a/libc/upstream-openbsd/lib/libc/net/inet_network.c b/libc/upstream-openbsd/lib/libc/net/inet_network.c
deleted file mode 100644
index ecf554e..0000000
--- a/libc/upstream-openbsd/lib/libc/net/inet_network.c
+++ /dev/null
@@ -1,84 +0,0 @@
-/* $OpenBSD: inet_network.c,v 1.11 2013/11/25 17:29:19 deraadt Exp $ */
-/*
- * Copyright (c) 1983, 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.
- * 3. 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.
- */
-
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <ctype.h>
-
-/*
- * Internet network address interpretation routine.
- * The library routines call this routine to interpret
- * network numbers.
- */
-in_addr_t
-inet_network(const char *cp)
-{
- in_addr_t val, base, n;
- u_char c;
- in_addr_t parts[4], *pp = parts;
- int i;
-
-again:
- val = 0; base = 10;
- if (*cp == '0')
- base = 8, cp++;
- if (*cp == 'x' || *cp == 'X')
- base = 16, cp++;
- while ((c = *cp)) {
- if (isdigit(c)) {
- val = (val * base) + (c - '0');
- cp++;
- continue;
- }
- if (base == 16 && isxdigit(c)) {
- val = (val << 4) + (c + 10 - (islower(c) ? 'a' : 'A'));
- cp++;
- continue;
- }
- break;
- }
- if (*cp == '.') {
- if (pp >= parts + 3)
- return (INADDR_NONE);
- *pp++ = val, cp++;
- goto again;
- }
- if (*cp && !isspace(*cp))
- return (INADDR_NONE);
- *pp++ = val;
- n = pp - parts;
- for (val = 0, i = 0; i < 4; i++) {
- val <<= 8;
- if (i < n)
- val |= parts[i] & 0xff;
- }
- return (val);
-}
diff --git a/linker/Android.mk b/linker/Android.mk
index a962c76..8be53a1 100644
--- a/linker/Android.mk
+++ b/linker/Android.mk
@@ -2,7 +2,9 @@
include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= \
+LOCAL_CLANG := true
+
+LOCAL_SRC_FILES := \
debugger.cpp \
dlfcn.cpp \
linker.cpp \
@@ -12,6 +14,7 @@
linker_libc_support.c \
linker_memory.cpp \
linker_phdr.cpp \
+ linker_utils.cpp \
rt.cpp \
LOCAL_SRC_FILES_arm := arch/arm/begin.S
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 3e59218..841b957 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -58,10 +58,12 @@
#include "linker_phdr.h"
#include "linker_relocs.h"
#include "linker_reloc_iterators.h"
+#include "linker_utils.h"
#include "base/strings.h"
#include "ziparchive/zip_archive.h"
+extern void __libc_init_globals(KernelArgumentBlock&);
extern void __libc_init_AT_SECURE(KernelArgumentBlock&);
// Override macros to use C++ style casts.
@@ -360,13 +362,13 @@
static bool realpath_fd(int fd, std::string* realpath) {
std::vector<char> buf(PATH_MAX), proc_self_fd(PATH_MAX);
- snprintf(&proc_self_fd[0], proc_self_fd.size(), "/proc/self/fd/%d", fd);
+ __libc_format_buffer(&proc_self_fd[0], proc_self_fd.size(), "/proc/self/fd/%d", fd);
if (readlink(&proc_self_fd[0], &buf[0], buf.size()) == -1) {
PRINT("readlink('%s') failed: %s [fd=%d]", &proc_self_fd[0], strerror(errno), fd);
return false;
}
- *realpath = std::string(&buf[0]);
+ *realpath = &buf[0];
return true;
}
@@ -1165,15 +1167,21 @@
}
static int open_library_in_zipfile(ZipArchiveCache* zip_archive_cache,
- const char* const path,
- off64_t* file_offset) {
- TRACE("Trying zip file open from path '%s'", path);
+ const char* const input_path,
+ off64_t* file_offset, std::string* realpath) {
+ std::string normalized_path;
+ if (!normalize_path(input_path, &normalized_path)) {
+ return -1;
+ }
+
+ const char* const path = normalized_path.c_str();
+ TRACE("Trying zip file open from path '%s' -> normalized '%s'", input_path, path);
// Treat an '!/' separator inside a path as the separator between the name
// of the zip file on disk and the subdirectory to search within it.
// For example, if path is "foo.zip!/bar/bas/x.so", then we search for
// "bar/bas/x.so" within "foo.zip".
- const char* separator = strstr(path, kZipFileSeparator);
+ const char* const separator = strstr(path, kZipFileSeparator);
if (separator == nullptr) {
return -1;
}
@@ -1215,6 +1223,15 @@
}
*file_offset = entry.offset;
+
+ if (realpath_fd(fd, realpath)) {
+ *realpath += separator;
+ } else {
+ PRINT("warning: unable to get realpath for the library \"%s\". Will use given path.",
+ normalized_path.c_str());
+ *realpath = normalized_path;
+ }
+
return fd;
}
@@ -1228,7 +1245,7 @@
return true;
}
-static int open_library_on_default_path(const char* name, off64_t* file_offset) {
+static int open_library_on_default_path(const char* name, off64_t* file_offset, std::string* realpath) {
for (size_t i = 0; g_default_ld_paths[i] != nullptr; ++i) {
char buf[512];
if (!format_path(buf, sizeof(buf), g_default_ld_paths[i], name)) {
@@ -1238,6 +1255,10 @@
int fd = TEMP_FAILURE_RETRY(open(buf, O_RDONLY | O_CLOEXEC));
if (fd != -1) {
*file_offset = 0;
+ if (!realpath_fd(fd, realpath)) {
+ PRINT("warning: unable to get realpath for the library \"%s\". Will use given path.", buf);
+ *realpath = buf;
+ }
return fd;
}
}
@@ -1247,7 +1268,8 @@
static int open_library_on_paths(ZipArchiveCache* zip_archive_cache,
const char* name, off64_t* file_offset,
- const std::vector<std::string>& paths) {
+ const std::vector<std::string>& paths,
+ std::string* realpath) {
for (const auto& path_str : paths) {
char buf[512];
const char* const path = path_str.c_str();
@@ -1257,13 +1279,17 @@
int fd = -1;
if (strstr(buf, kZipFileSeparator) != nullptr) {
- fd = open_library_in_zipfile(zip_archive_cache, buf, file_offset);
+ fd = open_library_in_zipfile(zip_archive_cache, buf, file_offset, realpath);
}
if (fd == -1) {
fd = TEMP_FAILURE_RETRY(open(buf, O_RDONLY | O_CLOEXEC));
if (fd != -1) {
*file_offset = 0;
+ if (!realpath_fd(fd, realpath)) {
+ PRINT("warning: unable to get realpath for the library \"%s\". Will use given path.", buf);
+ *realpath = buf;
+ }
}
}
@@ -1277,13 +1303,13 @@
static int open_library(ZipArchiveCache* zip_archive_cache,
const char* name, soinfo *needed_by,
- off64_t* file_offset) {
+ off64_t* file_offset, std::string* realpath) {
TRACE("[ opening %s ]", name);
// If the name contains a slash, we should attempt to open it directly and not search the paths.
if (strchr(name, '/') != nullptr) {
if (strstr(name, kZipFileSeparator) != nullptr) {
- int fd = open_library_in_zipfile(zip_archive_cache, name, file_offset);
+ int fd = open_library_in_zipfile(zip_archive_cache, name, file_offset, realpath);
if (fd != -1) {
return fd;
}
@@ -1297,13 +1323,13 @@
}
// Otherwise we try LD_LIBRARY_PATH first, and fall back to the built-in well known paths.
- int fd = open_library_on_paths(zip_archive_cache, name, file_offset, g_ld_library_paths);
+ int fd = open_library_on_paths(zip_archive_cache, name, file_offset, g_ld_library_paths, realpath);
if (fd == -1 && needed_by) {
- fd = open_library_on_paths(zip_archive_cache, name, file_offset, needed_by->get_dt_runpath());
+ fd = open_library_on_paths(zip_archive_cache, name, file_offset, needed_by->get_dt_runpath(), realpath);
}
if (fd == -1) {
- fd = open_library_on_default_path(name, file_offset);
+ fd = open_library_on_default_path(name, file_offset, realpath);
}
return fd;
@@ -1336,7 +1362,8 @@
static soinfo* load_library(int fd, off64_t file_offset,
LoadTaskList& load_tasks,
const char* name, int rtld_flags,
- const android_dlextinfo* extinfo) {
+ const android_dlextinfo* extinfo,
+ const std::string& realpath) {
if ((file_offset % PAGE_SIZE) != 0) {
DL_ERR("file offset for the library \"%s\" is not page-aligned: %" PRId64, name, file_offset);
return nullptr;
@@ -1378,12 +1405,6 @@
return nullptr;
}
- std::string realpath = name;
- if (!realpath_fd(fd, &realpath)) {
- PRINT("warning: unable to get realpath for the library \"%s\". Will use given name.", name);
- realpath = name;
- }
-
// Read the ELF header and load the segments.
ElfReader elf_reader(realpath.c_str(), fd, file_offset, file_stat.st_size);
if (!elf_reader.Load(extinfo)) {
@@ -1416,22 +1437,29 @@
LoadTaskList& load_tasks, const char* name,
soinfo* needed_by, int rtld_flags,
const android_dlextinfo* extinfo) {
+ off64_t file_offset;
+ std::string realpath;
if (extinfo != nullptr && (extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) != 0) {
- off64_t file_offset = 0;
+ file_offset = 0;
if ((extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET) != 0) {
file_offset = extinfo->library_fd_offset;
}
- return load_library(extinfo->library_fd, file_offset, load_tasks, name, rtld_flags, extinfo);
+
+ if (!realpath_fd(extinfo->library_fd, &realpath)) {
+ PRINT("warning: unable to get realpath for the library \"%s\" by extinfo->library_fd. "
+ "Will use given name.", name);
+ realpath = name;
+ }
+ return load_library(extinfo->library_fd, file_offset, load_tasks, name, rtld_flags, extinfo, realpath);
}
// Open the file.
- off64_t file_offset;
- int fd = open_library(zip_archive_cache, name, needed_by, &file_offset);
+ int fd = open_library(zip_archive_cache, name, needed_by, &file_offset, &realpath);
if (fd == -1) {
DL_ERR("library \"%s\" not found", name);
return nullptr;
}
- soinfo* result = load_library(fd, file_offset, load_tasks, name, rtld_flags, extinfo);
+ soinfo* result = load_library(fd, file_offset, load_tasks, name, rtld_flags, extinfo, realpath);
close(fd);
return result;
}
@@ -3075,9 +3103,7 @@
#if !defined(__LP64__)
if (has_text_relocations) {
// Fail if app is targeting sdk version > 22
- // TODO (dimitry): remove != __ANDROID_API__ check once http://b/20020312 is fixed
- if (get_application_target_sdk_version() != __ANDROID_API__
- && get_application_target_sdk_version() > 22) {
+ if (get_application_target_sdk_version() > 22) {
DL_ERR("%s: has text relocations", get_realpath());
return false;
}
@@ -3554,6 +3580,9 @@
__libc_init_main_thread(args);
+ // Initialize the linker's static libc's globals
+ __libc_init_globals(args);
+
// Initialize the linker's own global variables
linker_so.call_constructors();
diff --git a/linker/linker_utils.cpp b/linker/linker_utils.cpp
new file mode 100644
index 0000000..5d39d83
--- /dev/null
+++ b/linker/linker_utils.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "linker_utils.h"
+#include "linker_debug.h"
+
+bool normalize_path(const char* path, std::string* normalized_path) {
+ // Input should be an absolute path
+ if (path[0] != '/') {
+ PRINT("canonize_path - invalid input: '%s', the input path should be absolute", path);
+ return false;
+ }
+
+ const size_t len = strlen(path) + 1;
+ char buf[len];
+
+ const char* in_ptr = path;
+ char* out_ptr = buf;
+
+ while (*in_ptr != 0) {
+ if (*in_ptr == '/') {
+ char c1 = in_ptr[1];
+ if (c1 == '.') {
+ char c2 = in_ptr[2];
+ if (c2 == '/') {
+ in_ptr += 2;
+ continue;
+ } else if (c2 == '.' && (in_ptr[3] == '/' || in_ptr[3] == 0)) {
+ in_ptr += 3;
+ while (out_ptr > buf && *--out_ptr != '/') {
+ }
+ if (in_ptr[0] == 0) {
+ // retain '/'
+ out_ptr++;
+ }
+ continue;
+ }
+ } else if (c1 == '/') {
+ ++in_ptr;
+ continue;
+ }
+ }
+ *out_ptr++ = *in_ptr++;
+ }
+
+ *out_ptr = 0;
+ *normalized_path = buf;
+ return true;
+}
+
diff --git a/linker/linker_utils.h b/linker/linker_utils.h
new file mode 100644
index 0000000..fc79fd1
--- /dev/null
+++ b/linker/linker_utils.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef __LINKER_UTILS_H
+#define __LINKER_UTILS_H
+
+#include <string>
+
+bool normalize_path(const char* path, std::string* normalized_path);
+
+#endif
diff --git a/linker/tests/Android.mk b/linker/tests/Android.mk
index 0aaee3e..a061877 100644
--- a/linker/tests/Android.mk
+++ b/linker/tests/Android.mk
@@ -27,11 +27,14 @@
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../libc/
LOCAL_SRC_FILES := \
+ linker_globals.cpp \
linked_list_test.cpp \
linker_block_allocator_test.cpp \
../linker_block_allocator.cpp \
linker_memory_allocator_test.cpp \
- ../linker_allocator.cpp
+ ../linker_allocator.cpp \
+ linker_utils_test.cpp \
+ ../linker_utils.cpp
# for __libc_fatal
LOCAL_SRC_FILES += ../../libc/bionic/libc_logging.cpp
diff --git a/linker/tests/linker_globals.cpp b/linker/tests/linker_globals.cpp
new file mode 100644
index 0000000..7762a87
--- /dev/null
+++ b/linker/tests/linker_globals.cpp
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// To enable logging
+int g_ld_debug_verbosity = 0;
+
diff --git a/linker/tests/linker_memory_allocator_test.cpp b/linker/tests/linker_memory_allocator_test.cpp
index f002a0d..defd4f3 100644
--- a/linker/tests/linker_memory_allocator_test.cpp
+++ b/linker/tests/linker_memory_allocator_test.cpp
@@ -53,7 +53,7 @@
LinkerMemoryAllocator allocator;
void* ptr = allocator.alloc(0);
ASSERT_TRUE(ptr != nullptr);
- free(ptr);
+ allocator.free(ptr);
}
TEST(linker_memory, test_free_nullptr) {
@@ -110,7 +110,7 @@
ASSERT_TRUE(memcmp(reallocated_ptr, model, 4000) == 0);
- ASSERT_EQ(nullptr, realloc(reallocated_ptr, 0));
+ ASSERT_EQ(nullptr, allocator.realloc(reallocated_ptr, 0));
}
TEST(linker_memory, test_small_smoke) {
diff --git a/linker/tests/linker_utils_test.cpp b/linker/tests/linker_utils_test.cpp
new file mode 100644
index 0000000..458474e
--- /dev/null
+++ b/linker/tests/linker_utils_test.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include <gtest/gtest.h>
+
+#include "../linker_utils.h"
+
+TEST(linker_utils, normalize_path_smoke) {
+ std::string output;
+ ASSERT_TRUE(normalize_path("/../root///dir/.///dir2/somedir/../zipfile!/dir/dir9//..///afile", &output));
+ ASSERT_EQ("/root/dir/dir2/zipfile!/dir/afile", output);
+
+ ASSERT_TRUE(normalize_path("/../root///dir/.///dir2/somedir/.../zipfile!/.dir/dir9//..///afile", &output));
+ ASSERT_EQ("/root/dir/dir2/somedir/.../zipfile!/.dir/afile", output);
+
+ ASSERT_TRUE(normalize_path("/root/..", &output));
+ ASSERT_EQ("/", output);
+
+ ASSERT_TRUE(normalize_path("/root/notroot/..", &output));
+ ASSERT_EQ("/root/", output);
+
+ ASSERT_TRUE(normalize_path("/a/../../b", &output));
+ ASSERT_EQ("/b", output);
+
+ output = "unchanged";
+ ASSERT_FALSE(normalize_path("root///dir/.///dir2/somedir/../zipfile!/dir/dir9//..///afile", &output));
+ ASSERT_EQ("unchanged", output);
+}
diff --git a/tests/Android.mk b/tests/Android.mk
index 262be64..e2c4fb4 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -255,11 +255,6 @@
libBionicCtsGtestMain_cppflags := $(test_cppflags) -DUSING_GTEST_OUTPUT_FORMAT \
-# Temporarily fix the job count to 1 for CTS since on some devices the
-# number of online cores is incorrectly read as the total number of cores
-# in the system. When b/24376925 is fixed, this should be removed.
-libBionicCtsGtestMain_cppflags += -DJOB_COUNT_FIXED=1
-
module := libBionicCtsGtestMain
module_tag := optional
build_type := target
diff --git a/tests/arpa_inet_test.cpp b/tests/arpa_inet_test.cpp
index 5e53337..a368b8f 100644
--- a/tests/arpa_inet_test.cpp
+++ b/tests/arpa_inet_test.cpp
@@ -24,8 +24,85 @@
TEST(arpa_inet, inet_aton) {
in_addr a;
- ASSERT_EQ(1, inet_aton("127.0.0.1", &a));
+
+ // a.b.c.d
+ a.s_addr = 0;
+ ASSERT_EQ(1, inet_aton("127.1.2.3", &a));
+ ASSERT_EQ((htonl)(0x7f010203), a.s_addr);
+
+ // a.b.c
+ a.s_addr = 0;
+ ASSERT_EQ(1, inet_aton("127.1.2", &a));
+ ASSERT_EQ((htonl)(0x7f010002), a.s_addr);
+
+ // a.b
+ a.s_addr = 0;
+ ASSERT_EQ(1, inet_aton("127.1", &a));
ASSERT_EQ((htonl)(0x7f000001), a.s_addr);
+
+ // a
+ a.s_addr = 0;
+ ASSERT_EQ(1, inet_aton("0x7f000001", &a));
+ ASSERT_EQ((htonl)(0x7f000001), a.s_addr);
+
+ // Hex (0x) and mixed-case hex digits.
+ a.s_addr = 0;
+ ASSERT_EQ(1, inet_aton("0xFf.0.0.1", &a));
+ ASSERT_EQ((htonl)(0xff000001), a.s_addr);
+
+ // Hex (0X) and mixed-case hex digits.
+ a.s_addr = 0;
+ ASSERT_EQ(1, inet_aton("0XfF.0.0.1", &a));
+ ASSERT_EQ((htonl)(0xff000001), a.s_addr);
+
+ // Octal.
+ a.s_addr = 0;
+ ASSERT_EQ(1, inet_aton("0177.0.0.1", &a));
+ ASSERT_EQ((htonl)(0x7f000001), a.s_addr);
+
+ a.s_addr = 0;
+ ASSERT_EQ(1, inet_aton("036", &a));
+ ASSERT_EQ((htonl)(036U), a.s_addr);
+}
+
+TEST(arpa_inet, inet_aton_nullptr) {
+ ASSERT_EQ(0, inet_aton("", nullptr));
+ ASSERT_EQ(1, inet_aton("127.0.0.1", nullptr));
+}
+
+TEST(arpa_inet, inet_aton_invalid) {
+ ASSERT_EQ(0, inet_aton("", nullptr)); // Empty.
+ ASSERT_EQ(0, inet_aton("x", nullptr)); // Leading junk.
+ ASSERT_EQ(0, inet_aton("127.0.0.1x", nullptr)); // Trailing junk.
+ ASSERT_EQ(0, inet_aton("09.0.0.1", nullptr)); // Invalid octal.
+ ASSERT_EQ(0, inet_aton("0xg.0.0.1", nullptr)); // Invalid hex.
+
+ ASSERT_EQ(0, inet_aton("1.2.3.4.5", nullptr)); // Too many dots.
+ ASSERT_EQ(0, inet_aton("1.2.3.4.", nullptr)); // Trailing dot.
+
+ // Out of range a.b.c.d form.
+ ASSERT_EQ(0, inet_aton("999.0.0.1", nullptr));
+ ASSERT_EQ(0, inet_aton("0.999.0.1", nullptr));
+ ASSERT_EQ(0, inet_aton("0.0.999.1", nullptr));
+ ASSERT_EQ(0, inet_aton("0.0.0.999", nullptr));
+
+ // Out of range a.b.c form.
+ ASSERT_EQ(0, inet_aton("256.0.0", nullptr));
+ ASSERT_EQ(0, inet_aton("0.256.0", nullptr));
+ ASSERT_EQ(0, inet_aton("0.0.0x10000", nullptr));
+
+ // Out of range a.b form.
+ ASSERT_EQ(0, inet_aton("256.0", nullptr));
+ ASSERT_EQ(0, inet_aton("0.0x1000000", nullptr));
+
+ // Out of range a form.
+ ASSERT_EQ(0, inet_aton("0x100000000", nullptr));
+
+ // 64-bit overflow.
+ ASSERT_EQ(0, inet_aton("0x10000000000000000", nullptr));
+
+ // Out of range octal.
+ ASSERT_EQ(0, inet_aton("0400.0.0.1", nullptr));
}
TEST(arpa_inet, inet_lnaof) {
@@ -45,6 +122,8 @@
TEST(arpa_inet, inet_network) {
ASSERT_EQ(0x7f000001U, inet_network("127.0.0.1"));
+ ASSERT_EQ(0x7fU, inet_network("0x7f"));
+ ASSERT_EQ(~0U, inet_network(""));
}
TEST(arpa_inet, inet_ntoa) {
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index 9d8b71d..d5a5e56 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -59,6 +59,7 @@
#define LIBPATH LIBPATH_PREFIX "libdlext_test_fd/libdlext_test_fd.so"
#define LIBZIPPATH LIBPATH_PREFIX "libdlext_test_zip/libdlext_test_zip_zipaligned.zip"
+#define LIBZIPPATH_WITH_RUNPATH LIBPATH_PREFIX "libdlext_test_runpath_zip/libdlext_test_runpath_zip_zipaligned.zip"
#define LIBZIP_OFFSET PAGE_SIZE
@@ -228,6 +229,24 @@
dlclose(handle);
}
+TEST(dlfcn, dlopen_from_zip_with_dt_runpath) {
+ const std::string lib_path = std::string(getenv("ANDROID_DATA")) + LIBZIPPATH_WITH_RUNPATH;
+
+ void* handle = dlopen((lib_path + "!/libdir/libtest_dt_runpath_d_zip.so").c_str(), RTLD_NOW);
+
+ ASSERT_TRUE(handle != nullptr) << dlerror();
+
+ typedef void *(* dlopen_b_fn)();
+ dlopen_b_fn fn = (dlopen_b_fn)dlsym(handle, "dlopen_b");
+ ASSERT_TRUE(fn != nullptr) << dlerror();
+
+ void *p = fn();
+ ASSERT_TRUE(p != nullptr) << dlerror();
+
+ dlclose(p);
+ dlclose(handle);
+}
+
TEST(dlfcn, dlopen_from_zip_ld_library_path) {
const std::string lib_path = std::string(getenv("ANDROID_DATA")) + LIBZIPPATH + "!/libdir";
diff --git a/tests/gtest_main.cpp b/tests/gtest_main.cpp
index b79c8aa..3b9f6b9 100644
--- a/tests/gtest_main.cpp
+++ b/tests/gtest_main.cpp
@@ -100,7 +100,7 @@
" Don't use isolation mode, run all tests in a single process.\n"
" --deadline=[TIME_IN_MS]\n"
" Run each test in no longer than [TIME_IN_MS] time.\n"
- " It takes effect only in isolation mode. Deafult deadline is 60000 ms.\n"
+ " It takes effect only in isolation mode. Deafult deadline is 90000 ms.\n"
" --warnline=[TIME_IN_MS]\n"
" Test running longer than [TIME_IN_MS] will be warned.\n"
" It takes effect only in isolation mode. Default warnline is 2000 ms.\n"
@@ -878,11 +878,7 @@
}
static size_t GetDefaultJobCount() {
-#if defined(JOB_COUNT_FIXED)
- return JOB_COUNT_FIXED;
-#else
return static_cast<size_t>(sysconf(_SC_NPROCESSORS_ONLN));
-#endif
}
static void AddPathSeparatorInTestProgramPath(std::vector<char*>& args) {
diff --git a/tests/libs/Android.build.dlext_testzip.mk b/tests/libs/Android.build.dlext_testzip.mk
index 93df213..c06f4a9 100644
--- a/tests/libs/Android.build.dlext_testzip.mk
+++ b/tests/libs/Android.build.dlext_testzip.mk
@@ -34,8 +34,49 @@
$($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libatest_simple_zip.so
$(LOCAL_BUILT_MODULE) : $(my_shared_libs) | $(ZIPALIGN)
- @echo "Zipalign $(PRIVATE_ALIGNMENT): $@"
+ @echo "Zipalign: $@"
$(hide) rm -rf $(dir $@) && mkdir -p $(dir $@)/libdir
$(hide) cp $^ $(dir $@)/libdir
$(hide) (cd $(dir $@) && touch empty_file.txt && zip -qrD0 $(notdir $@).unaligned empty_file.txt libdir/*.so)
$(hide) $(ZIPALIGN) -p 4 $@.unaligned $@
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_MODULE := libdlext_test_runpath_zip_zipaligned
+LOCAL_MODULE_SUFFIX := .zip
+LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_PATH := $($(bionic_2nd_arch_prefix)TARGET_OUT_DATA_NATIVE_TESTS)/libdlext_test_runpath_zip
+LOCAL_2ND_ARCH_VAR_PREFIX := $(bionic_2nd_arch_prefix)
+
+include $(BUILD_SYSTEM)/base_rules.mk
+my_shared_libs := \
+ $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_d_zip.so \
+ $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_b.so \
+ $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_a.so \
+ $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_c.so \
+ $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_x.so
+
+
+$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_D := \
+ $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_d_zip.so
+$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_A := \
+ $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_a.so
+$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_B := \
+ $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_b.so
+$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_C := \
+ $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_c.so
+$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_X := \
+ $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_x.so
+$(LOCAL_BUILT_MODULE) : $(my_shared_libs) | $(ZIPALIGN)
+ @echo "Zipalign: $@"
+ $(hide) rm -rf $(dir $@) && mkdir -p $(dir $@)/libdir && \
+ mkdir -p $(dir $@)/libdir/dt_runpath_a && mkdir -p $(dir $@)/libdir/dt_runpath_b_c_x
+ $(hide) cp $(PRIVATE_LIB_D) $(dir $@)/libdir
+ $(hide) cp $(PRIVATE_LIB_A) $(dir $@)/libdir/dt_runpath_a
+ $(hide) cp $(PRIVATE_LIB_B) $(dir $@)/libdir/dt_runpath_b_c_x
+ $(hide) cp $(PRIVATE_LIB_C) $(dir $@)/libdir/dt_runpath_b_c_x
+ $(hide) cp $(PRIVATE_LIB_X) $(dir $@)/libdir/dt_runpath_b_c_x
+ $(hide) (cd $(dir $@) && touch empty_file.txt && zip -qrD0 $(notdir $@).unaligned empty_file.txt libdir)
+ $(hide) $(ZIPALIGN) -p 4 $@.unaligned $@
+
diff --git a/tests/libs/Android.build.dt_runpath.mk b/tests/libs/Android.build.dt_runpath.mk
index 44c8125..4544bb1 100644
--- a/tests/libs/Android.build.dt_runpath.mk
+++ b/tests/libs/Android.build.dt_runpath.mk
@@ -18,6 +18,19 @@
# Libraries used by dt_runpath tests.
# -----------------------------------------------------------------------------
+#
+# Dependencies
+#
+# libtest_dt_runpath_d.so runpath: ${ORIGIN}/dt_runpath_b_c_x
+# |-> dt_runpath_b_c_x/libtest_dt_runpath_b.so runpath: ${ORIGIN}/../dt_runpath_a
+# | |-> dt_runpath_a/libtest_dt_runpath_a.so
+# |-> dt_runpath_b_c_x/libtest_dt_runpath_c.so runpath: ${ORIGIN}/invalid_dt_runpath
+# | |-> libtest_dt_runpath_a.so (soname)
+#
+# This one is used to test dlopen
+# dt_runpath_b_c_x/libtest_dt_runpath_x.so
+#
+
# A leaf library in a non-standard directory.
libtest_dt_runpath_a_src_files := \
empty.cpp
@@ -57,6 +70,20 @@
module := libtest_dt_runpath_d
include $(LOCAL_PATH)/Android.build.testlib.mk
+# D version for open-from-zip test with runpath
+libtest_dt_runpath_d_zip_src_files := \
+ dlopen_b.cpp
+
+libtest_dt_runpath_d_zip_shared_libraries := libtest_dt_runpath_b libtest_dt_runpath_c
+libtest_dt_runpath_d_zip_ldflags := -Wl,--rpath,\$${ORIGIN}/dt_runpath_b_c_x -Wl,--enable-new-dtags
+libtest_dt_runpath_d_zip_install_to_out_data := true
+module := libtest_dt_runpath_d_zip
+module_tag := optional
+build_type := target
+build_target := SHARED_LIBRARY
+include $(TEST_PATH)/Android.build.mk
+
+
# A leaf library in a directory library D has DT_RUNPATH for.
libtest_dt_runpath_x_src_files := \
empty.cpp
@@ -64,3 +91,4 @@
libtest_dt_runpath_x_relative_path := dt_runpath_b_c_x
module := libtest_dt_runpath_x
include $(LOCAL_PATH)/Android.build.testlib.mk
+
diff --git a/tests/libs/dlopen_b.cpp b/tests/libs/dlopen_b.cpp
index 5291d81..cd81e16 100644
--- a/tests/libs/dlopen_b.cpp
+++ b/tests/libs/dlopen_b.cpp
@@ -4,8 +4,9 @@
// remove once it is fixed
static int dummy = 0;
- // This is not supposed to succeed. Even though this library has DT_RUNPATH
- // for libtest_dt_runpath_x.so, it is not taked into account for dlopen.
+ // This is supposed to succeed because this library has DT_RUNPATH
+ // for libtest_dt_runpath_x.so which should be taken into account
+ // by dlopen.
void *handle = dlopen("libtest_dt_runpath_x.so", RTLD_NOW);
if (handle != nullptr) {
dummy++;
diff --git a/tests/setjmp_test.cpp b/tests/setjmp_test.cpp
index 944dac8..c75ab51 100644
--- a/tests/setjmp_test.cpp
+++ b/tests/setjmp_test.cpp
@@ -221,15 +221,24 @@
#define __JB_SIGFLAG 7
#elif defined(__x86_64)
#define __JB_SIGFLAG 8
+#elif defined(__mips__) && defined(__LP64__)
+#define __JB_SIGFLAG 1
+#elif defined(__mips__)
+#define __JB_SIGFLAG 2
#endif
TEST(setjmp, setjmp_cookie) {
-#if !defined(__mips__)
jmp_buf jb;
int value = setjmp(jb);
ASSERT_EQ(0, value);
+#if defined(__mips__) && !defined(__LP64__)
+ // round address to 8-byte boundry
+ uintptr_t jb_aligned = reinterpret_cast<uintptr_t>(jb) & ~7L;
+ long* sigflag = reinterpret_cast<long*>(jb_aligned) + __JB_SIGFLAG;
+#else
long* sigflag = reinterpret_cast<long*>(jb) + __JB_SIGFLAG;
+#endif
// Make sure there's actually a cookie.
EXPECT_NE(0, *sigflag & ~1);
@@ -237,5 +246,4 @@
// Wipe it out
*sigflag &= 1;
EXPECT_DEATH(longjmp(jb, 0), "");
-#endif
}
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index 62677cd..afb1511 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -1012,3 +1012,39 @@
fclose(fp);
}
+
+// https://code.google.com/p/android/issues/detail?id=184847
+TEST(stdio, fread_EOF_184847) {
+ TemporaryFile tf;
+ char buf[6] = {0};
+
+ FILE* fw = fopen(tf.filename, "w");
+ ASSERT_TRUE(fw != nullptr);
+
+ FILE* fr = fopen(tf.filename, "r");
+ ASSERT_TRUE(fr != nullptr);
+
+ fwrite("a", 1, 1, fw);
+ fflush(fw);
+ ASSERT_EQ(1U, fread(buf, 1, 1, fr));
+ ASSERT_STREQ("a", buf);
+
+ // 'fr' is now at EOF.
+ ASSERT_EQ(0U, fread(buf, 1, 1, fr));
+ ASSERT_TRUE(feof(fr));
+
+ // Write some more...
+ fwrite("z", 1, 1, fw);
+ fflush(fw);
+
+ // ...and check that we can read it back.
+ // (BSD thinks that once a stream has hit EOF, it must always return EOF. SysV disagrees.)
+ ASSERT_EQ(1U, fread(buf, 1, 1, fr));
+ ASSERT_STREQ("z", buf);
+
+ // But now we're done.
+ ASSERT_EQ(0U, fread(buf, 1, 1, fr));
+
+ fclose(fr);
+ fclose(fw);
+}
diff --git a/tests/time_test.cpp b/tests/time_test.cpp
index 052997a..c685e94 100644
--- a/tests/time_test.cpp
+++ b/tests/time_test.cpp
@@ -296,8 +296,8 @@
Counter counter(Counter::CountAndDisarmNotifyFunction);
ASSERT_EQ(0, counter.Value());
- counter.SetTime(0, 1, 1, 0);
- usleep(500000);
+ counter.SetTime(0, 500000000, 1, 0);
+ sleep(1);
// The count should just be 1 because we disarmed the timer the first time it fired.
ASSERT_EQ(1, counter.Value());
@@ -381,8 +381,8 @@
ASSERT_EQ(0, counter2.Value());
ASSERT_EQ(0, counter3.Value());
- counter2.SetTime(0, 1, 0, 0);
- usleep(500000);
+ counter2.SetTime(0, 500000000, 0, 0);
+ sleep(1);
EXPECT_EQ(0, counter1.Value());
EXPECT_EQ(1, counter2.Value());