sl@0: /************************************************************************ sl@0: * sl@0: * driver.cpp - definitions of the test driver sl@0: * sl@0: * $Id: driver.cpp 290009 2005-09-18 23:28:26Z sebor $ sl@0: * sl@0: ************************************************************************ sl@0: * sl@0: * Copyright (c) 1994-2005 Quovadx, Inc., acting through its Rogue Wave sl@0: * Software division. Licensed under the Apache License, Version 2.0 (the sl@0: * "License"); you may not use this file except in compliance with the sl@0: * License. You may obtain a copy of the License at sl@0: * http://www.apache.org/licenses/LICENSE-2.0. Unless required by sl@0: * applicable law or agreed to in writing, software distributed under sl@0: * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR sl@0: * CONDITIONS OF ANY KIND, either express or implied. See the License sl@0: * for the specific language governing permissions and limitations under sl@0: * the License. sl@0: * sl@0: **************************************************************************/ sl@0: sl@0: // expand _TEST_EXPORT macros sl@0: #define _RWSTD_TEST_SRC sl@0: sl@0: #include "opt_diags.h" sl@0: #include "opt_lines.h" sl@0: #include "opt_trace.h" sl@0: #include "opt_types.h" sl@0: sl@0: #include // for rw_setopts() sl@0: #include // for rw_snprintfa() sl@0: sl@0: #include // for assert sl@0: #include // for longjmp, setjmp, ... sl@0: #include // for va_list sl@0: #include // for fileno, fprintf sl@0: #include // for free sl@0: #include // for strchr, strcpy sl@0: #include"std_log_result.h" sl@0: #define LOG_FILENAME_LINE __FILE__, __LINE__ sl@0: #if !defined (_WIN32) && !defined (_WIN64) sl@0: # include // for isatty sl@0: sl@0: // declare fileno in case it's not declared (for strict ANSI conformance) sl@0: extern "C" { sl@0: sl@0: IMPORT_C int (fileno)(FILE*) _LIBC_THROWS (); sl@0: sl@0: } // extern "C" sl@0: sl@0: #else sl@0: // no isatty on Windoze sl@0: # define _RWSTD_NO_ISATTY sl@0: #endif // _WIN{32,64} sl@0: sl@0: // expand _TEST_EXPORT macros sl@0: #define _RWSTD_TEST_SRC sl@0: #include sl@0: sl@0: /************************************************************************/ sl@0: sl@0: #define RW_TEST_STRSTR(x) #x sl@0: #define RW_TEST_STR(x) RW_TEST_STRSTR(x) sl@0: sl@0: #ifndef RW_TEST_COMPILER sl@0: # if defined (__DECCXX__) sl@0: # define RW_TEST_COMPILER "Compaq C++, __DECCXX__ = " \ sl@0: RW_TEST_STR (__DECCXX__) sl@0: # elif defined (__INTEL_COMPILER) sl@0: # if defined (__EDG_VERSION__) sl@0: # define RW_TEST_ICC_EDG_VER \ sl@0: ", __EDG_VERSION__ = " RW_TEST_STR (__EDG_VERSION__) sl@0: # else sl@0: # define RW_TEST_ICC_EDG_VER "" sl@0: # endif sl@0: # if defined (_MSC_VER) sl@0: # define RW_TEST_COMPILER "Intel C++, __INTEL_COMPILER = " \ sl@0: RW_TEST_STR (__INTEL_COMPILER) ", _MSC_VER = " \ sl@0: RW_TEST_STR (_MSC_VER) \ sl@0: RW_TEST_ICC_EDG_VER sl@0: # elif defined (__INTEL_COMPILER_BUILD_DATE) sl@0: # define RW_TEST_COMPILER "Intel C++, __INTEL_COMPILER = " \ sl@0: RW_TEST_STR (__INTEL_COMPILER) \ sl@0: ", __INTEL_COMPILER_BUILD_DATE = " \ sl@0: RW_TEST_STR (__INTEL_COMPILER_BUILD_DATE) \ sl@0: RW_TEST_ICC_EDG_VER sl@0: # else sl@0: # define RW_TEST_COMPILER "Intel C++, __INTEL_COMPILER = " \ sl@0: RW_TEST_STR (__INTEL_COMPILER) \ sl@0: RW_TEST_ICC_EDG_VER sl@0: # endif sl@0: # elif defined (__GNUC__) sl@0: # if defined (__VERSION__) sl@0: # define RW_TEST_GCC_VER ", __VERSION__ = \"" __VERSION__ "\"" sl@0: # else sl@0: # define RW_TEST_GCC_VER "" sl@0: # endif sl@0: # if defined (__GNUC_PATCHLEVEL__) sl@0: # define RW_TEST_COMPILER "gcc " \ sl@0: RW_TEST_STR (__GNUC__) "." \ sl@0: RW_TEST_STR (__GNUC_MINOR__) "." \ sl@0: RW_TEST_STR (__GNUC_PATCHLEVEL__) \ sl@0: RW_TEST_GCC_VER sl@0: # else sl@0: # define RW_TEST_COMPILER "gcc " \ sl@0: RW_TEST_STR (__GNUC__) "." RW_TEST_STR (__GNUC_MINOR__) sl@0: RW_TEST_GCC_VER sl@0: # endif sl@0: # elif defined (_COMPILER_VERSION) && defined (__sgi) sl@0: # define RW_TEST_COMPILER "SGI MIPSpro, _COMPILER_VERSION = " \ sl@0: RW_TEST_STR (_COMPILER_VERSION) sl@0: # elif defined (__INTEL_COMPILER) sl@0: # if defined (_MSC_VER) sl@0: # define RW_TEST_COMPILER "Intel C++, __INTEL_COMPILER = " \ sl@0: RW_TEST_STR (__INTEL_COMPILER) ", _MSC_VER = " \ sl@0: RW_TEST_STR (_MSC_VER) sl@0: # else sl@0: # define RW_TEST_COMPILER "Intel C++, __INTEL_COMPILER = " \ sl@0: RW_TEST_STR (__INTEL_COMPILER) sl@0: # endif sl@0: # elif defined (__EDG__) sl@0: # define RW_TEST_COMPILER "EDG eccp, __EDG_VERSION__ = " \ sl@0: RW_TEST_STR (__EDG_VERSION__) sl@0: # elif defined (__HP_aCC) sl@0: # define RW_TEST_COMPILER "HP aCC, __HP_aCC = " \ sl@0: RW_TEST_STR (__HP_aCC) sl@0: # elif defined (__IBMCPP__) sl@0: # define RW_TEST_COMPILER "IBM VisualAge C++, __IBMCPP__ = " \ sl@0: RW_TEST_STR (__IBMCPP__) sl@0: # elif defined (_MSC_VER) sl@0: # define RW_TEST_COMPILER "MSVC, _MSC_VER = " \ sl@0: RW_TEST_STR (_MSC_VER) sl@0: # elif defined (__SUNPRO_CC) sl@0: # define RW_TEST_COMPILER "SunPro, __SUNPRO_CC = " \ sl@0: RW_TEST_STR (__SUNPRO_CC) sl@0: # else sl@0: # define RW_TEST_COMPILER "unknown" sl@0: # endif sl@0: #endif sl@0: sl@0: #ifndef RW_TEST_LIBSTD sl@0: # ifdef _RWSTD_VER sl@0: # define RW_TEST_LIBSTD "Rogue Wave C++ Standard Library, " \ sl@0: "_RWSTD_VER = " RW_TEST_STR (_RWSTD_VER) sl@0: # elif defined (__GLIBCXX__) sl@0: # define RW_TEST_LIBSTD "GNU C++ Standard Library, " \ sl@0: "__GLIBCXX__ = " \ sl@0: RW_TEST_STR (__GLIBCXX__) sl@0: # elif defined (_STLPORT_VERSION) sl@0: // check for STLport before SGI STL since STLport, sl@0: // being derived from SGI STL, #defines both macros sl@0: # define RW_TEST_LIBSTD "STLport, " \ sl@0: "_STLPORT_VERSION = " \ sl@0: RW_TEST_STR (_STLPORT_VERSION) sl@0: # elif defined (__SGI_STL) sl@0: # define RW_TEST_LIBSTD "SGI STL, " \ sl@0: "__SGI_STL = " \ sl@0: RW_TEST_STR (__SGI_STL) sl@0: # elif defined (_YVALS) sl@0: // is there a better way to identify the Dinkumware sl@0: // implementation? does it have a version macro? sl@0: # define RW_TEST_LIBSTD "Dinkum C++ Standard Library" sl@0: # endif sl@0: #endif // RW_TEST_LIBSTD sl@0: sl@0: #ifndef RW_TEST_HARDWARE sl@0: # if defined (__alpha__) || defined (__alpha) sl@0: # define RW_TEST_ARCH "alpha" sl@0: # elif defined (__amd64__) || defined (__amd64) sl@0: # if defined (__LP64__) || defined (_LP64) sl@0: # define RW_TEST_ARCH "amd64/LP64" sl@0: # else sl@0: # define RW_TEST_ARCH "amd64/ILP32" sl@0: # endif sl@0: # elif defined (_PA_RISC2_0) sl@0: # define RW_TEST_ARCH "pa-risc 2.0" sl@0: # elif defined (_PA_RISC1_0) sl@0: # define RW_TEST_ARCH "pa-risc 1.0" sl@0: # elif defined (__hppa) sl@0: # define RW_TEST_ARCH "pa-risc" sl@0: # elif defined (__pentiumpro__) || defined (__pentiumpro) sl@0: # define RW_TEST_ARCH "pentiumpro" sl@0: # elif defined (__pentium__) || defined (__pentium) sl@0: # define RW_TEST_ARCH "pentium" sl@0: # elif defined (__i486__) || defined (__i486) sl@0: # define RW_TEST_ARCH "i486" sl@0: # elif defined (__i386__) || defined (__i386) sl@0: # define RW_TEST_ARCH "i386" sl@0: # elif defined (__i586__) || defined (__i586) sl@0: # define RW_TEST_ARCH "i586" sl@0: # elif defined (__ia64) sl@0: # define RW_TEST_ARCH "ia64" sl@0: # elif defined (__mips) sl@0: # define RW_TEST_ARCH "mips" sl@0: # elif defined (__sparcv9) sl@0: # define RW_TEST_ARCH "sparc-v9" sl@0: # elif defined (__sparcv8) sl@0: # define RW_TEST_ARCH "sparc-v8" sl@0: # elif defined (__sparc) sl@0: # define RW_TEST_ARCH "sparc" sl@0: # elif defined (_POWER) sl@0: # if defined (_ARCH_PWR5) sl@0: # define RW_TEST_ARCH "power-5" sl@0: # elif defined (_ARCH_PWR4) sl@0: # define RW_TEST_ARCH "power-4" sl@0: # elif defined (_ARCH_PWR3) sl@0: # define RW_TEST_ARCH "power-3" sl@0: # elif defined (_ARCH_604) sl@0: # define RW_TEST_ARCH "powerpc-604" sl@0: # elif defined (_ARCH_603) sl@0: # define RW_TEST_ARCH "powerpc-603" sl@0: # elif defined (_ARCH_602) sl@0: # define RW_TEST_ARCH "powerpc-602" sl@0: # elif defined (_ARCH_601) sl@0: # define RW_TEST_ARCH "powerpc-601" sl@0: # elif defined (_ARCH_403) sl@0: # define RW_TEST_ARCH "powerpc-403" sl@0: # elif defined (_ARCH_PPC64) sl@0: # define RW_TEST_ARCH "powerpc/LP64" sl@0: # else sl@0: # define RW_TEST_ARCH "powerpc" sl@0: # endif sl@0: # elif defined (_WIN64) sl@0: # define RW_TEST_ARCH "ia64" sl@0: # elif defined (_WIN32) sl@0: # define RW_TEST_ARCH "i86" sl@0: # elif defined (__x86_64__) || defined (__x86_64) sl@0: # if defined (__LP64__) || defined (_LP64) sl@0: # define RW_TEST_ARCH "x86_64/LP64" sl@0: # else sl@0: # define RW_TEST_ARCH "x86_64/ILP32" sl@0: # endif sl@0: # else sl@0: # define RW_TEST_ARCH "unknown" sl@0: # endif sl@0: sl@0: sl@0: # if defined (_AIX54) sl@0: # define RW_TEST_OS "aix-5.4 (or better)" sl@0: # elif defined (_AIX53) sl@0: # define RW_TEST_OS "aix-5.3" sl@0: # elif defined (_AIX52) sl@0: # define RW_TEST_OS "aix-5.2" sl@0: # elif defined (_AIX51) sl@0: # define RW_TEST_OS "aix-5.1" sl@0: # elif defined (_AIX50) sl@0: # define RW_TEST_OS "aix-5.0" sl@0: # elif defined (_AIX43) sl@0: # define RW_TEST_OS "aix-4.3" sl@0: # elif defined (_AIX41) sl@0: # define RW_TEST_OS "aix-4.1" sl@0: # elif defined (_AIX32) sl@0: # define RW_TEST_OS "aix-3.2" sl@0: # elif defined (_AIX) sl@0: # define RW_TEST_OS "aix" sl@0: # elif defined (__hpux) sl@0: # define RW_TEST_OS "hp-ux" sl@0: # elif defined (__osf__) sl@0: # define RW_TEST_OS "tru64-unix" sl@0: # elif defined (__sgi) && defined (__mips) sl@0: # define RW_TEST_OS "irix" sl@0: # elif defined (__linux__) || defined (__linux) sl@0: sl@0: // get Linux release string (UTS_RELEASE) sl@0: # include sl@0: sl@0: # ifndef UTS_RELEASE sl@0: # define UTS_RELEASE "(unknown release)" sl@0: # endif // UTS_RELEASE sl@0: sl@0: # if defined (__ELF__) sl@0: # define LINUX_TYPE "linux-elf" sl@0: # else sl@0: # define LINUX_TYPE "linux" sl@0: # endif sl@0: sl@0: # define RW_TEST_OS LINUX_TYPE " " \ sl@0: UTS_RELEASE " with glibc " \ sl@0: RW_TEST_STR (__GLIBC__) "." \ sl@0: RW_TEST_STR (__GLIBC_MINOR__) sl@0: sl@0: # elif defined (__SunOS_5_10) sl@0: # define RW_TEST_OS "sunos-5.10" sl@0: # elif defined (__SunOS_5_9) sl@0: # define RW_TEST_OS "sunos-5.9" sl@0: # elif defined (__SunOS_5_8) sl@0: # define RW_TEST_OS "sunos-5.8" sl@0: # elif defined (__SunOS_5_7) sl@0: # define RW_TEST_OS "sunos-5.7" sl@0: # elif defined (__SunOS_5_6) sl@0: # define RW_TEST_OS "sunos-5.6" sl@0: # elif defined (__sun__) sl@0: # define RW_TEST_OS "sunos" sl@0: # elif defined (_WIN64) sl@0: # define RW_TEST_OS "win64" sl@0: # elif defined (_WIN32) sl@0: # define RW_TEST_OS "win32" sl@0: # else sl@0: # define RW_TEST_OS "unknown" sl@0: # endif sl@0: sl@0: # define RW_TEST_HARDWARE RW_TEST_ARCH " running " RW_TEST_OS sl@0: #else sl@0: # define RW_TEST_HARDWARE "unknown" sl@0: #endif sl@0: sl@0: /************************************************************************/ sl@0: sl@0: // defined in printf.cpp but not declared in printf.h sl@0: _TEST_EXPORT int sl@0: rw_vasnprintf (char**, size_t*, const char*, va_list); sl@0: sl@0: /************************************************************************/ sl@0: sl@0: // array to store the number of each type of diagnostic sl@0: static int sl@0: ndiags [N_DIAG_TYPES][2] /* = { { total, active }, ... }*/; sl@0: sl@0: static FILE *ftestout; sl@0: sl@0: static jmp_buf test_env; sl@0: sl@0: // set to 1 after the driver has been initialized sl@0: static int driver_initialized = 0; sl@0: sl@0: // set to 1 after the driver has finished running sl@0: static int driver_finished = 0; sl@0: sl@0: #if 0 // disabled sl@0: // %S: severity sl@0: // %M: diagnostic sl@0: // %m: diagnostic if not empty sl@0: // %F: file name sl@0: // %f: file name if not empty sl@0: // %C: clause sl@0: // %c: clause if not empty sl@0: // %L: line number sl@0: // %l: line number if valid sl@0: // %T: text sl@0: // %t: text if not empty sl@0: static char diag_pattern [80]; sl@0: #endif sl@0: sl@0: // option: use CSV format (comma separated values) sl@0: static int _rw_opt_csv = 0; sl@0: sl@0: static char clause_id [80]; sl@0: sl@0: /************************************************************************/ sl@0: sl@0: #define CHECK_INIT(init, func) _rw_check_init (init, __LINE__, func) sl@0: sl@0: static inline void sl@0: _rw_check_init (bool init, int line, const char *func) sl@0: { sl@0: if (init && !driver_initialized) { sl@0: fprintf (stderr, "%s:%d: %s: test driver already initialized\n", sl@0: __FILE__, line, func); sl@0: std_log(LOG_FILENAME_LINE,"%s:%d: %s: test driver already initialized\n", sl@0: __FILE__, line, func); sl@0: abort (); sl@0: } sl@0: sl@0: if (!init && driver_initialized) { sl@0: fprintf (stderr, "%s:%d: %s: test driver not initialized yet\n", sl@0: __FILE__, line, func); sl@0: std_log(LOG_FILENAME_LINE,"%s:%d: %s: test driver not initialized yet\n", sl@0: __FILE__, line, func); sl@0: abort (); sl@0: } sl@0: sl@0: if (driver_finished) { sl@0: fprintf (stderr, "%s:%d: %s: test finished, cannot call\n", sl@0: __FILE__, line, func); sl@0: std_log(LOG_FILENAME_LINE,"%s:%d: %s: test finished, cannot call\n", sl@0: __FILE__, line, func); sl@0: } sl@0: } sl@0: sl@0: /************************************************************************/ sl@0: sl@0: static int sl@0: _rw_opt_brief (int argc, char *argv[]) sl@0: { sl@0: static int opt_brief; sl@0: sl@0: if (0 == argc) { sl@0: // query mode: return the value of the option sl@0: return opt_brief; sl@0: } sl@0: sl@0: if (1 == argc && argv && 0 == argv [0]) { sl@0: // help mode: set argv[0] to the text of the help message sl@0: sl@0: static const char helpstr[] = { sl@0: "Enables brief mode.\n" sl@0: }; sl@0: sl@0: argv [0] = _RWSTD_CONST_CAST (char*, helpstr); sl@0: sl@0: return 0; sl@0: } sl@0: sl@0: // set mode: enable the option sl@0: opt_brief = 1; sl@0: sl@0: return 0; sl@0: } sl@0: sl@0: /************************************************************************/ sl@0: sl@0: static int sl@0: _rw_opt_quiet (int argc, char *argv[]) sl@0: { sl@0: static int opt_quiet; sl@0: sl@0: if (0 == argc) { sl@0: // query mode: return the value of the option sl@0: return opt_quiet; sl@0: } sl@0: sl@0: if (1 == argc && argv && 0 == argv [0]) { sl@0: // help mode: set argv[0] to the text of the help message sl@0: sl@0: static const char helpstr[] = { sl@0: "Enables quiet mode.\n" sl@0: "In quiet mode only diagnostics with severity 7 and above are " sl@0: "issued." sl@0: }; sl@0: sl@0: argv [0] = _RWSTD_CONST_CAST (char*, helpstr); sl@0: sl@0: return 0; sl@0: } sl@0: sl@0: // set mode: enable the option sl@0: _rw_diag_mask = ~((1 << 7) | (1 << 8) | (1 << 9)); sl@0: opt_quiet = 1; sl@0: sl@0: return 0; sl@0: } sl@0: sl@0: /************************************************************************/ sl@0: sl@0: static int sl@0: _rw_opt_verbose (int argc, char *argv[]) sl@0: { sl@0: static int opt_verbose; sl@0: sl@0: if (0 == argc) { sl@0: // query mode: return the value of the option sl@0: return opt_verbose; sl@0: } sl@0: sl@0: if (1 == argc && argv && 0 == argv [0]) { sl@0: // help mode: set argv[0] to the text of the help message sl@0: sl@0: static const char helpstr[] = { sl@0: "Enables verbose mode.\n" sl@0: }; sl@0: sl@0: argv [0] = _RWSTD_CONST_CAST (char*, helpstr); sl@0: sl@0: return 0; sl@0: } sl@0: sl@0: // set mode: enable the option sl@0: opt_verbose = 1; sl@0: sl@0: return 0; sl@0: } sl@0: sl@0: /************************************************************************/ sl@0: sl@0: static int sl@0: _rw_setopt_csv (int argc, char *argv[]) sl@0: { sl@0: if (1 == argc && argv && 0 == argv [0]) { sl@0: static const char helpstr[] = { sl@0: "Enables CSV (comma separated values) mode.\n" sl@0: }; sl@0: sl@0: argv [0] = _RWSTD_CONST_CAST (char*, helpstr); sl@0: sl@0: return 0; sl@0: } sl@0: sl@0: _rw_opt_csv = 1; sl@0: return 0; sl@0: } sl@0: sl@0: /************************************************************************/ sl@0: sl@0: static int sl@0: _rw_opt_compat (int argc, char *argv[]) sl@0: { sl@0: static int opt_compat; sl@0: sl@0: if (0 == argc) { sl@0: // query mode: return the value of the option sl@0: return opt_compat; sl@0: } sl@0: sl@0: if (1 == argc && argv && 0 == argv [0]) { sl@0: // help mode: set argv[0] to the text of the help message sl@0: sl@0: static const char helpstr[] = { sl@0: "Enables RWTest-format compatibility mode.\n" sl@0: }; sl@0: sl@0: argv [0] = _RWSTD_CONST_CAST (char*, helpstr); sl@0: sl@0: return 0; sl@0: } sl@0: sl@0: // set mode: enable the option sl@0: opt_compat = 1; sl@0: sl@0: return 0; sl@0: } sl@0: sl@0: /************************************************************************/ sl@0: sl@0: static int sl@0: _rw_opt_no_stdout (int argc, char *argv[]) sl@0: { sl@0: static int opt_no_stdout; sl@0: sl@0: if (0 == argc) { sl@0: // query mode: return the value of the option sl@0: return opt_no_stdout; sl@0: } sl@0: sl@0: if (1 == argc && argv && 0 == argv [0]) { sl@0: // help mode: set argv[0] to the text of the help message sl@0: sl@0: static const char helpstr[] = { sl@0: "Prevents the program from using stdandard output for diagnostic\n" sl@0: "messages. Instead, the driver will create a log file with a name\n" sl@0: "obtained from the from the basename of the program source file,\n" sl@0: "usually obtained by passing the value of the __FILE__ macro to\n" sl@0: "the driver, with the .out extension. If successful, the driver\n" sl@0: "will write all diagnostic messages issued by the program to this\n" sl@0: "file. Otherwise, the driver exits with an error.\n" sl@0: }; sl@0: sl@0: argv [0] = _RWSTD_CONST_CAST (char*, helpstr); sl@0: sl@0: return 0; sl@0: } sl@0: sl@0: // set mode: enable the option sl@0: opt_no_stdout = 1; sl@0: sl@0: return 0; sl@0: } sl@0: sl@0: /************************************************************************/ sl@0: sl@0: static int sl@0: _rw_setopt_output_file (int argc, char *argv[]) sl@0: { sl@0: if (1 == argc && argv && 0 == argv [0]) { sl@0: static const char helpstr[] = { sl@0: "Specifies the name of the output file to be used by the program\n" sl@0: "for diagnostic messages. Unless this option is specified, the\n" sl@0: "program will issue all diagnostic messages to the standard output." sl@0: "\nDriver diagnostics are always directed to stderr.\n" sl@0: }; sl@0: sl@0: argv [0] = _RWSTD_CONST_CAST (char*, helpstr); sl@0: sl@0: return 0; sl@0: } sl@0: sl@0: const char *file_name = 0; sl@0: sl@0: if ('-' == argv [0][0] && 'O' == argv [0][1] || 'o' == argv [0][1]) { sl@0: file_name = argv [0] + 2; sl@0: } sl@0: else if (1 < argc && '-' != argv [1][0]) { sl@0: file_name = argv [1]; sl@0: } sl@0: sl@0: if (file_name) { sl@0: sl@0: FILE* const f = fopen (file_name, "w"); sl@0: sl@0: if (f) { sl@0: if (ftestout && ftestout != stderr) sl@0: fclose (ftestout); sl@0: sl@0: ftestout = f; sl@0: } sl@0: } sl@0: sl@0: // return 0 on success, any non-zero value on failure sl@0: return !(ftestout != 0); sl@0: } sl@0: sl@0: /************************************************************************/ sl@0: sl@0: _TEST_EXPORT int sl@0: rw_vsetopts (const char *opts, va_list va); sl@0: sl@0: /************************************************************************/ sl@0: sl@0: static int sl@0: _rw_use_color () sl@0: { sl@0: #ifndef _RWSTD_NO_ISATTY sl@0: sl@0: // is output sent to a terminal? sl@0: // if so, assume a vt100 compatible terminal for now sl@0: static const int tty = isatty (fileno (ftestout)); sl@0: sl@0: #else // if defined (_RWSTD_NO_ISATTY) sl@0: sl@0: // FIXME: deal with a missing isatty() and Windows sl@0: static const int tty = 0; sl@0: sl@0: #endif // _RWSTD_NO_ISATTY sl@0: sl@0: return 0 != tty; sl@0: } sl@0: sl@0: /************************************************************************/ sl@0: sl@0: _TEST_EXPORT int sl@0: rw_vtest (int argc, char **argv, sl@0: const char *file_name, sl@0: const char *clause, sl@0: const char *comment, sl@0: int (*fun)(int, char**), sl@0: const char *optstr, sl@0: va_list va) sl@0: { sl@0: CHECK_INIT (false, "rw_vtest()"); sl@0: sl@0: driver_initialized = 1; sl@0: sl@0: if (optstr && 0 > rw_vsetopts (optstr, va)) { sl@0: fprintf (stderr, "%s:%d: rw_setopts() failed\n", __FILE__, __LINE__); sl@0: std_log(LOG_FILENAME_LINE,"%s:%d: rw_setopts() failed\n", __FILE__, __LINE__); sl@0: return 1; sl@0: } sl@0: sl@0: const int nopts = sl@0: rw_setopts ("|-no-stdout " sl@0: "|-diags= " // argument required sl@0: "|-trace " sl@0: "|-severity= " // argument required sl@0: "|-csv " sl@0: "|-compat " sl@0: "o|-output:" // argument optional sl@0: "b|-brief " sl@0: "q|-quiet " sl@0: "v|-verbose", sl@0: _rw_opt_no_stdout, sl@0: _rw_setopt_diags, sl@0: _rw_setopt_trace, sl@0: _rw_setopt_trace_mask, sl@0: _rw_setopt_csv, sl@0: _rw_opt_compat, sl@0: _rw_setopt_output_file, sl@0: _rw_opt_brief, sl@0: _rw_opt_quiet, sl@0: _rw_opt_verbose, sl@0: 0); sl@0: sl@0: if (3 > nopts) { sl@0: fprintf (stderr, "%s:%d: rw_setopts() failed\n", __FILE__, __LINE__); sl@0: std_log(LOG_FILENAME_LINE,"%s:%d: rw_setopts() failed\n", __FILE__, __LINE__); sl@0: abort (); sl@0: return 1; sl@0: } sl@0: sl@0: #ifndef _RWSTD_USE_CONFIG sl@0: sl@0: // enable RWTest-format compatibility mode sl@0: _rw_opt_compat (1, 0); sl@0: sl@0: // disable output to stdout sl@0: _rw_opt_no_stdout (1, 0); sl@0: sl@0: #endif // _RWSTD_USE_CONFIG sl@0: sl@0: _rw_setopts_types (); sl@0: sl@0: _rw_setopts_lines (); sl@0: sl@0: int status = rw_runopts (argc, argv); sl@0: sl@0: if (status) sl@0: return status; sl@0: sl@0: if (0 == ftestout) { sl@0: sl@0: if (_rw_opt_no_stdout (0, 0) && file_name) { sl@0: char fname [256] = "C:\\"; sl@0: sl@0: char* temp_ret = strchr (file_name, '/'); sl@0: sl@0: if(temp_ret != NULL) sl@0: { sl@0: const char* const slash = strrchr (file_name, '/'); sl@0: sl@0: strcat (fname, slash ? slash + 1 : file_name); sl@0: } sl@0: else sl@0: { sl@0: const char* const slash = strrchr (file_name, _RWSTD_PATH_SEP); sl@0: sl@0: strcat (fname, slash ? slash + 1 : file_name); sl@0: } sl@0: sl@0: char* const dot = strchr (fname, '.'); sl@0: if (dot) sl@0: strcpy (dot, ".out"); sl@0: else sl@0: strcat (fname, ".out"); sl@0: sl@0: ftestout = fopen (fname, "w"); sl@0: } sl@0: else sl@0: ftestout = stdout; sl@0: } sl@0: sl@0: if (clause) sl@0: strcpy (clause_id, clause); sl@0: sl@0: const char begin_fmt[] = { sl@0: "\n" sl@0: "# COMPILER: %s\n" sl@0: "# ENVIRONMENT: %s\n" sl@0: "# FILE: %s\n" sl@0: "# COMPILED: %s, %s\n" sl@0: "# COMMENT: %s\n" sl@0: "######################################################" sl@0: }; sl@0: sl@0: const char* const fname = strrchr (file_name, _RWSTD_PATH_SEP); sl@0: sl@0: rw_info (0, 0, 0, sl@0: begin_fmt, sl@0: RW_TEST_COMPILER, RW_TEST_HARDWARE, sl@0: fname ? fname + 1 : file_name, sl@0: __DATE__, __TIME__, sl@0: comment ? comment : ""); sl@0: sl@0: status = setjmp (test_env); sl@0: sl@0: if (0 == status) { sl@0: // environment set, execute the callback function sl@0: status = fun (argc, argv); sl@0: } sl@0: else { sl@0: // fatal test error (via a call to rw_fatal()) sl@0: } sl@0: sl@0: driver_finished = 1; sl@0: sl@0: static const char tblrow[] = sl@0: "+-----------------------+--------+--------+--------+"; sl@0: sl@0: fprintf (ftestout, sl@0: "# %s\n" sl@0: "# | DIAGNOSTIC | ACTIVE | TOTAL |INACTIVE|\n" sl@0: "# %s\n", sl@0: tblrow, tblrow); sl@0: sl@0: int nlines = 0; sl@0: sl@0: for (int i = 0; i != N_DIAG_TYPES; ++i) { sl@0: if (ndiags [i][0] || !(_rw_diag_mask & (1 << diag_trace))) { sl@0: sl@0: // print out details for any non-zero totals sl@0: // or for all totals when debugging or tracing sl@0: // is enabled sl@0: sl@0: ++nlines; sl@0: sl@0: const long num = (ndiags [i][0] - ndiags [i][1]) * 100L; sl@0: const long den = ndiags [i][0]; sl@0: sl@0: const long pct = den ? num / den : 0; sl@0: sl@0: const char* pfx = ""; sl@0: const char* sfx = ""; sl@0: sl@0: static int use_color = _rw_use_color (); sl@0: sl@0: if (use_color) { sl@0: pfx = ndiags [i][1] ? diag_msgs [i].esc_pfx : ""; sl@0: sfx = ndiags [i][1] ? diag_msgs [i].esc_sfx : ""; sl@0: } sl@0: sl@0: fprintf (ftestout, sl@0: "# | (S%d) %-*s |%s %6d %s| %6d | %5ld%% |\n", sl@0: i, int (sizeof diag_msgs [i].code), diag_msgs [i].code, sl@0: pfx, ndiags [i][1], sfx, ndiags [i][0], pct); sl@0: } sl@0: } sl@0: sl@0: if (0 == nlines) sl@0: fprintf (ftestout, "# no diagnostics\n"); sl@0: sl@0: fprintf (ftestout, "# %s\n", tblrow); sl@0: sl@0: if (_rw_opt_compat (0, 0)) { sl@0: sl@0: // TO DO: get rid of this sl@0: sl@0: // RWTest compatibility format sl@0: sl@0: fprintf (ftestout, sl@0: "######################################################\n" sl@0: "## Warnings = %d\n" sl@0: "## Assertions = %d\n" sl@0: "## FailedAssertions = %d\n", sl@0: ndiags [diag_warn][1] + ndiags [diag_xwarn][1], sl@0: ndiags [diag_assert][0], sl@0: ndiags [diag_assert][1] + ndiags [diag_xassert][1]); sl@0: } sl@0: sl@0: fclose (ftestout); sl@0: ftestout = 0; sl@0: sl@0: return status; sl@0: } sl@0: sl@0: /************************************************************************/ sl@0: sl@0: _TEST_EXPORT int sl@0: rw_test (int argc, char **argv, sl@0: const char *fname, sl@0: const char *clause, sl@0: const char *comment, sl@0: int (*testfun)(int, char**), sl@0: const char *optstr, sl@0: ...) sl@0: { sl@0: CHECK_INIT (false, "rw_test()"); sl@0: sl@0: va_list va; sl@0: va_start (va, optstr); sl@0: sl@0: const int status = sl@0: rw_vtest (argc, argv, fname, clause, comment, testfun, optstr, va); sl@0: sl@0: va_end (va); sl@0: sl@0: return status; sl@0: } sl@0: sl@0: /************************************************************************/ sl@0: sl@0: // escape every occurrence of the double quote character in the string sl@0: // pointed to by buf by prepending to it the escape character specified sl@0: // by the last acrgument sl@0: // returns the new buffer if the size of the existing buffer isn't sl@0: // sufficient and sets *pbufsize to the size of the newly allocated sl@0: // buffer, otherwise the original value of buf and leaves *pbufsize sl@0: // unchanged sl@0: static char* sl@0: _rw_escape (char *buf, size_t bufsize, char esc) sl@0: { sl@0: // handle null buffer sl@0: if (0 == buf) sl@0: return buf; sl@0: sl@0: // count the number of embedded quotes sl@0: char *quote = buf; sl@0: size_t nquotes = 0; sl@0: sl@0: #ifdef __ARMCC__ sl@0: #pragma diag_suppress 1293 sl@0: #endif sl@0: while ((quote = strchr (quote, '"'))) { sl@0: ++nquotes; sl@0: ++quote; sl@0: } sl@0: sl@0: sl@0: // no quotes found, return the original buffer sl@0: if (0 == nquotes) sl@0: return buf; sl@0: sl@0: // conpute the size of the buffer that will be needed to escape sl@0: // all the double quotes sl@0: size_t newbufsize = strlen (buf) + nquotes + 1; sl@0: sl@0: char *newbuf = 0; sl@0: sl@0: if (0 /* newbufsize <= bufsize */) { sl@0: // FIXME: escape embedded quotes in place w/o reallocation sl@0: _RWSTD_UNUSED (bufsize); sl@0: } sl@0: else { sl@0: newbuf = (char*)malloc (newbufsize); sl@0: if (0 == newbuf) { sl@0: return 0; sl@0: } sl@0: sl@0: // set the next pointer to the beginning of the new buffer sl@0: // as the destination where to copy the string argument sl@0: char *next = newbuf; sl@0: sl@0: // set quote to initially point to the beginning of sl@0: // the source buffer and then just past the last quote sl@0: quote = buf; sl@0: sl@0: for (char *q = buf; ; ++q) { sl@0: sl@0: // look for the next (or first) quote sl@0: q = strchr (q, '"'); sl@0: sl@0: // compute the number of characters, excluding the quote sl@0: // to copy to the destination buffer sl@0: const size_t nchars = q ? size_t (q - quote) : strlen (quote); sl@0: sl@0: memcpy (next, quote, nchars); sl@0: sl@0: if (q) { sl@0: // append the escape character to the destination buffer sl@0: next [nchars] = esc; sl@0: sl@0: // append the quote from the source string sl@0: next [nchars + 1] = '"'; sl@0: sl@0: // advance the destination pointer past the quote sl@0: next += nchars + 2; sl@0: sl@0: // advance the source pointer past the embedded quote sl@0: quote = q + 1; sl@0: } sl@0: else { sl@0: // NUL-terminate the destination buffer sl@0: *next = '\0'; sl@0: break; sl@0: } sl@0: } sl@0: } sl@0: sl@0: return newbuf; sl@0: } sl@0: sl@0: /************************************************************************/ sl@0: sl@0: static void sl@0: _rw_vissue_diag (diag_t diag, int severity, const char *file, int line, sl@0: const char *fmt, va_list va) sl@0: { sl@0: CHECK_INIT (true, "_rw_vissue_diag()"); sl@0: sl@0: if (0 == fmt) sl@0: fmt = ""; sl@0: sl@0: static char fmterr[] = "*** formatting error ***"; sl@0: sl@0: char *usrbuf = 0; sl@0: const int nchars = rw_vasnprintf (&usrbuf, 0, fmt, va); sl@0: sl@0: if (nchars < 0 || 0 == usrbuf) sl@0: usrbuf = fmterr; sl@0: sl@0: // compute the number of newline characters in the text sl@0: int nlines = 0; sl@0: sl@0: #ifdef __ARMCC__ sl@0: #pragma diag_suppress 1293 sl@0: #endif sl@0: for (const char *nl = usrbuf; (nl = strchr (nl, '\n')); ++nl) sl@0: ++nlines; sl@0: sl@0: sl@0: static const int use_color = _rw_use_color (); sl@0: sl@0: const char* const diagstr[] = { sl@0: use_color ? diag_msgs [severity].esc_pfx : "", sl@0: *diag_msgs [severity].code ? diag_msgs [severity].code : "UNKNOWN", sl@0: use_color ? diag_msgs [severity].esc_sfx : "", sl@0: _rw_opt_verbose (0, 0) && *diag_msgs [severity].desc ? sl@0: diag_msgs [severity].desc : 0 sl@0: }; sl@0: sl@0: const char* const traced_diag = sl@0: 0 == severity && diag_msgs [diag].code ? diag_msgs [diag].code : 0; sl@0: sl@0: const char* const slash = file ? strrchr (file, _RWSTD_PATH_SEP) : 0; sl@0: if (slash) sl@0: file = slash + 1; sl@0: sl@0: char *mybuf = 0; sl@0: sl@0: if (_rw_opt_csv) { sl@0: sl@0: // format all fields as comma separated values (CSV): sl@0: // -- a field containing the quote character, the comma, sl@0: // or the newline or linefeed character must be enclosed sl@0: // in a pair of double quotes sl@0: // -- every occurrence of the double quote character in a field sl@0: // must be escaped by prepening another double quote character sl@0: // to it sl@0: sl@0: // escape all double quotes by prepending the double sl@0: // quote character to each according to the CSV format sl@0: char* const newbuf = _rw_escape (usrbuf, 0, '"'); sl@0: if (newbuf != usrbuf) { sl@0: free (usrbuf); sl@0: usrbuf = newbuf; sl@0: } sl@0: sl@0: mybuf = sl@0: rw_sprintfa ("%d, " // severity sl@0: "\"%s%s" // diagnostic sl@0: "%{?}_%s%{;}%s\", " // traced diagnostic sl@0: "\"%s\", " // clause sl@0: "\"%s\", " // file sl@0: "%d, " // line sl@0: "\"%s\"", // user text sl@0: severity, sl@0: diagstr [0], diagstr [1], sl@0: 0 != traced_diag, traced_diag, diagstr [2], sl@0: clause_id, sl@0: 0 != file ? file : "", sl@0: line, sl@0: usrbuf); sl@0: } sl@0: else { sl@0: sl@0: nlines += 2 + ('\0' != *clause_id) + (0 != file) + (0 < line); sl@0: sl@0: mybuf = sl@0: rw_sprintfa ("# %s" // escape prefix sl@0: "%s" // diagnostic sl@0: "%{?}_%s%{;}" // traced diagnostic sl@0: "%s " // escape suffix sl@0: "(S%d)" // severity sl@0: "%{?}, %s%{;} " // description sl@0: "(%d lines):\n" // number of lines sl@0: "# TEXT: %s\n" // user text sl@0: "%{?}# CLAUSE: %s\n%{;}" // clause if not empty sl@0: "%{?}# FILE: %s\n%{;}" // file if not null sl@0: "%{?}# LINE: %d\n%{;}", // line if positive sl@0: diagstr [0], sl@0: diagstr [1], sl@0: 0 != traced_diag, traced_diag, sl@0: diagstr [2], sl@0: severity, sl@0: 0 != diagstr [3], diagstr [3], sl@0: nlines, sl@0: usrbuf, sl@0: '\0' != *clause_id, clause_id, sl@0: 0 != file, file, sl@0: 0 < line, line); sl@0: } sl@0: #if 0 // disabled sl@0: else { sl@0: sl@0: mybuf = sl@0: rw_sprintfa ("# %s%s" // diagnostic sl@0: "%{?}_%s%{;}%s " // traced diagnostic sl@0: "(S%d): " // severity sl@0: "%{?}[%s] %{;}" // clause if not empty sl@0: "%{?}(%d lines): %{;}" // number of lines if > 1 sl@0: "%{?}%s:" // if (file) then file sl@0: "%{?}%d:%{;} " // if (0 < line) line sl@0: "%{:}" // else sl@0: "%{?}line %d: %{;}" // if (0 < line) line sl@0: "%{;}" // endif sl@0: "%s", // user text sl@0: diagstr [0], diagstr [1], sl@0: 0 != traced_diag, traced_diag, diagstr [2], sl@0: severity, sl@0: '\0' != *clause_id, clause_id, sl@0: 1 < nlines, nlines, sl@0: 0 != file, file, sl@0: 0 < line, line, sl@0: 0 < line, line, sl@0: usrbuf); sl@0: } sl@0: #endif // 0/1 sl@0: sl@0: fprintf (ftestout, "%s\n", mybuf); sl@0: sl@0: if (mybuf != fmterr) sl@0: free (mybuf); sl@0: sl@0: if (usrbuf != fmterr) sl@0: free (usrbuf); sl@0: } sl@0: sl@0: /************************************************************************/ sl@0: sl@0: static void sl@0: _rw_vdiag (diag_t diag, int severity, const char *file, int line, sl@0: const char *fmt, va_list va) sl@0: { sl@0: CHECK_INIT (true, "_rw_vdiag()"); sl@0: sl@0: // check if the diagnostic is expected sl@0: const int expected = 0 != _rw_expected (line); sl@0: sl@0: if (expected) { sl@0: if (severity) { sl@0: // if the diagnostic is expected to be active, sl@0: // adjust its type and severity sl@0: if (diag_assert == diag) sl@0: diag = diag_xassert; sl@0: else if (diag_warn == diag) sl@0: diag = diag_xwarn; sl@0: sl@0: severity = diag * severity; sl@0: } sl@0: else { sl@0: // if the diagnostic is expected to be active but isn't, sl@0: // adjust its type to an unexpectdly inactive one sl@0: if (diag_assert == diag || diag_warn == diag) sl@0: diag = diag_expect; sl@0: sl@0: severity = diag; sl@0: } sl@0: } sl@0: else if (diag) { sl@0: // normalize the severity sl@0: severity = diag * severity; sl@0: } sl@0: sl@0: if (severity < 0) sl@0: severity = 0; sl@0: else if (N_DIAG_TYPES <= severity) sl@0: severity = N_DIAG_TYPES - 1; sl@0: sl@0: // increment the diagnostic counter sl@0: ++ndiags [diag][0]; sl@0: sl@0: if (severity) { sl@0: sl@0: ++ndiags [diag][1]; sl@0: } sl@0: sl@0: const int sevbit = (1 << severity); sl@0: sl@0: if (0 == (sevbit & _rw_diag_mask)) { sl@0: // issue the diagnostic sl@0: _rw_vissue_diag (diag, severity, file, line, fmt, va); sl@0: } sl@0: sl@0: if (diag_fatal == diag && severity) { sl@0: // fatal error, terminate test sl@0: longjmp (test_env, severity); sl@0: } sl@0: } sl@0: sl@0: /************************************************************************/ sl@0: sl@0: _TEST_EXPORT int sl@0: rw_fatal (int success, const char *file, int line, const char *fmt, ...) sl@0: { sl@0: CHECK_INIT (true, "rw_fatal()"); sl@0: sl@0: va_list va; sl@0: va_start (va, fmt); sl@0: sl@0: _rw_vdiag (diag_fatal, 0 == success, file, line, fmt, va); sl@0: sl@0: va_end (va); sl@0: sl@0: return success; sl@0: } sl@0: sl@0: /************************************************************************/ sl@0: sl@0: _TEST_EXPORT int sl@0: rw_error (int success, const char *file, int line, const char *fmt, ...) sl@0: { sl@0: CHECK_INIT (true, "rw_error()"); sl@0: sl@0: va_list va; sl@0: va_start (va, fmt); sl@0: sl@0: _rw_vdiag (diag_error, 0 == success, file, line, fmt, va); sl@0: sl@0: va_end (va); sl@0: sl@0: return success; sl@0: } sl@0: sl@0: /************************************************************************/ sl@0: sl@0: _TEST_EXPORT int sl@0: rw_assert (int success, const char *file, int line, const char *fmt, ...) sl@0: { sl@0: CHECK_INIT (true, "rw_assert()"); sl@0: sl@0: va_list va; sl@0: va_start (va, fmt); sl@0: sl@0: _rw_vdiag (diag_assert, 0 == success, file, line, fmt, va); sl@0: sl@0: va_end (va); sl@0: sl@0: return success; sl@0: } sl@0: sl@0: /************************************************************************/ sl@0: sl@0: _TEST_EXPORT int sl@0: rw_warn (int success, const char *file, int line, const char *fmt, ...) sl@0: { sl@0: CHECK_INIT (true, "rw_warn()"); sl@0: sl@0: va_list va; sl@0: va_start (va, fmt); sl@0: sl@0: _rw_vdiag (diag_warn, 0 == success, file, line, fmt, va); sl@0: sl@0: va_end (va); sl@0: sl@0: return success; sl@0: } sl@0: sl@0: /************************************************************************/ sl@0: sl@0: _TEST_EXPORT int sl@0: rw_note (int success, const char *file, int line, const char *fmt, ...) sl@0: { sl@0: CHECK_INIT (true, "rw_note()"); sl@0: sl@0: va_list va; sl@0: va_start (va, fmt); sl@0: sl@0: _rw_vdiag (diag_note, 0 == success, file, line, fmt, va); sl@0: sl@0: va_end (va); sl@0: sl@0: return success; sl@0: } sl@0: sl@0: /************************************************************************/ sl@0: sl@0: _TEST_EXPORT int sl@0: rw_info (int success, const char *file, int line, const char *fmt, ...) sl@0: { sl@0: CHECK_INIT (true, "rw_info()"); sl@0: sl@0: va_list va; sl@0: va_start (va, fmt); sl@0: sl@0: _rw_vdiag (diag_info, 0 == success, file, line, fmt, va); sl@0: sl@0: va_end (va); sl@0: sl@0: return success; sl@0: }