Author: ed Date: Wed Jun 24 03:44:38 2015 New Revision: 240527 URL: http://llvm.org/viewvc/llvm-project?rev=240527&view=rev Log: Make support for thread-unsafe C functions optional.
One of the aspects of CloudABI is that it aims to help you write code that is thread-safe out of the box. This is very important if you want to write libraries that are easy to reuse. For CloudABI we decided to not provide the thread-unsafe functions. So far this is working out pretty well, as thread-unsafety issues are detected really early on. The following patch adds a knob to libc++, _LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS, that can be set to disable thread-unsafe functions that can easily be avoided in practice. The following functions are not thread-safe: - <clocale>: locale handles should be preferred over setlocale(). - <cstdlib>: mbrlen(), mbrtowc() and wcrtomb() should be preferred over their non-restartable counterparts. - <ctime>: asctime(), ctime(), gmtime() and localtime() are not thread-safe. The first two are also deprecated by POSIX. Differential Revision: http://reviews.llvm.org/D8703 Reviewed by: marshall Added: libcxx/trunk/test/std/utilities/date.time/asctime.thread-unsafe.fail.cpp libcxx/trunk/test/std/utilities/date.time/ctime.thread-unsafe.fail.cpp libcxx/trunk/test/std/utilities/date.time/gmtime.thread-unsafe.fail.cpp libcxx/trunk/test/std/utilities/date.time/localtime.thread-unsafe.fail.cpp Modified: libcxx/trunk/CMakeLists.txt libcxx/trunk/include/__config libcxx/trunk/include/clocale libcxx/trunk/include/cstdlib libcxx/trunk/include/cstring libcxx/trunk/include/ctime libcxx/trunk/test/CMakeLists.txt libcxx/trunk/test/libcxx/test/config.py libcxx/trunk/test/lit.site.cfg.in libcxx/trunk/test/std/depr/depr.c.headers/stdlib_h.pass.cpp libcxx/trunk/test/std/depr/depr.c.headers/string_h.pass.cpp libcxx/trunk/test/std/language.support/support.runtime/cstdlib.pass.cpp libcxx/trunk/test/std/language.support/support.runtime/ctime.pass.cpp libcxx/trunk/test/std/localization/c.locales/clocale.pass.cpp libcxx/trunk/test/std/strings/c.strings/cstring.pass.cpp Modified: libcxx/trunk/CMakeLists.txt URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/CMakeLists.txt?rev=240527&r1=240526&r2=240527&view=diff ============================================================================== --- libcxx/trunk/CMakeLists.txt (original) +++ libcxx/trunk/CMakeLists.txt Wed Jun 24 03:44:38 2015 @@ -58,6 +58,7 @@ option(LIBCXX_ENABLE_GLOBAL_FILESYSTEM_N option(LIBCXX_ENABLE_STDIN "Build libc++ with support for stdin/std::cin." ON) option(LIBCXX_ENABLE_STDOUT "Build libc++ with support for stdout/std::cout." ON) option(LIBCXX_ENABLE_THREADS "Build libc++ with support for threads." ON) +option(LIBCXX_ENABLE_THREAD_UNSAFE_C_FUNCTIONS "Build libc++ with support for thread-unsafe C functions" ON) option(LIBCXX_BUILD_32_BITS "Build 32 bit libc++" OFF) option(LIBCXX_ENABLE_MONOTONIC_CLOCK "Build libc++ with support for a monotonic clock. @@ -274,6 +275,11 @@ elseif(NOT LIBCXX_ENABLE_MONOTONIC_CLOCK " when LIBCXX_ENABLE_THREADS is also set to OFF.") endif() +# LIBCXX_ENABLE_THREAD_UNSAFE_C_FUNCTIONS configuration +if (NOT LIBCXX_ENABLE_THREAD_UNSAFE_C_FUNCTIONS) + add_definitions(-D_LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS) +endif() + # Configure for sanitizers. If LIBCXX_BUILT_STANDALONE then we have to do # the flag translation ourselves. Othewise LLVM's CMakeList.txt will handle it. if (LIBCXX_BUILT_STANDALONE) Modified: libcxx/trunk/include/__config URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/__config?rev=240527&r1=240526&r2=240527&view=diff ============================================================================== --- libcxx/trunk/include/__config (original) +++ libcxx/trunk/include/__config Wed Jun 24 03:44:38 2015 @@ -766,4 +766,10 @@ extern "C" void __sanitizer_annotate_con #define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE #endif +// Thread-unsafe functions such as strtok(), mbtowc() and localtime() +// are not available. +#ifdef __CloudABI__ +#define _LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS +#endif + #endif // _LIBCPP_CONFIG Modified: libcxx/trunk/include/clocale URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/clocale?rev=240527&r1=240526&r2=240527&view=diff ============================================================================== --- libcxx/trunk/include/clocale (original) +++ libcxx/trunk/include/clocale Wed Jun 24 03:44:38 2015 @@ -45,7 +45,9 @@ lconv* localeconv(); _LIBCPP_BEGIN_NAMESPACE_STD using ::lconv; +#ifndef _LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS using ::setlocale; +#endif using ::localeconv; _LIBCPP_END_NAMESPACE_STD Modified: libcxx/trunk/include/cstdlib URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/cstdlib?rev=240527&r1=240526&r2=240527&view=diff ============================================================================== --- libcxx/trunk/include/cstdlib (original) +++ libcxx/trunk/include/cstdlib Wed Jun 24 03:44:38 2015 @@ -147,9 +147,11 @@ using ::ldiv; #undef lldiv using ::lldiv; #endif // _LIBCPP_HAS_NO_LONG_LONG +#ifndef _LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS using ::mblen; using ::mbtowc; using ::wctomb; +#endif using ::mbstowcs; using ::wcstombs; #ifdef _LIBCPP_HAS_QUICK_EXIT Modified: libcxx/trunk/include/cstring URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/cstring?rev=240527&r1=240526&r2=240527&view=diff ============================================================================== --- libcxx/trunk/include/cstring (original) +++ libcxx/trunk/include/cstring Wed Jun 24 03:44:38 2015 @@ -102,7 +102,9 @@ inline _LIBCPP_INLINE_VISIBILITY v inline _LIBCPP_INLINE_VISIBILITY char* strstr( char* __s1, const char* __s2) {return ::strstr(__s1, __s2);} #endif +#ifndef _LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS using ::strtok; +#endif using ::memset; using ::strerror; using ::strlen; Modified: libcxx/trunk/include/ctime URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/ctime?rev=240527&r1=240526&r2=240527&view=diff ============================================================================== --- libcxx/trunk/include/ctime (original) +++ libcxx/trunk/include/ctime Wed Jun 24 03:44:38 2015 @@ -61,10 +61,12 @@ using ::clock; using ::difftime; using ::mktime; using ::time; +#ifndef _LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS using ::asctime; using ::ctime; using ::gmtime; using ::localtime; +#endif using ::strftime; _LIBCPP_END_NAMESPACE_STD Modified: libcxx/trunk/test/CMakeLists.txt URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/CMakeLists.txt?rev=240527&r1=240526&r2=240527&view=diff ============================================================================== --- libcxx/trunk/test/CMakeLists.txt (original) +++ libcxx/trunk/test/CMakeLists.txt Wed Jun 24 03:44:38 2015 @@ -46,6 +46,7 @@ if (LIT_EXECUTABLE) pythonize_bool(LIBCXX_ENABLE_STDIN) pythonize_bool(LIBCXX_ENABLE_STDOUT) pythonize_bool(LIBCXX_ENABLE_THREADS) + pythonize_bool(LIBCXX_ENABLE_THREAD_UNSAFE_C_FUNCTIONS) pythonize_bool(LIBCXX_ENABLE_MONOTONIC_CLOCK) pythonize_bool(LIBCXX_GENERATE_COVERAGE) pythonize_bool(LIBCXXABI_USE_LLVM_UNWINDER) Modified: libcxx/trunk/test/libcxx/test/config.py URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/libcxx/test/config.py?rev=240527&r1=240526&r2=240527&view=diff ============================================================================== --- libcxx/trunk/test/libcxx/test/config.py (original) +++ libcxx/trunk/test/libcxx/test/config.py Wed Jun 24 03:44:38 2015 @@ -372,6 +372,8 @@ class Configuration(object): elif not enable_monotonic_clock: self.lit_config.fatal('enable_monotonic_clock cannot be false when' ' enable_threads is true.') + self.configure_compile_flags_no_thread_unsafe_c_functions() + # Use verbose output for better errors self.cxx.flags += ['-v'] sysroot = self.get_lit_conf('sysroot') @@ -431,6 +433,15 @@ class Configuration(object): self.cxx.compile_flags += ['-D_LIBCPP_HAS_NO_THREADS'] self.config.available_features.add('libcpp-has-no-threads') + def configure_compile_flags_no_thread_unsafe_c_functions(self): + enable_thread_unsafe_c_functions = self.get_lit_bool( + 'enable_thread_unsafe_c_functions', True) + if not enable_thread_unsafe_c_functions: + self.cxx.compile_flags += [ + '-D_LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS'] + self.config.available_features.add( + 'libcpp-has-no-thread-unsafe-c-functions') + def configure_compile_flags_no_monotonic_clock(self): self.cxx.compile_flags += ['-D_LIBCPP_HAS_NO_MONOTONIC_CLOCK'] self.config.available_features.add('libcpp-has-no-monotonic-clock') @@ -522,7 +533,7 @@ class Configuration(object): else: self.cxx.link_flags += ['-lgcc_s'] elif target_platform.startswith('freebsd'): - self.cxx.link_flags += ['-lc', '-lm', '-lpthread', '-lgcc_s'] + self.cxx.link_flags += ['-lc', '-lm', '-lpthread', '-lgcc_s', '-lcxxrt'] else: self.lit_config.fatal("unrecognized system: %r" % target_platform) Modified: libcxx/trunk/test/lit.site.cfg.in URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/lit.site.cfg.in?rev=240527&r1=240526&r2=240527&view=diff ============================================================================== --- libcxx/trunk/test/lit.site.cfg.in (original) +++ libcxx/trunk/test/lit.site.cfg.in Wed Jun 24 03:44:38 2015 @@ -12,6 +12,7 @@ config.enable_global_filesystem_namespac config.enable_stdin = "@LIBCXX_ENABLE_STDIN@" config.enable_stdout = "@LIBCXX_ENABLE_STDOUT@" config.enable_threads = "@LIBCXX_ENABLE_THREADS@" +config.enable_thread_unsafe_c_functions = "@LIBCXX_ENABLE_THREAD_UNSAFE_C_FUNCTIONS@" config.enable_monotonic_clock = "@LIBCXX_ENABLE_MONOTONIC_CLOCK@" config.cxx_abi = "@LIBCXX_CXX_ABI_LIBNAME@" config.use_sanitizer = "@LLVM_USE_SANITIZER@" Modified: libcxx/trunk/test/std/depr/depr.c.headers/stdlib_h.pass.cpp URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/depr/depr.c.headers/stdlib_h.pass.cpp?rev=240527&r1=240526&r2=240527&view=diff ============================================================================== --- libcxx/trunk/test/std/depr/depr.c.headers/stdlib_h.pass.cpp (original) +++ libcxx/trunk/test/std/depr/depr.c.headers/stdlib_h.pass.cpp Wed Jun 24 03:44:38 2015 @@ -71,12 +71,14 @@ int main() static_assert((std::is_same<decltype(div(0,0)), div_t>::value), ""); static_assert((std::is_same<decltype(ldiv(0L,0L)), ldiv_t>::value), ""); static_assert((std::is_same<decltype(lldiv(0LL,0LL)), lldiv_t>::value), ""); - static_assert((std::is_same<decltype(mblen("",0)), int>::value), ""); wchar_t* pw = 0; const wchar_t* pwc = 0; char* pc = 0; +#ifndef _LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS + static_assert((std::is_same<decltype(mblen("",0)), int>::value), ""); static_assert((std::is_same<decltype(mbtowc(pw,"",0)), int>::value), ""); static_assert((std::is_same<decltype(wctomb(pc,L' ')), int>::value), ""); +#endif static_assert((std::is_same<decltype(mbstowcs(pw,"",0)), size_t>::value), ""); static_assert((std::is_same<decltype(wcstombs(pc,pwc,0)), size_t>::value), ""); } Modified: libcxx/trunk/test/std/depr/depr.c.headers/string_h.pass.cpp URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/depr/depr.c.headers/string_h.pass.cpp?rev=240527&r1=240526&r2=240527&view=diff ============================================================================== --- libcxx/trunk/test/std/depr/depr.c.headers/string_h.pass.cpp (original) +++ libcxx/trunk/test/std/depr/depr.c.headers/string_h.pass.cpp Wed Jun 24 03:44:38 2015 @@ -41,7 +41,9 @@ int main() static_assert((std::is_same<decltype(strrchr(cp, 0)), char*>::value), ""); static_assert((std::is_same<decltype(strspn(cpc, cpc)), size_t>::value), ""); static_assert((std::is_same<decltype(strstr(cp, cpc)), char*>::value), ""); +#ifndef _LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS static_assert((std::is_same<decltype(strtok(cp, cpc)), char*>::value), ""); +#endif static_assert((std::is_same<decltype(memset(vp, 0, s)), void*>::value), ""); static_assert((std::is_same<decltype(strerror(0)), char*>::value), ""); static_assert((std::is_same<decltype(strlen(cpc)), size_t>::value), ""); Modified: libcxx/trunk/test/std/language.support/support.runtime/cstdlib.pass.cpp URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/language.support/support.runtime/cstdlib.pass.cpp?rev=240527&r1=240526&r2=240527&view=diff ============================================================================== --- libcxx/trunk/test/std/language.support/support.runtime/cstdlib.pass.cpp (original) +++ libcxx/trunk/test/std/language.support/support.runtime/cstdlib.pass.cpp Wed Jun 24 03:44:38 2015 @@ -75,12 +75,14 @@ int main() static_assert((std::is_same<decltype(std::div(0LL,0LL)), std::lldiv_t>::value), ""); static_assert((std::is_same<decltype(std::ldiv(0L,0L)), std::ldiv_t>::value), ""); static_assert((std::is_same<decltype(std::lldiv(0LL,0LL)), std::lldiv_t>::value), ""); - static_assert((std::is_same<decltype(std::mblen("",0)), int>::value), ""); wchar_t* pw = 0; const wchar_t* pwc = 0; char* pc = 0; +#ifndef _LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS + static_assert((std::is_same<decltype(std::mblen("",0)), int>::value), ""); static_assert((std::is_same<decltype(std::mbtowc(pw,"",0)), int>::value), ""); static_assert((std::is_same<decltype(std::wctomb(pc,L' ')), int>::value), ""); +#endif static_assert((std::is_same<decltype(std::mbstowcs(pw,"",0)), std::size_t>::value), ""); static_assert((std::is_same<decltype(std::wcstombs(pc,pwc,0)), std::size_t>::value), ""); } Modified: libcxx/trunk/test/std/language.support/support.runtime/ctime.pass.cpp URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/language.support/support.runtime/ctime.pass.cpp?rev=240527&r1=240526&r2=240527&view=diff ============================================================================== --- libcxx/trunk/test/std/language.support/support.runtime/ctime.pass.cpp (original) +++ libcxx/trunk/test/std/language.support/support.runtime/ctime.pass.cpp Wed Jun 24 03:44:38 2015 @@ -30,10 +30,12 @@ int main() static_assert((std::is_same<decltype(std::difftime(t,t)), double>::value), ""); static_assert((std::is_same<decltype(std::mktime(&tm)), std::time_t>::value), ""); static_assert((std::is_same<decltype(std::time(&t)), std::time_t>::value), ""); +#ifndef _LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS static_assert((std::is_same<decltype(std::asctime(&tm)), char*>::value), ""); static_assert((std::is_same<decltype(std::ctime(&t)), char*>::value), ""); static_assert((std::is_same<decltype(std::gmtime(&t)), std::tm*>::value), ""); static_assert((std::is_same<decltype(std::localtime(&t)), std::tm*>::value), ""); +#endif char* c1 = 0; const char* c2 = 0; static_assert((std::is_same<decltype(std::strftime(c1,s,c2,&tm)), std::size_t>::value), ""); Modified: libcxx/trunk/test/std/localization/c.locales/clocale.pass.cpp URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/localization/c.locales/clocale.pass.cpp?rev=240527&r1=240526&r2=240527&view=diff ============================================================================== --- libcxx/trunk/test/std/localization/c.locales/clocale.pass.cpp (original) +++ libcxx/trunk/test/std/localization/c.locales/clocale.pass.cpp Wed Jun 24 03:44:38 2015 @@ -12,6 +12,8 @@ #include <clocale> #include <type_traits> +#ifndef _LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS + #ifndef LC_ALL #error LC_ALL not defined #endif @@ -36,6 +38,8 @@ #error LC_TIME not defined #endif +#endif // !_LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS + #ifndef NULL #error NULL not defined #endif @@ -43,6 +47,8 @@ int main() { std::lconv lc; +#ifndef _LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS static_assert((std::is_same<decltype(std::setlocale(0, "")), char*>::value), ""); +#endif static_assert((std::is_same<decltype(std::localeconv()), std::lconv*>::value), ""); } Modified: libcxx/trunk/test/std/strings/c.strings/cstring.pass.cpp URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/strings/c.strings/cstring.pass.cpp?rev=240527&r1=240526&r2=240527&view=diff ============================================================================== --- libcxx/trunk/test/std/strings/c.strings/cstring.pass.cpp (original) +++ libcxx/trunk/test/std/strings/c.strings/cstring.pass.cpp Wed Jun 24 03:44:38 2015 @@ -46,7 +46,9 @@ int main() static_assert((std::is_same<decltype(std::strspn(cpc, cpc)), std::size_t>::value), ""); // static_assert((std::is_same<decltype(std::strstr(cpc, cpc)), const char*>::value), ""); static_assert((std::is_same<decltype(std::strstr(cp, cpc)), char*>::value), ""); +#ifndef _LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS static_assert((std::is_same<decltype(std::strtok(cp, cpc)), char*>::value), ""); +#endif static_assert((std::is_same<decltype(std::memset(vp, 0, s)), void*>::value), ""); static_assert((std::is_same<decltype(std::strerror(0)), char*>::value), ""); static_assert((std::is_same<decltype(std::strlen(cpc)), std::size_t>::value), ""); Added: libcxx/trunk/test/std/utilities/date.time/asctime.thread-unsafe.fail.cpp URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/utilities/date.time/asctime.thread-unsafe.fail.cpp?rev=240527&view=auto ============================================================================== --- libcxx/trunk/test/std/utilities/date.time/asctime.thread-unsafe.fail.cpp (added) +++ libcxx/trunk/test/std/utilities/date.time/asctime.thread-unsafe.fail.cpp Wed Jun 24 03:44:38 2015 @@ -0,0 +1,18 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// REQUIRES: libcpp-has-no-thread-unsafe-c-functions + +#include <ctime> + +int main() { + // asctime is not thread-safe. + std::time_t t = 0; + std::asctime(&t); +} Added: libcxx/trunk/test/std/utilities/date.time/ctime.thread-unsafe.fail.cpp URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/utilities/date.time/ctime.thread-unsafe.fail.cpp?rev=240527&view=auto ============================================================================== --- libcxx/trunk/test/std/utilities/date.time/ctime.thread-unsafe.fail.cpp (added) +++ libcxx/trunk/test/std/utilities/date.time/ctime.thread-unsafe.fail.cpp Wed Jun 24 03:44:38 2015 @@ -0,0 +1,18 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// REQUIRES: libcpp-has-no-thread-unsafe-c-functions + +#include <ctime> + +int main() { + // ctime is not thread-safe. + std::time_t t = 0; + std::ctime(&t); +} Added: libcxx/trunk/test/std/utilities/date.time/gmtime.thread-unsafe.fail.cpp URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/utilities/date.time/gmtime.thread-unsafe.fail.cpp?rev=240527&view=auto ============================================================================== --- libcxx/trunk/test/std/utilities/date.time/gmtime.thread-unsafe.fail.cpp (added) +++ libcxx/trunk/test/std/utilities/date.time/gmtime.thread-unsafe.fail.cpp Wed Jun 24 03:44:38 2015 @@ -0,0 +1,18 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// REQUIRES: libcpp-has-no-thread-unsafe-c-functions + +#include <ctime> + +int main() { + // gmtime is not thread-safe. + std::time_t t = 0; + std::gmtime(&t); +} Added: libcxx/trunk/test/std/utilities/date.time/localtime.thread-unsafe.fail.cpp URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/utilities/date.time/localtime.thread-unsafe.fail.cpp?rev=240527&view=auto ============================================================================== --- libcxx/trunk/test/std/utilities/date.time/localtime.thread-unsafe.fail.cpp (added) +++ libcxx/trunk/test/std/utilities/date.time/localtime.thread-unsafe.fail.cpp Wed Jun 24 03:44:38 2015 @@ -0,0 +1,18 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// REQUIRES: libcpp-has-no-thread-unsafe-c-functions + +#include <ctime> + +int main() { + // localtime is not thread-safe. + std::time_t t = 0; + std::localtime(&t); +} _______________________________________________ cfe-commits mailing list [email protected] http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
