# /*
#               Testprogram for ANSI compatibility and other
#                     features of a system's C compiler
#
#                         Joerg Schoen (C) 1996-98
#
#
#  Test certain features of the ANSI-C standard:
#
#  1. Does the preprocessor behave like an ANSI one, i.e. does it
#     reject token concatenation via comments and macro argument
#     expansion within strings (relicts from Kernighan & Ritchie times)?
#     This check is only done if the compiler claims to be ANSI ("__STDC__"
#     defined).
#  2. Check for all include files and library functions of the ANSI standard.
#     The functions are called with appropriate arguments to check for their
#     existance while preventing the compiler to claim illegal arguments and
#     so on. The function calls are never executed, since they do nothing
#     useful.
#  3. Regardless if the compiler claims to be ANSI ("__STDC__"), test
#     for the ANSI predefined macros "__FILE__" etc. and print a warning
#     message if they are missing.
#  4. ANSI claims that FILENAME_MAX is reasonable big to hold a file name,
#     some systems define it to a very small number (like 14). The program
#     prints a warning message, if this value is lower than 255.
#  5. Also test some quite sophisticated ANSI-C features like the redefinition
#     of typedef names as variables and enumeration constants in an inner block
#     and usage of typedef names for statement labels. Surprisingly, a lot of
#     compilers that claim to be ANSI ones fail in this step (see below)!
#
#  Since I have used this tool in checking the behaviour of certain compilers,
#  it reports additionally, if '-D' and '-U' command line options are treated
#  in the sequence they appear (only relevant if the define/undefine the same
#  name) or if all '-D' are done before any '-U' options.
#
#  On output, the program prints the compiler's definition of "__STDC__"
#  to stdout and some common extension of predefined symbols that have
#  been detected.
#
#  The return value of the program is 0 if everything is OK, otherwise it
#  will be non-zero.
#
#
#   Notes:
#    - The program must be linked with "-lm" to include the mathematical
#      library functions.
#    - Do not optimize, since sophisticated compilers may remove
#      parts of the code so that some functions won't be referenced and
#      thus it is not detected that they are missing. Altough I have taken
#      care that optimization should not remove code lines, I cannot guarantee
#      that.
#    - If your compiler complains about unused variables, unreachable
#      code etc., don't worry! The code is only meant to call all routines
#      of the ANSI standard library, not to do reasonable things with them.
#    - If your compiler fails to accept enumeration constants in inner
#      blocks that redefine typedef names, define
#
#        NO_TYPE_REDEFINES_ENUM
#
#      to the preprocessor to skip this test.
#    - If your compiler fails to accept ANY object (i.e. variable, another
#      typedef of enumeration constant) in inner blocks that redefines a
#      typedef name in an outer block, define
#
#        NO_TYPE_REDEFINES
#
#      to the preprocessor to skip this section of the code.
#    - If your compiler fails to accept typedef names as statement labels,
#      define
#
#        NO_LABEL_NAMESPACE
#
#      to skip this test.
#    - ANSI requires that in a parameter declaration a single typename in
#      parenthesis should be taken as an abstract declarator with a function
#      argument. If this fails, define
#
#        NO_ABSTRACT_DECL_SPECIAL
#
#    - This program may be used as a Bourne shell script, too.
#      The arguments are
#
#        ./TestAnsi.c [<level>] [-c<symbol_or_assertion> ...] [<compiler1> ...]
#
#      <level> gives roughly the conformance of the compiler. Only one of the
#      values 0, 1, 2, 10, 11, 12 may be used. 1 and 11 skips the
#      enumeration-constant-redefines-typedef-name test, 2 and 12 also the
#      variable-redefines-typedef-name test and 10, 11 and 12 skips the
#      label-is-typedef-name.
#      "0" means total conformance, 12 means very bad.
#      The program may check for predefined symbols, like "__unix" with the
#      option "-c<symbol>" and for predefined assertions (is valid only on
#      some systems) with option "-cpredicate(answer)". The latter form
#      should be quoted to prevent the interpretation of the brackets by the
#      shell.
#    - To find more information, the program "enquire.c" gives a lot of
#      information about internal limits of a system and deals also with
#      non-ANSIism.
#
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#
#   Bugs, questions, improvements, mail to
#        my-first-name AT die-Schoens DOT de
#
#
#   A test on some systems I have access to gave the following results:
#
#==============================================================================
#        |             |  Macro   | Type re | Type re | Label |
# System |  Compiler   |"__STDC__"| defines | defines | name  | Remarks
#        |             |          | as enum | as vars | space |
#==============================================================================
# AIX    | cc          |    -     |   OK    |   OK    |  OK   | (1)
# 3.2,   | xlc         |    1     |   OK    |   OK    |  OK   | (1)
# 4.1    | c89         |    1     |   OK    |   OK    |  OK   | (1)
#------------------------------------------------------------------------------
# GNU    | gcc         |    1     |   OK    |   OK    |  OK   | (2)
# 2.7.2  |             |          |         |         |       |
#------------------------------------------------------------------------------
# SUN    | cc -X a     |    0     |   OK    |   OK    |  OK   |
# 5.5    | cc -X c     |    1     |   OK    |   OK    |  OK   |
#        | cc -X s     |          |         |         |       | cannot compile
#        | cc -X t     |    0     |   OK    |   OK    |  OK   | (3) (4)
#------------------------------------------------------------------------------
# HP     | c89         |    1     |   --    |   OK    |  OK   |
# A.09.05| cc          |          |         |         |       | cannot compile
#------------------------------------------------------------------------------
# CRAY   | cc          |    2     |   OK    |   OK    |  --   |
# 9.0.2.0| cc -h stdc  |    1     |   OK    |   OK    |  --   |
#        | c89         |    2     |   OK    |   OK    |  --   |
#------------------------------------------------------------------------------
# SGI    | cc          |    1     |   --    |   OK    |  --   |
# 5.2    |             |          |         |         |       |
#------------------------------------------------------------------------------
# DEC    | cc [-std0]  |    -     |   --    |   --    |  --   |
# V4.0   | cc -std     |    0     |   --    |   --    |  --   |
#        | cc -std1    |    1     |   --    |   --    |  --   |
#        | c89 [-std?] |    1     |   --    |   --    |  --   |
#        | c89 -std    |    0     |   --    |   --    |  --   |
#------------------------------------------------------------------------------#
#
# Remarks:
#   (1) "__TIMESTAMP__" defined.
#   (2) "__BASE_FILE__" and "__INCLUDE_LEVEL__" defined.
#   (3) Expands macro argument in strings.
#   (4) Token concatenation via comments works.
#
#
opts=""
case "$1" in
  0)
    opts=""
    shift
    ;;
  1)
    opts="-DNO_TYPE_REDEFINES_ENUM"
    shift
    ;;
  2)
    opts="-DNO_TYPE_REDEFINES"
    shift
    ;;
  10)
    opts="-DNO_LABEL_NAMESPACE"
    shift
    ;;
  11)
    opts="-DNO_LABEL_NAMESPACE -DNO_TYPE_REDEFINES_ENUM"
    shift
    ;;
  12)
    opts="-DNO_LABEL_NAMESPACE -DNO_TYPE_REDEFINES"
    shift
    ;;
esac
if [ -r testansi.h ]
then
  rm testansi.h
fi
copt=""
while :
do
  case $1 in
    -c*\(*\))
      #  Assertion to check for
      copt="-DTEST_FOR_SYMBOLS "
      a="`expr \"$1\" ':' '-c\(.*\)'`"
      shift
      #  Assertion
      cat >>testansi.h <<EOF
#if #$a
  { "$a", NULL },
#endif
EOF
      ;;
    -c*)
      #  Symbol to check for
      copt="-DTEST_FOR_SYMBOLS "
      s="`expr $1 ':' '-c\(.*\)'`"
      shift
      cat >>testansi.h <<EOF
#ifdef $s
  { "$s", STRING($s) },
#endif
EOF
      ;;
    *)
      break
      ;;
  esac
done
#  Any compilers to check?
if [ $# -eq 0 ]
then
  set -- cc
fi

opts="-USECOND_SYMBOL -DFIRST_SYMBOL -DSECOND_SYMBOL -UFIRST_SYMBOL $opts $copt"
for cc
do
  echo ">>$cc $opts$0 -lm"
  if $cc $opts$0 -lm
  then
    ./a.out
    rm a.out
  fi
done

if [ -r testansi.h ]
then
  rm testansi.h
fi

exit
# */

/*  This macro will produce the value of the macro as a string  */
#define STRING(x)   STRINGX(x)
#define STRINGX(x)  #x

#ifdef TEST_FOR_SYMBOLS
#define NULL  (void *)0L
/*  Include tests for symbols before any includes  */
struct {
  char *Name;
  char *Value;
} Symbols[] = {
# include "testansi.h"
  { NULL, 0 }
};
#undef NULL
#endif

/* **  Test all include files required by ANSI  ** */
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <float.h>
#include <limits.h>
#include <locale.h>
#include <math.h>
#include <setjmp.h>
#include <signal.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/* ***  Call all functions required by the standard library  *** */
int cmpfunc(const void *k,const void *d) { return(0); }
void doAtExit(void) { printf("Good bye!\n"); }
void testsignal(int sig) { printf("Oops %d!\n",sig); }

int testAnsi(int doTest,int next,...)
{
  char c = 'X';
  unsigned ui;
  long l = 10000;
  char *s;
  char s2[100];
  wchar_t ws[100];
  double x;
  int ret;

  ret = 0;

#ifndef NULL
  fprintf(stderr,"WARNING: Macro \"NULL\" undefined!\n");  ret = 10;
#endif

  /* **  stddef  ** */
#ifndef offsetof
  fprintf(stderr,"WARNING: Macro \"offsetof\" undefined!\n");  ret = 10;
#endif

  /* **  stdio  ** */
#ifndef FILENAME_MAX
  fprintf(stderr,"WARNING: Macro \"FILENAME_MAX\" undefined!\n");  ret = 10;
#else
  /*  Check if value has a reasonable value.
   *  Don't check in '#if' line, since we do
   *   not know how macro expands.
   */
  if(FILENAME_MAX < 255) {
    fprintf(stderr,"WARNING: Macro \"FILENAME_MAX\" defined, but only %d!\n",
	    FILENAME_MAX);
    ret = 10;
  }
#endif
#ifndef FOPEN_MAX
  fprintf(stderr,"WARNING: Macro \"FOPEN_MAX\" undefined!\n");  ret = 10;
#endif
#ifndef EOF
  fprintf(stderr,"WARNING: Macro \"EOF\" undefined!\n");  ret = 10;
#endif
#ifndef L_tmpnam
  fprintf(stderr,"WARNING: Macro \"L_tmpnam\" undefined!\n");  ret = 10;
#endif
#ifndef TMP_MAX
  fprintf(stderr,"WARNING: Macro \"TMP_MAX\" undefined!\n");  ret = 10;
#endif
#ifndef _IOFBF
  fprintf(stderr,"WARNING: Macro \"_IOFBF\" undefined!\n");  ret = 10;
#endif
#ifndef _IOLBF
  fprintf(stderr,"WARNING: Macro \"_IOLBF\" undefined!\n");  ret = 10;
#endif
#ifndef _IONBF
  fprintf(stderr,"WARNING: Macro \"_IONBF\" undefined!\n");  ret = 10;
#endif
#ifndef BUFSIZ
  fprintf(stderr,"WARNING: Macro \"BUFSIZ\" undefined!\n");  ret = 10;
#endif
#ifndef SEEK_SET
  fprintf(stderr,"WARNING: Macro \"SEEK_SET\" undefined!\n");  ret = 10;
#endif
#ifndef SEEK_CUR
  fprintf(stderr,"WARNING: Macro \"SEEK_CUR\" undefined!\n");  ret = 10;
#endif
#ifndef SEEK_END
  fprintf(stderr,"WARNING: Macro \"SEEK_END\" undefined!\n");  ret = 10;
#endif
  if(doTest & 1) {
    FILE *fp;
    fpos_t fpt;
    char name[FILENAME_MAX] = "filename";
    va_list vl;

    fp = NULL;
    fp = fopen(name,"w");
    fp = freopen(name,"w",fp);
    fflush(stdin);
    fclose(fp);
    remove(name);
    rename(name,name);
    fp = tmpfile();
    s = tmpnam(s2);
    setvbuf(stdout,s2,_IOFBF,sizeof(s2));
    setbuf(stderr,s2);
    fprintf(fp,s); fscanf(fp,s); printf(s); scanf(s); sprintf(s2,s);
    sscanf(s,s); vfprintf(fp,s,vl); vprintf(s,vl); vsprintf(s2,s,vl);
    fgetc(fp); fgets(s2,next,fp); fputc(next,fp);
    fputs(s,fp); getc(fp); getchar(); gets(s2); putc(c,fp); putchar(c);
    puts(s); ungetc(c,fp); fread(s2,next,1,fp); fwrite(s,next,1,fp);
    fgetpos(fp,&fpt);
    fseek(fp,next,SEEK_SET); fsetpos(fp,&fpt);
    if(ftell(fp) > 10) rewind(fp);
    clearerr(fp);
    if(feof(fp) || ferror(fp)) perror(s);
  }

  /* **  ctype  ** */
  if(doTest & 2) {
    int ret2;

    switch(next) {
    case 0:  ret2 = isalnum(c);  break;
    case 1:  ret2 = isalpha(c);  break;
    case 2:  ret2 = iscntrl(c);  break;
    case 3:  ret2 = isdigit(c);  break;
    case 4:  ret2 = isgraph(c);  break;
    case 5:  ret2 = islower(c);  break;
    case 6:  ret2 = isprint(c);  break;
    case 7:  ret2 = ispunct(c);  break;
    case 8:  ret2 = isspace(c);  break;
    case 9:  ret2 = isupper(c);  break;
    case 10: ret2 = isxdigit(c); break;
    case 11: ret2 = tolower(c);  break;
    case 12: ret2 = toupper(c);  break;
    }
  }

  /* **  string  ** */
  if(doTest & 4) {
    memcpy(s2,s,strlen(s)); memmove(s2,s,1);
    strcpy(s2,strerror(next)); strncpy(s2,s,1);
    strcat(s2,s); strncat(s2,s,1);
    if(memcmp(s2,s,3) || strcmp(s2,s) || strncmp(s2,s,1) || strcoll(s,s2))
      strxfrm(s2,s,1);
    if(memchr(s2,c,1) || strchr(s,c) || strcspn(s,s2) > 4 ||
       strpbrk(s,s2) || strrchr(s,c) || strspn(s,s2) > 3 ||
       strstr(s,s2) || strtok(s,s2))
      memset(s,c,next);

    mbtowc(ws,s,10 * mblen(s,next));
    wctomb(s,ws[0]); mbstowcs(ws,s,next); wcstombs(s,ws,next);
  }

  /* **  math  ** */
#ifndef EDOM
  fprintf(stderr,"WARNING: Macro \"EDOM\" undefined!\n");  ret = 10;
#endif
#ifndef ERANGE
  fprintf(stderr,"WARNING: Macro \"ERANGE\" undefined!\n");  ret = 10;
#endif
#ifndef HUGE_VAL
  fprintf(stderr,"WARNING: Macro \"HUGE_VAL\" undefined!\n");  ret = 10;
#endif
  if(doTest & 8) {
    x = acos(x) - asin(x) + atan(x) * atan2(x,x);
    x += cos(x) / sin(x) + tan(x) - cosh(x) / sinh(x) / tanh(x) * exp(x);
    x *= frexp(x,&next) * ldexp(x,next) - log(x) / log10(x) + modf(x,&x);
    x /= pow(x,x) - sqrt(x) - ceil(x) - fabs(x) - floor(x) - fmod(x,x);
  }

  /* **  stdlib  ** */
  if(doTest & 16) {
    div_t d;
    ldiv_t ld;
    int i = next;

    x = atof(s) - atoi(s) - atol(s) - strtod(s,NULL) + strtol(s,NULL,10);
    next = strtoul(s,NULL,10) - rand();
    srand(ui);
    free(calloc(1,1));
    if(realloc(malloc(3),4) == NULL) abort();
    atexit(doAtExit);
    if(getenv("HOME") == NULL) exit(1);
    system("echo");
    if(bsearch(s,s2,sizeof(s2),1,cmpfunc)) {
      d = div(i,i);
      ld = ldiv(l,l);
      if(abs(d.quot * d.rem) > 5 || labs(ld.quot * ld.rem) > 0)
	qsort(s2,sizeof(s2),1,cmpfunc);
    }
  }

  /* **  assert  ** */
  if(doTest & 32) {
    assert(next == 5);
  }

  /* **  stdarg  ** */
#ifndef va_start
  fprintf(stderr,"WARNING: Macro \"va_start\" undefined!\n");  ret = 10;
#endif
#ifndef va_arg
  fprintf(stderr,"WARNING: Macro \"va_arg\" undefined!\n");  ret = 10;
#endif
#ifndef va_end
  fprintf(stderr,"WARNING: Macro \"va_end\" undefined!\n");  ret = 10;
#endif
  if(doTest & 64) {
    va_list ap;
    double d;

    va_start(ap,next);

    s = va_arg(ap,char*);
    d = va_arg(ap,double);

    va_end(ap);
  }

  /* ** setjmp  ** */
  if(doTest & 128) {
    jmp_buf jb;
    if(setjmp(jb) == 0) longjmp(jb,next);
  }

  /* **  signal  ** */
#ifndef SIG_DFL
  fprintf(stderr,"WARNING: Macro \"SIG_DFL\" undefined!\n");  ret = 10;
#endif
#ifndef SIG_IGN
  fprintf(stderr,"WARNING: Macro \"SIG_IGN\" undefined!\n");  ret = 10;
#endif
#ifndef SIGABRT
  fprintf(stderr,"WARNING: Macro \"SIGABRT\" undefined!\n");  ret = 10;
#endif
#ifndef SIGFPE
  fprintf(stderr,"WARNING: Macro \"SIGFPE\" undefined!\n");  ret = 10;
#endif
#ifndef SIGILL
  fprintf(stderr,"WARNING: Macro \"SIGILL\" undefined!\n");  ret = 10;
#endif
#ifndef SIGINT
  fprintf(stderr,"WARNING: Macro \"SIGINT\" undefined!\n");  ret = 10;
#endif
#ifndef SIGSEGV
  fprintf(stderr,"WARNING: Macro \"SIGSEGV\" undefined!\n");  ret = 10;
#endif
#ifndef SIGTERM
  fprintf(stderr,"WARNING: Macro \"SIGTERM\" undefined!\n");  ret = 10;
#endif
  if(doTest & 256) {
    signal(SIGINT,testsignal); raise(next);
    raise(SIGABRT);
  }

  /* **  time  ** */
  if(doTest & 512) {
    struct tm *ptm;
    clock_t ct;
    time_t tt;

    ct = clock();
    tt = time(NULL);
    x  = difftime(tt,tt);
    ptm = gmtime(&tt);
    ptm = localtime(&tt);
    tt = mktime(ptm);
    s  = asctime(ptm);
    s  = ctime(&tt);
    strftime(s2,sizeof(s2),"%a%A%b%B%c%d%H%I%j%m%M%p%S%U%w%W%x%X%y%Y%Z%%",ptm);
  }

  /* **  locale ** */
  if(doTest & 1024) {
    setlocale(next,s); localeconv();
  }

  return(ret);
}

/* *  Test acceptance of old style definitions and some lazy definitions * */
k_and_r_routine(a,b,c)
int a,b;
const double *c;
{
  printf("Once upon a time in %d and %g\n",a+b,*c);
  return(0);
}

/*  Dropping explicit types defaults to "int"  */
lazyStyle(first_int,second_int)
{
static int A[10];

  return(A[first_int + second_int]);
}

/* ***  Test almost all valid declaration specifiers  *** */
void *a;

char b;                unsigned char b1;       signed char b2;

short c;               unsigned short c1;      signed short c2;
unsigned short int c3; signed short int c4;

long d;                unsigned long d1;       signed long d2;
unsigned long int d3;  signed long int d4;

int e;                 unsigned int e1;        signed int e2;
unsigned e3;           signed e4;

float f;

double g;              float extern h;

unsigned const short volatile int Totally_Garbled;

/* **  Test redefinition of typedefs  ** */
typedef int INTEGER;
typedef double DOUBLE;
const typedef double CDOUBLE;
int const typedef CINTEGER;
char const typedef unsigned volatile UCCHAR;

void test1(
#ifndef NO_TYPE_REDEFINES
	   double DOUBLE
#else
	   void
#endif
	   )
{
  int a1,bx = -1000;
#ifndef NO_TYPE_REDEFINES
  double CDOUBLE;
  const INTEGER UCCHAR = 0;
#endif

#ifndef NO_TYPE_REDEFINES
  a1 = UCCHAR + bx;
  CDOUBLE = DOUBLE;
#else
  a1 = bx;
#endif

#ifndef NO_LABEL_NAMESPACE
  /* **  Test separate name space for labels  ** */
CINTEGER:
#else
label:
#endif
  printf("BINGO\n");
  a1++;

  if(a1 > 5)
#ifndef NO_LABEL_NAMESPACE
    goto CINTEGER;
#else
    goto label;
#endif
}
CDOUBLE doubdoub;

/* ***  More complicated typedef redefinitions  *** */
typedef char   One;
typedef int    Two;
typedef long   Three;
typedef float  Four;
typedef double Five;
typedef unsigned char Six;

#ifndef NO_TYPE_REDEFINES
/*  Test if typedef is visible at the end of the declarator  */
typedef float Tarzan,Jane(Tarzan);
#endif

int test2(void)
{
  int var1,var2;
#ifndef NO_TYPE_REDEFINES
  typedef unsigned char One;
  typedef int (*Two)(One arg);
  struct Bongo {
    int A,B,Two,Three;
# ifndef NO_TYPE_REDEFINES_ENUM
    enum Bingo {
      /*  These redefinitions of typedefs must be visible in function block */
      Three, Four
    } XX;
# endif
    struct {
      /*  Even though "Two" was redefined in previous
       *   struct, should still work here!
       */
      Two two,three;
    } YY;
  } BB;
  unsigned int Five;
#endif

#ifndef NO_LABEL_NAMESPACE
One:
#else
label1:
#endif

#ifndef NO_TYPE_REDEFINES
  /*  Test if redefined names are accepted  */
  BB.Three = 1;
# ifndef NO_TYPE_REDEFINES_ENUM
  Five = Three;
  Five = Four;
# endif

  if(Five != BB.Three)
#else
  var1 = 1;
  var2 = 2;

  if(var1 != var2)
#endif

#ifndef NO_LABEL_NAMESPACE
    goto One;
#else
    goto label1;
#endif

#if !defined(NO_TYPE_REDEFINES) && !defined(NO_TYPE_REDEFINES_ENUM)
  else if(Five != Three)
#else
  else if(var1 == 2*var2)
#endif

#ifndef NO_LABEL_NAMESPACE
    goto Six;
#else
    goto label2;
#endif

  return(0);

#ifndef NO_LABEL_NAMESPACE
Six:
#else
label2:
#endif
  return(-1);
}

/* **  Test if typedefs retain their meaning outside function block ** */
Three X,Y;
Four K,L;

#ifndef NO_ABSTRACT_DECL_SPECIAL
/*  Test that in a parameter declaration a single typename in
 *   parenthesis is taken as an abstract declarator with a function
 *   argument. This is one of the more sophisticated features required
 *   by the standard.
 */
typedef float T;
int dodo      (register (T[5]));
int dodo_equiv(register id1(T id2[5]));
int dudu      (register (T(int)));
int dudu_equiv(register id1(T id2(int id3)));
#endif

/* ********************************************************************** */
/*  We are through! Now finally a program that checks "__STDC__" and
 *   other symbols to check for and prints the values.
 */

int MACRO1(int arg) { return(arg - 1); }

int main(void)
{
  int ret;

  ret = 0;

  /* ***  Test for some old fashioned or wrong behaviours  *** */
  /*  Expansion of macro argument within string  */
#define macro(arg)   "expand arg in string?"
  if(strcmp("expand arg in string?",macro(different)) != 0) {
    fprintf(stderr,"WARNING: Compiler expands arguments in strings!\n");
    ret = 10;
  }
#undef macro

  /*  Using comment to concatenate tokens  */
  {
#define OLDCONCAT(a,b)    a/**/b
#define longint           double
    OLDCONCAT(long,int)  var = 3.1415926;

    if(var != 3) {
      fprintf(stderr,"WARNING: Token concatenation via \"/**/\" works!\n");
      ret = 10;
    }
  }
#undef OLDCONCAT
#undef longint

  /*  Test if the preprocessor allows nested comments
   *  If comments do not nest, all the preprocessor lines
   *   starting with '#' should be visible, causing the
   *   "if(0)" to mask out the error message.
   *  If comments do nest, the three preprocessor lines in between
   *   are invisible, thus masking out the "if(0)".
   */
#if 1
  /*
     /*  a nested comment  */
#else
#endif
#if 0
  
  */
#else
  if(0)
#endif
    fprintf(stderr,"WARNING: Preprocessor nests comments!\n");

  /*  Are C++ style comments recognized per default? This must
   *   not be, see below.
   */
  {
    int x = 2 //* This statement differs between ANSI C and C++ */ 2
      ;
    if(x != 1) {
      fprintf(stderr,"WARNING: C++ style comments recognized per default!\n");
      ret = 10;
    }
  }

  /*  Does preprocessor allow '$' in identifiers?  */
#define foo(a) #a
#define lose(b) foo(b)
#define test$
  if(strcmp("$",lose(test)) != 0) {
    fprintf(stderr,"WARNING: Preprocessor accepts '$' in identifiers!\n");
    ret = 10;
  }
#undef foo
#undef lose

  /*  No conditional compilation is allowed when collecting macro arguments  */
#define STRINGIZE(arg)    #arg
  {
    char *s;

    /*  The correct value should be something like
     *   "\"START-\" #if 0 \"END\" #endif"
     */
    s = STRINGIZE("START-"
#if 0
		  "END"
#endif
		  );

#define EXPECT       "\"START-\" #if 0 \"END\" #endif"

    if(strcmp(s,"\"START-\"") == 0 || strstr(s,"END") == NULL) {
      fprintf(stderr,"\
WARNING: Preprocessor allows preprocessing in macro args ('%s')!\n",s);
      ret = 10;
    } else if(strcmp(s,EXPECT) != 0)
      fprintf(stderr,"\
WARNING: Probably incorrectly stringified: '%s' != '%s'\n",s,EXPECT);
  }
#undef STRINGIZE
#undef EXPECT

  /*  Check if macro calls with arguments are recognized if there is
   *   white space before the opening bracket.
   */
#define MACRO1(arg)   (arg - 2)
  {
    int r1,r2,r3;

    r1 = MACRO1 (2); 
    r2 = MACRO1/*  This is a bit harder */(2); 
    r3 = MACRO1 /* This is even worse... */

                /*  ... at least for some compilers */ (2); 

    if(r1 || r2 || r3) {
      fprintf(stderr,"WARNING: Preprocessor doesn't allow whitespace before macro call bracket!\n");
      if(!(r1 && r2 && r3))
        fprintf(stderr,"  Whitespace treatment is not consistent!\n");
    }
  }

  /* ***  Now print some informational messages  *** */

#ifdef __STDC__ /* ***  Compiler claims to be ANSI  *** */
  /*  Value of "__STDC__"  */
  printf("-D__STDC__=%s\n",STRING(__STDC__));
#else
  /*  "__STDC__" is undefined  */
  printf("-U__STDC__\n");
  ret = 10;
#endif

  /*  Check treatment of '-D' and '-U' options on command line  */
#if !defined(FIRST_SYMBOL) && defined(SECOND_SYMBOL)
  /*  '-D' and '-U' commands are done in the sequence they appear  */
  printf("-DCONFIG_MACROOPT_SEQ\n");
#elif !defined(FIRST_SYMBOL) && !defined(SECOND_SYMBOL)
  /*  '-D' commands are done first, then all '-U' commands  */
  printf("-DCONFIG_MACROOPT_DFIRST\n");
#elif defined(FIRST_SYMBOL) && defined(SECOND_SYMBOL)
  /*  '-U' commands are done first, then all '-D' commands (strange!)  */
  printf("-DCONFIG_MACROOPT_UFIRST\n");
#else
  fprintf(stderr,"\
WARNING: Strange treatment of '-D' and '-U' command line options!\n");
#endif

  /* ***  Check some non-ANSI behaviour  *** */
  ret = testAnsi(0,4711) ? 10 : ret;

/*  Are the ANSI symbols present?  */
#ifndef __FILE__
  fprintf(stderr,"WARNING: Macro \"__FILE__\" undefined!\n");  ret = 10;
#endif
#ifndef __LINE__
  fprintf(stderr,"WARNING: Macro \"__LINE__\" undefined!\n");  ret = 10;
#endif
#ifndef __DATE__
  fprintf(stderr,"WARNING: Macro \"__DATE__\" undefined!\n");  ret = 10;
#endif
#ifndef __TIME__
  fprintf(stderr,"WARNING: Macro \"__TIME__\" undefined!\n");  ret = 10;
#endif

  /* ***  Check for some preprocessor extensions  *** */
#ifdef __INCLUDE_LEVEL__
  printf("-DCONFIG_USE_INCLUDE_LEVEL\n");
#endif
#ifdef __BASE_FILE__
  printf("-DCONFIG_USE_BASE_FILE\n");
#endif
#ifdef __TIMESTAMP__
  printf("-DCONFIG_USE_TIMESTAMP\n");
#endif

  /* ***  Test for user supplied preprocessor symbols  *** */
#ifdef TEST_FOR_SYMBOLS
  {
    int i;

    for(i = 0 ; Symbols[i].Name ; i++) {
      if(Symbols[i].Value == NULL) /*  Assertion  */
        printf("-A%s\n",Symbols[i].Name);
      else if(strcmp(Symbols[i].Value,"1") == 0) /*  Symbol is "1"  */
        printf("-D%s\n",Symbols[i].Name);
      else /*  Symbol has any other value  */
        printf("-D%s=%s\n",Symbols[i].Name,Symbols[i].Value);
    }
  }
#endif

  return(ret);
}
