blob: fce728043fa41f0e7fae6b96feb3eef4649c8e5a [file] [log] [blame]
/************************************************************************
*
* driver.cpp - definitions of the test driver
*
* $Id$
*
************************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you 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.
*
* Copyright 2005-2008 Rogue Wave Software, Inc.
*
**************************************************************************/
// expand _TEST_EXPORT macros
#define _RWSTD_TEST_SRC
#include "opt_diags.h"
#include "opt_lines.h"
#include "opt_trace.h"
#include "opt_types.h"
#include <rw_cmdopt.h> // for rw_setopts()
#include <rw_printf.h> // for rw_snprintfa()
#include <assert.h> // for assert
#include <ctype.h> // for islower(), isupper()
#include <setjmp.h> // for longjmp, setjmp, ...
#include <stdarg.h> // for va_list
#include <stdio.h> // for fileno
#include <stdlib.h> // for free, _set_invalid_parameter_handler()
#include <string.h> // for strchr, strcpy
#ifdef _WIN32
# include <windows.h> // for SetErrorMode()
#endif // _WIN32
#ifdef _MSC_VER
# include <crtdbg.h> // for _CrtSetReportMode(), _CrtSetDbgFlag()
#endif // _MSC_VER
#ifndef _WIN32
# include <unistd.h> // for isatty()
# include <sys/resource.h> // for setlimit()
# ifndef RLIM_SAVED_CUR
# define RLIM_SAVED_CUR RLIM_INFINITY
# endif // RLIM_SAVED_CUR
# ifndef RLIM_SAVED_MAX
# define RLIM_SAVED_MAX RLIM_INFINITY
# endif // RLIM_SAVED_MAX
// declare fileno in case it's not declared (for strict ANSI conformance)
extern "C" {
_RWSTD_DLLIMPORT int (fileno)(FILE*) _LIBC_THROWS ();
} // extern "C"
#else // if Windows
// no isatty on Windoze
# define _RWSTD_NO_ISATTY
#endif // _WIN32
// expand _TEST_EXPORT macros
#define _RWSTD_TEST_SRC
#include <rw_driver.h>
/************************************************************************/
#define RW_TEST_STRSTR(x) #x
#define RW_TEST_STR(x) RW_TEST_STRSTR(x)
#ifndef RW_TEST_COMPILER
# if defined (__DECCXX__)
# define RW_TEST_COMPILER "Compaq C++, __DECCXX__ = " \
RW_TEST_STR (__DECCXX__)
# elif defined (__INTEL_COMPILER)
# if defined (__EDG_VERSION__)
# define RW_TEST_ICC_EDG_VER \
", __EDG_VERSION__ = " RW_TEST_STR (__EDG_VERSION__)
# else
# define RW_TEST_ICC_EDG_VER ""
# endif
# if defined (_MSC_VER)
# define RW_TEST_COMPILER "Intel C++, __INTEL_COMPILER = " \
RW_TEST_STR (__INTEL_COMPILER) ", _MSC_VER = " \
RW_TEST_STR (_MSC_VER) \
RW_TEST_ICC_EDG_VER
# elif defined (__INTEL_COMPILER_BUILD_DATE)
# define RW_TEST_COMPILER "Intel C++, __INTEL_COMPILER = " \
RW_TEST_STR (__INTEL_COMPILER) \
", __INTEL_COMPILER_BUILD_DATE = " \
RW_TEST_STR (__INTEL_COMPILER_BUILD_DATE) \
RW_TEST_ICC_EDG_VER
# else
# define RW_TEST_COMPILER "Intel C++, __INTEL_COMPILER = " \
RW_TEST_STR (__INTEL_COMPILER) \
RW_TEST_ICC_EDG_VER
# endif
# elif defined (__GNUC__)
# if defined (__VERSION__)
# define RW_TEST_GCC_VER ", __VERSION__ = \"" __VERSION__ "\""
# else
# define RW_TEST_GCC_VER ""
# endif
# if defined (__GNUC_PATCHLEVEL__)
# define RW_TEST_COMPILER "gcc " \
RW_TEST_STR (__GNUC__) "." \
RW_TEST_STR (__GNUC_MINOR__) "." \
RW_TEST_STR (__GNUC_PATCHLEVEL__) \
RW_TEST_GCC_VER
# else
# define RW_TEST_COMPILER "gcc " \
RW_TEST_STR (__GNUC__) "." RW_TEST_STR (__GNUC_MINOR__)
RW_TEST_GCC_VER
# endif
# elif defined (_COMPILER_VERSION) && defined (__sgi)
# define RW_TEST_COMPILER "SGI MIPSpro, _COMPILER_VERSION = " \
RW_TEST_STR (_COMPILER_VERSION)
# elif defined (__INTEL_COMPILER)
# if defined (_MSC_VER)
# define RW_TEST_COMPILER "Intel C++, __INTEL_COMPILER = " \
RW_TEST_STR (__INTEL_COMPILER) ", _MSC_VER = " \
RW_TEST_STR (_MSC_VER)
# else
# define RW_TEST_COMPILER "Intel C++, __INTEL_COMPILER = " \
RW_TEST_STR (__INTEL_COMPILER)
# endif
# elif defined (__HP_aCC)
# if defined (__EDG_VERSION__)
# define RW_TEST_ACC_EDG_VER \
", __EDG_VERSION__ = " RW_TEST_STR (__EDG_VERSION__)
# else
# define RW_TEST_ACC_EDG_VER ""
# endif
# define RW_TEST_COMPILER "HP aCC, __HP_aCC = " \
RW_TEST_STR (__HP_aCC) \
RW_TEST_ACC_EDG_VER
# elif defined (__IBMCPP__)
# define RW_TEST_COMPILER "IBM VisualAge C++, __IBMCPP__ = " \
RW_TEST_STR (__IBMCPP__)
# elif defined (_MSC_VER)
# define RW_TEST_COMPILER "MSVC, _MSC_VER = " \
RW_TEST_STR (_MSC_VER)
# elif defined (__SUNPRO_CC)
# define RW_TEST_COMPILER "SunPro, __SUNPRO_CC = " \
RW_TEST_STR (__SUNPRO_CC)
# elif defined (__EDG__)
// handle the vanilla EDG eccp last to avoid overriding
// the real compiler's macro (compilers such as Intel C++
// and HP aCC use eccp for their C++ front end)
# define RW_TEST_COMPILER "EDG eccp, __EDG_VERSION__ = " \
RW_TEST_STR (__EDG_VERSION__)
# else
# define RW_TEST_COMPILER "unknown"
# endif
#endif
#ifndef RW_TEST_LIBSTD
# ifdef _RWSTD_VER
# define RW_TEST_LIBSTD "Rogue Wave C++ Standard Library, " \
"_RWSTD_VER = " RW_TEST_STR (_RWSTD_VER)
# elif defined (__GLIBCXX__)
# define RW_TEST_LIBSTD "GNU C++ Standard Library, " \
"__GLIBCXX__ = " \
RW_TEST_STR (__GLIBCXX__)
# elif defined (_STLPORT_VERSION)
// check for STLport before SGI STL since STLport,
// being derived from SGI STL, #defines both macros
# define RW_TEST_LIBSTD "STLport, " \
"_STLPORT_VERSION = " \
RW_TEST_STR (_STLPORT_VERSION)
# elif defined (__SGI_STL)
# define RW_TEST_LIBSTD "SGI STL, " \
"__SGI_STL = " \
RW_TEST_STR (__SGI_STL)
# elif defined (_YVALS)
// is there a better way to identify the Dinkumware
// implementation? does it have a version macro?
# define RW_TEST_LIBSTD "Dinkum C++ Standard Library"
# endif
#endif // RW_TEST_LIBSTD
#ifndef RW_TEST_HARDWARE
# if defined (__alpha__) || defined (__alpha)
# define RW_TEST_ARCH "alpha"
# elif defined (__x86_64__) || defined (__x86_64)
# if defined (__LP64__) || defined (_LP64)
# define RW_TEST_ARCH "x86_64/LP64"
# else
# define RW_TEST_ARCH "x86_64/ILP32"
# endif
# elif defined (__amd64__) || defined (__amd64)
# if defined (__LP64__) || defined (_LP64)
# define RW_TEST_ARCH "amd64/LP64"
# else
# define RW_TEST_ARCH "amd64/ILP32"
# endif
# elif defined (_PA_RISC2_0)
# define RW_TEST_ARCH "pa-risc 2.0"
# elif defined (_PA_RISC1_0)
# define RW_TEST_ARCH "pa-risc 1.0"
# elif defined (__hppa)
# define RW_TEST_ARCH "pa-risc"
# elif defined (__pentiumpro__) || defined (__pentiumpro)
# define RW_TEST_ARCH "pentiumpro"
# elif defined (__pentium__) || defined (__pentium)
# define RW_TEST_ARCH "pentium"
# elif defined (__i486__) || defined (__i486)
# define RW_TEST_ARCH "i486"
# elif defined (__i386__) || defined (__i386)
# define RW_TEST_ARCH "i386"
# elif defined (__i586__) || defined (__i586)
# define RW_TEST_ARCH "i586"
# elif defined (__ia64)
# define RW_TEST_ARCH "ia64"
# elif defined (__mips)
# define RW_TEST_ARCH "mips"
# elif defined (__sparcv9)
# define RW_TEST_ARCH "sparc-v9"
# elif defined (__sparcv8)
# define RW_TEST_ARCH "sparc-v8"
# elif defined (__sparc)
# define RW_TEST_ARCH "sparc"
# elif defined (_POWER)
# if defined (_ARCH_PWR5)
# define RW_TEST_ARCH "power-5"
# elif defined (_ARCH_PWR4)
# define RW_TEST_ARCH "power-4"
# elif defined (_ARCH_PWR3)
# define RW_TEST_ARCH "power-3"
# elif defined (_ARCH_604)
# define RW_TEST_ARCH "powerpc-604"
# elif defined (_ARCH_603)
# define RW_TEST_ARCH "powerpc-603"
# elif defined (_ARCH_602)
# define RW_TEST_ARCH "powerpc-602"
# elif defined (_ARCH_601)
# define RW_TEST_ARCH "powerpc-601"
# elif defined (_ARCH_403)
# define RW_TEST_ARCH "powerpc-403"
# elif defined (_ARCH_PPC64)
# define RW_TEST_ARCH "powerpc/LP64"
# else
# define RW_TEST_ARCH "powerpc"
# endif
# elif defined (_WIN64)
# define RW_TEST_ARCH "ia64"
# elif defined (_WIN32)
# define RW_TEST_ARCH "i86"
# else
# define RW_TEST_ARCH "unknown"
# endif
# if defined (_AIX54)
# define RW_TEST_OS "aix-5.4 (or better)"
# elif defined (_AIX53)
# define RW_TEST_OS "aix-5.3"
# elif defined (_AIX52)
# define RW_TEST_OS "aix-5.2"
# elif defined (_AIX51)
# define RW_TEST_OS "aix-5.1"
# elif defined (_AIX50)
# define RW_TEST_OS "aix-5.0"
# elif defined (_AIX43)
# define RW_TEST_OS "aix-4.3"
# elif defined (_AIX41)
# define RW_TEST_OS "aix-4.1"
# elif defined (_AIX32)
# define RW_TEST_OS "aix-3.2"
# elif defined (_AIX)
# define RW_TEST_OS "aix"
# elif defined (__hpux)
# define RW_TEST_OS "hp-ux"
# elif defined (__osf__)
# define RW_TEST_OS "tru64-unix"
# elif defined (__sgi) && defined (__mips)
# define RW_TEST_OS "irix"
# elif defined (__linux__) || defined (__linux)
# if defined (__ELF__)
# define LINUX_TYPE "linux-elf"
# else
# define LINUX_TYPE "linux"
# endif
# define RW_TEST_OS LINUX_TYPE " (" \
_RWSTD_LINUX_RELEASE ") with glibc " \
RW_TEST_STR (__GLIBC__) "." \
RW_TEST_STR (__GLIBC_MINOR__)
# elif defined (__SunOS_5_11)
# define RW_TEST_OS "sunos-5.11"
# elif defined (__SunOS_5_10)
# define RW_TEST_OS "sunos-5.10"
# elif defined (__SunOS_5_9)
# define RW_TEST_OS "sunos-5.9"
# elif defined (__SunOS_5_8)
# define RW_TEST_OS "sunos-5.8"
# elif defined (__SunOS_5_7)
# define RW_TEST_OS "sunos-5.7"
# elif defined (__SunOS_5_6)
# define RW_TEST_OS "sunos-5.6"
# elif defined (__sun__)
# define RW_TEST_OS "sunos"
# elif defined (_WIN64)
# define RW_TEST_OS "win64"
# elif defined (_WIN32)
# define RW_TEST_OS "win32"
# else
# define RW_TEST_OS "unknown"
# endif
# define RW_TEST_HARDWARE RW_TEST_ARCH " running " RW_TEST_OS
#else
# define RW_TEST_HARDWARE "unknown"
#endif
/************************************************************************/
// defined in printf.cpp but not declared in printf.h
_TEST_EXPORT int
rw_vasnprintf (char**, size_t*, const char*, va_list);
/************************************************************************/
// array to store the number of each type of diagnostic
static int
ndiags [N_DIAG_TYPES][2] /* = { { total, active }, ... }*/;
static rw_file *_rw_ftestout;
static jmp_buf test_env;
// set to 1 after the driver has been initialized
static int _rw_driver_init = 0;
// set to 1 after the driver has finished running
static int _rw_driver_done = 0;
#if 0 // disabled
// %S: severity
// %M: diagnostic
// %m: diagnostic if not empty
// %F: file name
// %f: file name if not empty
// %C: clause
// %c: clause if not empty
// %L: line number
// %l: line number if valid
// %T: text
// %t: text if not empty
static char diag_pattern [80];
#endif
// option: use CSV format (comma separated values)
static int _rw_opt_csv = 0;
static char clause_id [80];
/************************************************************************/
#define CHECK_INIT(init, func) _rw_check_init (init, __LINE__, func)
static inline void
_rw_check_init (int expect_init, int line, const char *func)
{
if (expect_init) {
// driver is expected to be initialized
if (!_rw_driver_init) {
rw_fprintf (rw_stderr,
"%s:%d: %s: error: test driver not initialized yet\n",
__FILE__, line, func);
abort ();
}
}
else if (_rw_driver_init) {
// driver is NOT expected to be initialized
rw_fprintf (rw_stderr,
"%s:%d: %s: error: test driver already initialized\n",
__FILE__, line, func);
abort ();
}
if (_rw_driver_done) {
// driver is NOT expected to be done at this point
rw_fprintf (rw_stderr,
"%s:%d: %s: warning: test finished, cannot call\n",
__FILE__, line, func);
}
}
/************************************************************************/
static int
_rw_opt_brief (int argc, char *argv[])
{
static int opt_brief;
if (0 == argc) {
// query mode: return the value of the option
return opt_brief;
}
if (1 == argc && argv && 0 == argv [0]) {
// help mode: set argv[0] to the text of the help message
static const char helpstr[] = {
"Enables brief mode.\n"
};
argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
return 0;
}
// set mode: enable the option
opt_brief = 1;
return 0;
}
/************************************************************************/
static int
_rw_opt_quiet (int argc, char *argv[])
{
static int opt_quiet;
if (0 == argc) {
// query mode: return the value of the option
return opt_quiet;
}
if (1 == argc && argv && 0 == argv [0]) {
// help mode: set argv[0] to the text of the help message
static const char helpstr[] = {
"Enables quiet mode.\n"
"In quiet mode only diagnostics with severity 7 and above are "
"issued."
};
argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
return 0;
}
// set mode: enable the option
_rw_diag_mask = ~((1 << 7) | (1 << 8) | (1 << 9));
opt_quiet = 1;
return 0;
}
/************************************************************************/
static int
_rw_opt_verbose (int argc, char *argv[])
{
static int opt_verbose;
if (0 == argc) {
// query mode: return the value of the option
return opt_verbose;
}
if (1 == argc && argv && 0 == argv [0]) {
// help mode: set argv[0] to the text of the help message
static const char helpstr[] = {
"Enables verbose mode.\n"
};
argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
return 0;
}
// set mode: enable the option
opt_verbose = 1;
return 0;
}
/************************************************************************/
static int
_rw_setopt_csv (int argc, char *argv[])
{
if (1 == argc && argv && 0 == argv [0]) {
static const char helpstr[] = {
"Enables CSV (comma separated values) mode.\n"
};
argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
return 0;
}
_rw_opt_csv = 1;
return 0;
}
/************************************************************************/
static int
_rw_opt_compat (int argc, char *argv[])
{
static int opt_compat;
if (0 == argc) {
// query mode: return the value of the option
return opt_compat;
}
if (1 == argc && argv && 0 == argv [0]) {
// help mode: set argv[0] to the text of the help message
static const char helpstr[] = {
"Enables RWTest-format compatibility mode.\n"
};
argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
return 0;
}
// set mode: enable the option
opt_compat = 1;
return 0;
}
/************************************************************************/
static int
_rw_opt_no_stdout (int argc, char *argv[])
{
static int opt_no_stdout;
if (0 == argc) {
// query mode: return the value of the option
return opt_no_stdout;
}
if (1 == argc && argv && 0 == argv [0]) {
// help mode: set argv[0] to the text of the help message
static const char helpstr[] = {
"Prevents the program from using stdandard output for diagnostic\n"
"messages. Instead, the driver will create a log file with a name\n"
"obtained from the from the basename of the program source file,\n"
"usually obtained by passing the value of the __FILE__ macro to\n"
"the driver, with the .out extension. If successful, the driver\n"
"will write all diagnostic messages issued by the program to this\n"
"file. Otherwise, the driver exits with an error.\n"
};
argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
return 0;
}
// set mode: enable the option
opt_no_stdout = 1;
return 0;
}
/************************************************************************/
static int
_rw_setopt_output_file (int argc, char *argv[])
{
if (1 == argc && argv && 0 == argv [0]) {
static const char helpstr[] = {
"Specifies the name of the output file to be used by the program\n"
"for diagnostic messages. Unless this option is specified, the\n"
"program will issue all diagnostic messages to the standard output."
"\nDriver diagnostics are always directed to stderr.\n"
};
argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
return 0;
}
const char *file_name = 0;
if ('-' == argv [0][0] && ('O' == argv [0][1] || 'o' == argv [0][1])
&& argv [0][2]) {
file_name = argv [0] + 2;
}
else if (1 < argc && '-' != argv [1][0]) {
file_name = argv [1];
}
if (file_name) {
if (file_name[0] != '-' || file_name[1] != '\0') {
FILE* const f = fopen (file_name, "w");
if (f) {
if (_rw_ftestout && _rw_ftestout != rw_stdout)
fclose ((FILE*)(void*)_rw_ftestout);
_rw_ftestout = (rw_file*)(void*)f;
}
}
else
_rw_ftestout = (rw_file*)(void*)stdout;
}
// return 0 on success, any non-zero value on failure
return !(_rw_ftestout != 0);
}
/************************************************************************/
_TEST_EXPORT int
rw_vsetopts (const char *opts, va_list va);
/************************************************************************/
static int
_rw_use_color ()
{
#ifndef _RWSTD_NO_ISATTY
// is output sent to a terminal?
// if so, assume a vt100 compatible terminal for now
static const int tty = _rw_ftestout ?
isatty (fileno ((FILE*)(void*)_rw_ftestout)) : 0;
#else // if defined (_RWSTD_NO_ISATTY)
// FIXME: deal with a missing isatty() and Windows
static const int tty = 0;
#endif // _RWSTD_NO_ISATTY
return 0 != tty;
}
/************************************************************************/
static int
_rw_setopt_ulimit (int argc, char **argv)
{
if (1 == argc && argv && 0 == argv [0]) {
static const char helpstr[] = {
"Sets limits on one or more system resources.\n"
"The syntax of <arg> is as follows:\n"
"<arg> ::= <limit-list>\n"
"<limit-list> ::= <limit> [, <limit-list> ]\n"
"<limit> ::= <resource> : <number>\n"
"<resource> ::= core | cpu | data | fsize | nofile | stack | as\n"
" ::= CORE | CPU | DATA | FSIZE | NOFILE | STACK | AS\n"
" ::= Core | Cpu | Data | Fsize | Nofile | Stack | As\n"
"Names in all lowercase letters set the soft limit, those in all\n"
"uppercase set the hard limit, names in mixed case will cause \n"
"both limits to attempt to be set.\n"
};
argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
return 0;
}
RW_ASSERT (0 != argv);
#if defined (RLIMIT_CORE) || defined (RLIMIT_CPU) || defined (RLIMIT_DATA)
static const struct {
const char* name; // name to set only the soft limit
const char* caps; // name to set only the hard limit
const char* mixd; // name to set both limits
int resource;
} limits[] = {
#ifdef RLIMIT_CORE
{ "core", "CORE", "Core", RLIMIT_CORE },
#endif // RLIMIT_CORE
#ifdef RLIMIT_CPU
{ "cpu", "CPU", "Cpu", RLIMIT_CPU },
#endif // RLIMIT_CPU
#ifdef RLIMIT_DATA
{ "data", "DATA", "Data", RLIMIT_DATA },
#endif // RLIMIT_DATA
#ifdef RLIMIT_FSIZE
{ "fsize", "FSIZE", "Fsize", RLIMIT_FSIZE },
#endif // RLIMIT_FSIZE
#ifdef RLIMIT_NOFILE
{ "nofile", "NOFILE", "Nofile", RLIMIT_NOFILE },
#endif // RLIMIT_NOFILE
#ifdef RLIMIT_STACK
{ "stack", "STACK", "Stack", RLIMIT_STACK },
#endif // RLIMIT_STACK
#ifdef RLIMIT_AS
{ "as", "AS", "As", RLIMIT_AS },
#endif // RLIMIT_AS
{ 0, 0, 0, 0 }
};
const char* arg = strchr (argv [0], '=');
while (arg && *arg) {
++arg;
const size_t arglen = strlen (arg);
for (size_t i = 0; limits [i].name; ++i) {
const size_t limit_len = strlen (limits [i].name);
if ( limit_len < arglen
&& ( 0 == memcmp (limits [i].name, arg, limit_len)
|| 0 == memcmp (limits [i].caps, arg, limit_len)
|| 0 == memcmp (limits [i].mixd, arg, limit_len))
&& ':' == arg [limit_len]) {
// determine whether the hard limit and/or
// the soft limit should be set
const bool hard = isupper (arg [0]);
const bool soft = islower (arg [1]);
arg += limit_len + 1;
char *end;
const long lim = strtol (arg, &end, 10);
arg = end;
if ('\0' != *arg && ',' != *arg)
break;
rlimit rlim;
memset (&rlim, 0, sizeof rlim);
rlim.rlim_cur = soft ? lim : RLIM_SAVED_CUR;
rlim.rlim_max = hard ? lim : RLIM_SAVED_MAX;
const int result = setrlimit (limits [i].resource, &rlim);
if (result) {
rw_fprintf (rw_stderr,
"setrlimit(RLIMIT_%s, { .rlim_cur=%ld, "
".rlim_max=%ld }) error: %m\n",
limits [i].caps, rlim.rlim_cur, rlim.rlim_max);
}
break;
}
}
if ('\0' != *arg && ',' != *arg) {
rw_fprintf (rw_stderr,
"%s: parse error at \"%s\"\n", argv [0], arg);
return 1;
}
}
#else // if !defined (RLIMIT_XXX)
rw_fprintf (rw_stderr, "warning: --ulimit: ignoring unimplemented "
"option: %s\n", argv [0]);
#endif // defined (RLIMIT_XXX)
return 0;
}
/************************************************************************/
static int
_rw_setopt_compat_error (int argc, char **argv, char opt)
{
if (1 == argc && argv && 0 == argv [0]) {
static const char helpstr[] = {
"Compatibility-mode option\n"
};
argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
return 0;
}
rw_fprintf (rw_stderr, "error: -%c: option available only "
"in compatibility mode\n", opt);
return 1;
}
static int
_rw_setopt_compat_warn (int argc, char **argv, char opt)
{
if (1 == argc && argv && 0 == argv [0]) {
return _rw_setopt_compat_error (argc, argv, opt);
}
rw_fprintf (rw_stderr,
"warning: -%c: ignoring unimplemented compatibility "
"mode option\n", opt);
return 0;
}
static int
_rw_setopt_compat_append (int argc, char **argv)
{
if (_rw_opt_compat (0, 0))
return _rw_setopt_compat_warn (argc, argv, 'A');
return _rw_setopt_compat_error (argc, argv, 'A');
}
static int
_rw_setopt_compat_compiler (int argc, char **argv)
{
if (_rw_opt_compat (0, 0))
return _rw_setopt_compat_warn (argc, argv, 'C');
return _rw_setopt_compat_error (argc, argv, 'C');
}
static int
_rw_setopt_compat_dir (int argc, char **argv)
{
if (_rw_opt_compat (0, 0))
return _rw_setopt_compat_warn (argc, argv, 'D');
return _rw_setopt_compat_error (argc, argv, 'D');
}
static int
_rw_setopt_compat_debug_file (int argc, char **argv)
{
if (_rw_opt_compat (0, 0))
return _rw_setopt_compat_warn (argc, argv, 'G');
return _rw_setopt_compat_error (argc, argv, 'G');
}
static int
_rw_setopt_compat_alarm (int argc, char **argv)
{
if (_rw_opt_compat (0, 0))
return _rw_setopt_compat_warn (argc, argv, 'L');
return _rw_setopt_compat_error (argc, argv, 'L');
}
static int
_rw_setopt_compat_machine (int argc, char **argv)
{
if (_rw_opt_compat (0, 0))
return _rw_setopt_compat_warn (argc, argv, 'M');
return _rw_setopt_compat_error (argc, argv, 'M');
}
static int
_rw_setopt_compat_output_file (int argc, char **argv)
{
if (_rw_opt_compat (0, 0))
return _rw_setopt_output_file (argc, argv);
return _rw_setopt_compat_error (argc, argv, 'O');
}
static int
_rw_setopts_compat ()
{
const int nopts =
rw_setopts ("A " // append output
"C: " // compiler
"D: " // directory
"G: " // debug file
"L: " // alarm
"M: " // machine
"O: ", // output file
_rw_setopt_compat_append,
_rw_setopt_compat_compiler,
_rw_setopt_compat_dir,
_rw_setopt_compat_debug_file,
_rw_setopt_compat_alarm,
_rw_setopt_compat_machine,
_rw_setopt_compat_output_file,
0 /* detect missing handlers */);
if (7 > nopts) {
rw_fprintf (rw_stderr,
"%s:%d: rw_setopts() failed\n", __FILE__, __LINE__);
abort ();
return 1;
}
return 0;
}
#ifdef _WIN32
# if defined (_MSC_VER) && _MSC_VER >= 1400
static void
_rw_invalid_parameter (const wchar_t* /*expression*/,
const wchar_t* /*function*/,
const wchar_t* /*file*/,
unsigned int /*line*/,
uintptr_t /*pReserved*/)
{
// empty handler - ignore invalid parameter validation
}
# endif // MSVC 8.0 and later
static int
_rw_opt_no_popups (int argc, char *argv[])
{
static int opt_no_popups;
if (0 == argc) {
// query mode: return the value of the option
return opt_no_popups;
}
if (1 == argc && argv && 0 == argv [0]) {
// help mode: set argv[0] to the text of the help message
static const char helpstr[] = {
"Prevents the program from using message box popup window's for\n"
"error messages.\n"
};
argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
return 0;
}
// set mode: enable the option
opt_no_popups = 1;
SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
# ifdef _MSC_VER
# if _MSC_VER >= 1400
_set_invalid_parameter_handler (_rw_invalid_parameter);
# endif // MSVC 8.0 and later
_CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
_CrtSetReportFile (_CRT_WARN, _CRTDBG_FILE_STDERR);
_CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
_CrtSetReportFile (_CRT_ERROR, _CRTDBG_FILE_STDERR);
_CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
_CrtSetReportFile (_CRT_ASSERT, _CRTDBG_FILE_STDERR);
# endif // _MSC_VER
return 0;
}
#endif // _WIN32
#if defined (_MSC_VER) && defined (_DEBUG)
static int
_rw_opt_debug_heap (int argc, char *argv[])
{
static int opt_debug_heap;
if (0 == argc) {
// query mode: return the value of the option
return opt_debug_heap;
}
if (1 == argc && argv && 0 == argv [0]) {
// help mode: set argv[0] to the text of the help message
static const char helpstr[] = {
"Enables the heap consistency checking on every memory allocation\n"
"and deallocation request.\n"
};
argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
return 0;
}
// set mode: enable the option
opt_debug_heap = 1;
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF
| _CRTDBG_CHECK_ALWAYS_DF
| _CRTDBG_LEAK_CHECK_DF);
return 0;
}
#endif // _MSC_VER && _DEBUG
#ifdef _WIN32
static int
_rw_setopts_windows ()
{
int nopts =
rw_setopts ("|-no-popups ",
_rw_opt_no_popups,
0 /* detect missing handlers */);
if (1 > nopts) {
rw_fprintf (rw_stderr,
"%s:%d: rw_setopts() failed\n", __FILE__, __LINE__);
abort ();
return 1;
}
# if defined (_MSC_VER) && defined (_DEBUG)
nopts =
rw_setopts ("|-debug-heap ",
_rw_opt_debug_heap,
0 /* detect missing handlers */);
if (1 > nopts) {
rw_fprintf (rw_stderr,
"%s:%d: rw_setopts() failed\n", __FILE__, __LINE__);
abort ();
return 1;
}
# endif // _MSC_VER && _DEBUG
return 0;
}
#endif // _WIN32
/************************************************************************/
_TEST_EXPORT int
rw_vtest (int argc, char **argv,
const char *file_name,
const char *clause,
const char *comment,
int (*fun)(int, char**),
const char *optstr,
va_list va)
{
CHECK_INIT (false, "rw_vtest()");
// set the default test output to stdout
RW_ASSERT (0 == _rw_ftestout);
RW_ASSERT (0 != rw_stdout);
_rw_ftestout = rw_stdout;
_rw_driver_init = 1;
if (optstr && 0 > rw_vsetopts (optstr, va)) {
rw_fprintf (rw_stderr,
"%s:%d: rw_setopts() failed\n", __FILE__, __LINE__);
return 1;
}
const int nopts =
rw_setopts ("|-no-stdout "
"|-diags= " // argument required
"|-trace "
"|-severity= " // argument required
"|-csv "
"|-compat "
"|-ulimit= " // argument required
"o|-output:" // argument optional
"b|-brief "
"q|-quiet "
"v|-verbose",
_rw_opt_no_stdout,
_rw_setopt_diags,
_rw_setopt_trace,
_rw_setopt_trace_mask,
_rw_setopt_csv,
_rw_opt_compat,
_rw_setopt_ulimit,
_rw_setopt_output_file,
_rw_opt_brief,
_rw_opt_quiet,
_rw_opt_verbose,
0);
if (3 > nopts) {
rw_fprintf (rw_stderr,
"%s:%d: rw_setopts() failed\n", __FILE__, __LINE__);
abort ();
return 1;
}
_rw_setopts_compat ();
_rw_setopts_types ();
_rw_setopts_lines ();
#ifdef _WIN32
_rw_setopts_windows ();
#endif // _WIN32
int status = rw_runopts (argc, argv);
if (status)
return status;
if (rw_stdout == _rw_ftestout) {
if (_rw_opt_no_stdout (0, 0) && file_name) {
char fname [256];
const char* const slash = strrchr (file_name, _RWSTD_PATH_SEP);
strcpy (fname, slash ? slash + 1 : file_name);
char* const dot = strchr (fname, '.');
if (dot)
strcpy (dot, ".out");
else
strcat (fname, ".out");
_rw_ftestout = (rw_file*)(void*)fopen (fname, "w");
}
else
_rw_ftestout = rw_stdout;
}
if (clause)
strcpy (clause_id, clause);
static const char thick_line[] = {
"############################################################"
};
static const char begin_fmt[] = {
"\n"
"# COMPILER: %s\n"
"# ENVIRONMENT: %s\n"
"# FILE: %s\n"
"# COMPILED: %s, %s\n"
"# COMMENT: %s\n"
"%s\n"
};
// allow file_name to be null
const char* const fname =
file_name ? strrchr (file_name, _RWSTD_PATH_SEP) : 0;
rw_info (0, 0, 0,
begin_fmt,
RW_TEST_COMPILER, RW_TEST_HARDWARE,
fname ? fname + 1 : file_name,
__DATE__, __TIME__,
comment ? comment : "",
thick_line);
status = setjmp (test_env);
if (0 == status) {
// environment set, execute the callback function
status = fun (argc, argv);
}
else {
// fatal test error (via a call to rw_fatal())
}
_rw_driver_done = 1;
static const char tblrow[] =
"+-----------------------+----------+----------+----------+";
rw_fprintf (_rw_ftestout,
"# %s\n"
"# | DIAGNOSTIC | ACTIVE | TOTAL | INACTIVE |\n"
"# %s\n",
tblrow, tblrow);
int nlines = 0;
for (int i = 0; i != N_DIAG_TYPES; ++i) {
if (ndiags [i][0] || !(_rw_diag_mask & (1 << diag_trace))) {
// print out details for any non-zero totals
// or for all totals when debugging or tracing
// is enabled
++nlines;
const long inactive = ndiags [i][0] - ndiags [i][1];
const long total = long (ndiags [i][0]);
const long pct = total ? long ((inactive * 100.0) / total) : 0;
const char* pfx = "";
const char* sfx = "";
static int use_color = _rw_use_color ();
if (use_color) {
pfx = ndiags [i][1] ? diag_msgs [i].esc_pfx : "";
sfx = ndiags [i][1] ? diag_msgs [i].esc_sfx : "";
}
rw_fprintf (_rw_ftestout,
"# | (S%d) %-*s |%s %8d %s| %8d | %7ld%% |\n",
i, int (sizeof diag_msgs [i].code),
diag_msgs [i].code,
pfx, ndiags [i][1], sfx, ndiags [i][0], pct);
}
}
if (0 == nlines)
rw_fprintf (_rw_ftestout, "# no diagnostics\n");
rw_fprintf (_rw_ftestout, "# %s\n", tblrow);
if (_rw_opt_compat (0, 0)) {
// TO DO: get rid of this
// RWTest compatibility format
rw_fprintf (_rw_ftestout,
"%s\n"
"## Warnings = %d\n"
"## Assertions = %d\n"
"## FailedAssertions = %d\n",
thick_line,
ndiags [diag_warn][1] + ndiags [diag_xwarn][1],
ndiags [diag_assert][0],
ndiags [diag_assert][1] + ndiags [diag_xassert][1]);
}
if (_rw_ftestout && _rw_ftestout != rw_stdout) {
fclose ((FILE*)(void*)_rw_ftestout);
_rw_ftestout = 0;
}
return status;
}
/************************************************************************/
_TEST_EXPORT int
rw_test (int argc, char **argv,
const char *fname,
const char *clause,
const char *comment,
int (*testfun)(int, char**),
const char *optstr,
...)
{
CHECK_INIT (false, "rw_test()");
va_list va;
va_start (va, optstr);
const int status =
rw_vtest (argc, argv, fname, clause, comment, testfun, optstr, va);
va_end (va);
return status;
}
/************************************************************************/
// escape every occurrence of the double quote character in the string
// pointed to by buf by prepending to it the escape character specified
// by the last acrgument
// returns the new buffer if the size of the existing buffer isn't
// sufficient and sets *pbufsize to the size of the newly allocated
// buffer, otherwise the original value of buf and leaves *pbufsize
// unchanged
static char*
_rw_escape (char *buf, size_t bufsize, char esc)
{
// handle null buffer
if (0 == buf)
return buf;
// count the number of embedded quotes
char *quote = buf;
size_t nquotes = 0;
while ((quote = strchr (quote, '"'))) {
++nquotes;
++quote;
}
// no quotes found, return the original buffer
if (0 == nquotes)
return buf;
// conpute the size of the buffer that will be needed to escape
// all the double quotes
size_t newbufsize = strlen (buf) + nquotes + 1;
char *newbuf = 0;
if (0 /* newbufsize <= bufsize */) {
// FIXME: escape embedded quotes in place w/o reallocation
_RWSTD_UNUSED (bufsize);
}
else {
newbuf = (char*)malloc (newbufsize);
if (0 == newbuf) {
return 0;
}
// set the next pointer to the beginning of the new buffer
// as the destination where to copy the string argument
char *next = newbuf;
// set quote to initially point to the beginning of
// the source buffer and then just past the last quote
quote = buf;
for (char *q = buf; ; ++q) {
// look for the next (or first) quote
q = strchr (q, '"');
// compute the number of characters, excluding the quote
// to copy to the destination buffer
const size_t nchars = q ? size_t (q - quote) : strlen (quote);
memcpy (next, quote, nchars);
if (q) {
// append the escape character to the destination buffer
next [nchars] = esc;
// append the quote from the source string
next [nchars + 1] = '"';
// advance the destination pointer past the quote
next += nchars + 2;
// advance the source pointer past the embedded quote
quote = q + 1;
}
else {
// NUL-terminate the destination buffer
*next = '\0';
break;
}
}
}
return newbuf;
}
/************************************************************************/
static void
_rw_vissue_diag (diag_t diag, int severity, const char *file, int line,
const char *fmt, va_list va)
{
CHECK_INIT (true, "_rw_vissue_diag()");
if (0 == fmt)
fmt = "";
static char fmterr[] = "*** formatting error ***";
char *usrbuf = 0;
const int nchars = rw_vasnprintf (&usrbuf, 0, fmt, va);
if (nchars < 0 || 0 == usrbuf)
usrbuf = fmterr;
// compute the number of newline characters in the text
int nlines = 0;
for (const char *nl = usrbuf; (nl = strchr (nl, '\n')); ++nl)
++nlines;
static const int use_color = _rw_use_color ();
const char* const diagstr[] = {
use_color ? diag_msgs [severity].esc_pfx : "",
*diag_msgs [severity].code ? diag_msgs [severity].code : "UNKNOWN",
use_color ? diag_msgs [severity].esc_sfx : "",
_rw_opt_verbose (0, 0) && *diag_msgs [severity].desc ?
diag_msgs [severity].desc : 0
};
const char* const traced_diag =
0 == severity && diag_msgs [diag].code ? diag_msgs [diag].code : 0;
const char* const slash = file ? strrchr (file, _RWSTD_PATH_SEP) : 0;
if (slash)
file = slash + 1;
char *mybuf = 0;
if (_rw_opt_csv) {
// format all fields as comma separated values (CSV):
// -- a field containing the quote character, the comma,
// or the newline or linefeed character must be enclosed
// in a pair of double quotes
// -- every occurrence of the double quote character in a field
// must be escaped by prepening another double quote character
// to it
// escape all double quotes by prepending the double
// quote character to each according to the CSV format
char* const newbuf = _rw_escape (usrbuf, 0, '"');
if (newbuf != usrbuf) {
free (usrbuf);
usrbuf = newbuf;
}
mybuf =
rw_sprintfa ("%d, " // severity
"\"%s%s" // diagnostic
"%{?}_%s%{;}%s\", " // traced diagnostic
"\"%s\", " // clause
"\"%s\", " // file
"%d, " // line
"\"%s\"", // user text
severity,
diagstr [0], diagstr [1],
0 != traced_diag, traced_diag, diagstr [2],
clause_id,
0 != file ? file : "",
line,
usrbuf);
}
else {
nlines += 2 + ('\0' != *clause_id) + (0 != file) + (0 < line);
mybuf =
rw_sprintfa ("# %s" // escape prefix
"%s" // diagnostic
"%{?}_%s%{;}" // traced diagnostic
"%s " // escape suffix
"(S%d)" // severity
"%{?}, %s%{;} " // description
"(%d lines):\n" // number of lines
"# TEXT: %s\n" // user text
"%{?}# CLAUSE: %s\n%{;}" // clause if not empty
"%{?}# FILE: %s\n%{;}" // file if not null
"%{?}# LINE: %d\n%{;}", // line if positive
diagstr [0],
diagstr [1],
0 != traced_diag, traced_diag,
diagstr [2],
severity,
0 != diagstr [3], diagstr [3],
nlines,
usrbuf,
'\0' != *clause_id, clause_id,
0 != file, file,
0 < line, line);
}
#if 0 // disabled
else {
mybuf =
rw_sprintfa ("# %s%s" // diagnostic
"%{?}_%s%{;}%s " // traced diagnostic
"(S%d): " // severity
"%{?}[%s] %{;}" // clause if not empty
"%{?}(%d lines): %{;}" // number of lines if > 1
"%{?}%s:" // if (file) then file
"%{?}%d:%{;} " // if (0 < line) line
"%{:}" // else
"%{?}line %d: %{;}" // if (0 < line) line
"%{;}" // endif
"%s", // user text
diagstr [0], diagstr [1],
0 != traced_diag, traced_diag, diagstr [2],
severity,
'\0' != *clause_id, clause_id,
1 < nlines, nlines,
0 != file, file,
0 < line, line,
0 < line, line,
usrbuf);
}
#endif // 0/1
rw_fprintf (_rw_ftestout, "%s\n", mybuf);
if (mybuf != fmterr)
free (mybuf);
if (usrbuf != fmterr)
free (usrbuf);
}
/************************************************************************/
static void
_rw_vdiag (diag_t diag, int severity, const char *file, int line,
const char *fmt, va_list va)
{
// ignore this diagnostic if it's present in _rw_diag_ignore
if (_rw_diag_ignore & (1 << diag))
return;
CHECK_INIT (true, "_rw_vdiag()");
// check if the diagnostic is expected
const int expected = 0 != _rw_expected (line);
if (expected) {
if (severity) {
// if the diagnostic is expected to be active,
// adjust its type and severity
if (diag_assert == diag)
diag = diag_xassert;
else if (diag_warn == diag)
diag = diag_xwarn;
severity = diag * severity;
}
else {
// if the diagnostic is expected to be active but isn't,
// adjust its type to an unexpectdly inactive one
if (diag_assert == diag || diag_warn == diag)
diag = diag_expect;
severity = diag;
}
}
else if (diag) {
// normalize the severity
severity = diag * severity;
}
if (severity < 0)
severity = 0;
else if (N_DIAG_TYPES <= severity)
severity = N_DIAG_TYPES - 1;
// increment the diagnostic counter
++ndiags [diag][0];
if (severity) {
++ndiags [diag][1];
}
const int sevbit = (1 << severity);
if (0 == (sevbit & _rw_diag_mask)) {
// issue the diagnostic
_rw_vissue_diag (diag, severity, file, line, fmt, va);
}
if (diag_fatal == diag && severity) {
// fatal error, terminate test
longjmp (test_env, severity);
}
}
/************************************************************************/
_TEST_EXPORT int
rw_fatal (int success, const char *file, int line, const char *fmt, ...)
{
CHECK_INIT (true, "rw_fatal()");
va_list va;
va_start (va, fmt);
_rw_vdiag (diag_fatal, 0 == success, file, line, fmt, va);
va_end (va);
return success;
}
/************************************************************************/
_TEST_EXPORT int
rw_error (int success, const char *file, int line, const char *fmt, ...)
{
CHECK_INIT (true, "rw_error()");
va_list va;
va_start (va, fmt);
_rw_vdiag (diag_error, 0 == success, file, line, fmt, va);
va_end (va);
return success;
}
/************************************************************************/
_TEST_EXPORT int
rw_assert (int success, const char *file, int line, const char *fmt, ...)
{
CHECK_INIT (true, "rw_assert()");
va_list va;
va_start (va, fmt);
_rw_vdiag (diag_assert, 0 == success, file, line, fmt, va);
va_end (va);
return success;
}
/************************************************************************/
_TEST_EXPORT int
rw_warn (int success, const char *file, int line, const char *fmt, ...)
{
CHECK_INIT (true, "rw_warn()");
va_list va;
va_start (va, fmt);
_rw_vdiag (diag_warn, 0 == success, file, line, fmt, va);
va_end (va);
return success;
}
/************************************************************************/
_TEST_EXPORT int
rw_note (int success, const char *file, int line, const char *fmt, ...)
{
CHECK_INIT (true, "rw_note()");
va_list va;
va_start (va, fmt);
_rw_vdiag (diag_note, 0 == success, file, line, fmt, va);
va_end (va);
return success;
}
/************************************************************************/
_TEST_EXPORT int
rw_info (int success, const char *file, int line, const char *fmt, ...)
{
CHECK_INIT (true, "rw_info()");
va_list va;
va_start (va, fmt);
_rw_vdiag (diag_info, 0 == success, file, line, fmt, va);
va_end (va);
return success;
}
/************************************************************************/
_TEST_EXPORT bool
rw_enable (int (*fun) (int, const char*, int, const char*, ...), bool enable)
{
diag_t diag;
if (&rw_fatal == fun)
diag = diag_fatal;
else if (&rw_error == fun)
diag = diag_error;
else if (&rw_assert == fun)
diag = diag_assert;
else if (&rw_warn == fun)
diag = diag_warn;
else if (&rw_note == fun)
diag = diag_note;
else if (&rw_info == fun)
diag = diag_info;
else {
RW_ASSERT (!"Invalid function in rw_enable");
return false;
}
const bool enabled = 0 == (_rw_diag_ignore & (1 << diag));
// if (enable)
// _rw_diag_ignore &= ~(1 << diag);
// else
// _rw_diag_ignore |= 1 << diag;
_rw_diag_ignore ^= ((enable - 1) ^ _rw_diag_ignore) & (1 << diag);
return enabled;
}