#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/utsname.h>
#include <sys/systemcfg.h>
#include <math.h>

#define GLX_VERSION_1_0       /* No query capability in GLX 1.0, so hard code for now */
/* #define GLX_VERSION_1_1 */ /* Use this define when IBM Releases GLX 1.1            */
/* #define GLU_VERSION_1_1 */ /* Use this define when IBM Releases GLU 1.1            */
#define IBMSHM
#ifdef XWINDOWS
/*
 * Kludge to get access to the smt ptr in the display struct which is not
 * normally visible to clients. It must be defined before including Xlib.h.
 * This is the only reliable way for us to tell if we are running with SMT or
 * not. Its ugly, but it works, as long as know your library is built with
 * SMT support.
 */
#define SMT 1
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <GL/glx.h>
#include <X11/Xatom.h>
#endif

#include "Env.h"
#include <malloc.h>
#include "aux.h"

static char *GetShortVendorName(char *input);
static int GetHostMemorySize(void);
static char *GetHostVendor(void);
static char *GetHostModel(void);
static char *GetHostCPU(void);
static char *GetHostOperatingSystem(void);
static char *GetHostOperatingSystemRelease(void);
static char *GetHostName(void);
static char *GetOpenGLClientVendor(void);
static char *GetOpenGLClientVersion(void);
static char *GetOpenGLClientExtensions(void);
static char *GetHostCPUCount(void);
static char *GetHostPrimaryCacheSize(void);
static char *GetHostSecondaryCacheSize(void);
static char *GetWindowSystem(void);
static char *GetDriverVersion(void);

#if defined(XWINDOWS)
static int GetOpenGLBufferConfig(Display *dpy, XVisualInfo *visInfo,
                                 EnvironmentInfo *info);
static int GetXEnvironInfo(EnvironmentInfo *info);
#endif


/******************************************************************************
 * 
 * StringSearch - search for pattern in string
 *  
 * Description:
 *  StringSearch returns a pointer to the first occurance of pattern found in
 *  the subject or NULL if the pattern is not found. It implements the
 *  Knuth-Morris-Pratt pattern matching algorithm. If m is the length of the
 *  pattern and n is the length of the subject, the complexity of the KMP
 *  algorithm is (m+n), much better than the (m*n) complexity of the naive
 *  nested loop algorithm. - John Dennis
 *  
 * Returns:
 *  pointer to the first occurance of pattern found the subject or NULL
 *  
 * Side Effects:
 *  None
 *  
 * Errors:
 *  None
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Thu Aug  4 15:49:59 1994
 *    Initial Release
 *
 *****************************************************************************/
char *
StringSearch(char *subject, char *pattern)
{
#define MAX_FLINK (256)
  int *flink, *dynamicFlink = NULL, staticFlink[MAX_FLINK];
  int patternLen = strlen(pattern);
  int subjectLen = strlen(subject);
  int found = 0;
  int i,j;

  if (patternLen > MAX_FLINK) {      /* -1 for NULL terminator */
    dynamicFlink = malloc(patternLen * sizeof(int));
    if (dynamicFlink == NULL) {
      fprintf(stderr, "malloc failure, line %d, file: %s, exiting...\n",
	      __LINE__, __FILE__);
      exit(1);
    }
    flink = dynamicFlink;
  }
  else {
    flink = staticFlink;
  }


  /* Step 1: Constuct Flowchart */
  flink[0] = -1;		       /* -1 == read next char */
  for(i = 1; i < patternLen; i++) {
    j = flink[i-1];
    while((j >= 0) && (pattern[j] != pattern[i-1])) {
      j = flink[j];
    }
    flink[i] = j+1;
  }

  /* Step 2: Scan Algorithm */
  for (i = j = 0; i < subjectLen; i++, j++) {
    while((j >= 0) && (pattern[j] != subject[i])) {
      j = flink[j];
    }
    if (j == patternLen-1) {
      found = 1;
      goto exit;
    }
  }

 exit:
  if (dynamicFlink != NULL) free(dynamicFlink);
  if (!found)
    return(NULL);
  else
    return(&subject[i-patternLen+1]);
#undef MAX_FLINK
}


/******************************************************************************
 *
 * GetShortVendorName - return short vendor string
 *  
 * Description:
 *  some vendor strings are verbose. This function will return a shortest
 *  vendor name it can given an arbitrary vendor string. The returned string
 *  is allocated with malloc, it should be freed when no longer in use. The
 *  input string is not freed or modified by this function.
 *  
 * Returns:
 *  pointer to allocated string
 *  
 * Side Effects:
 *  string allocation, string should be freed when no longer needed.
 *  
 * Errors:
 *  no errors, if function fails the string "unknown" is returned
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Mon Aug  8 16:30:53 1994
 *    Initial Release
 *
 *****************************************************************************/
static char *
GetShortVendorName(char *input)
{
  int i;
  char *s1 = NULL;              /* s1 is temp work string */
  char *s2 = NULL;              /* s2 is string to return */

  /* duplicate input string so that we can modify it */
  s1 = strdup(input);
  /* upcase string */
  for (i = 0; s1[i]; i++)
    if (islower(s1[i])) s1[i] = toupper(s1[i]);

  /* return a short name if possible, some vendor names are verbose */
  if (StringSearch(s1, "DEC") ||
      StringSearch(s1, "DECWINDOWS") ||
      StringSearch(s1, "Digital Equipment Corporation") ||
      StringSearch(s1, "DigitalEquipmentCorporation"))
    s2 = strdup("DEC");
  else if (StringSearch(s1, "SGI") ||
	   StringSearch(s1, "SILCON GRAPHICS"))
    s2 = strdup("SGI");
  else if (StringSearch(s1, "IBM") ||
	   StringSearch(s1, "INTERNATIONAL BUSINESS MACHINES"))
    s2 = strdup("IBM");
  else
    s2 = strdup(s1);

  free(s1);
  return(s2);
}


/******************************************************************************
 *
 * GetHostMemorySize - Return kilobytes of memory installed on host platform
 *
 * Description:
 *  Returns the number of kilobytes of memory installed on host platform
 *
 * Returns:
 *  kilobytes of host platform memory
 *
 * Side Effects:
 *  None
 *
 * Errors:
 *  return 0 if unable to determine memory configuration
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Thu Aug  4 15:55:19 1994
 *    Initial Release
 *  Revision 1: Author: Rob Putney     Date: Mon Feb 27 13:40:00 1995
 *    Change to AIX specific function to get memory size.
 *
 *****************************************************************************/
static int
GetHostMemorySize(void)
{
#define MAXCMD (1024)
  FILE *inputPipe;
  int status;
  char sizerBuf[MAXCMD];
  int mem = 0;

/* This returns number of MB
  char cmd[] = "lsattr -F 'value' -l sys0 -a realmem |/usr/bin/awk '{printf(\"%d MB\", $1/1024)}' ";
*/

  char cmd[] = "lsattr -F 'value' -l sys0 -a realmem |/usr/bin/awk '{printf(\"%d\", $1)}' ";
  inputPipe= popen(cmd, "r");
  if (inputPipe == NULL) {
    fprintf(stderr, "could not open pipe for %s\n", cmd);
  }
  fgets(sizerBuf, MAXCMD, inputPipe);
  status = pclose(inputPipe);
  if (status != 0) {
    fprintf(stderr, "cmd \"%s\" returned error %d\n", cmd, status);
  }

/*
printf( "Memory size: %s\n", sizerBuf );
*/

mem = atoi(sizerBuf);
/* printf( "Memory size: %d KB\n", mem ); */
return(mem);

}


/******************************************************************************
 *
 * GetHostVendor - return string naming the system vendor
 *
 * Description:
 *  return the name of the system vendor as a string. This is to identify
 *  manufacturer of the system. The string is allocated with malloc, it should
 *  be freed when no longer in use.
 *
 * Returns:
 *  pointer to allocated string
 *
 * Side Effects:
 *  string allocation, string should be freed when no longer needed.
 *
 * Errors:
 *  no errors, if function fails the string "unknown" is returned
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Mon Aug  8 16:30:53 1994
 *    Initial Release
 *  Revision 1: Author: Rob Putney     Date: Mon Feb 27 14:12:00 1995
 *    Change to be AIX Specific.
 *
 *****************************************************************************/
static char *
GetHostVendor(void)
{
  return(strdup("IBM"));
}



/******************************************************************************
 *
 * GetHostModel - return string naming the host model
 *
 * Description:
 * return string identifying the host platform's model designation. The string
 * is allocated with malloc, it should be freed when no longer in use.
 *
 * Returns:
 *  pointer to allocated string
 *
 * Side Effects:
 *  string allocation, string should be freed when no longer needed.
 *
 * Errors:
 *  no errors, if function fails the string "unknown" is returned
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Mon Aug  8 16:30:53 1994
 *    Initial Release
 *  Revision 1: Author: Rob Putney     Date: Mon Feb 27 14:13:00 1995
 *    Change to be AIX Specific.
 *
 *****************************************************************************/
static char *
GetHostModel(void)
{
/*
 * There is no system entry point in AIX that returns the name of the processor. The
 * structure utsname contains the cpuid (chararter 8,9) which can then be correlated
 * to a given host cpu model via a lookup table.  This table will need to be updated
 * at each release of new processor models.  I don't know of a better way than this
 * right now.
 */

static char Host[100];
struct utsname un;
char cmd[20];
char id[20];
int  cpuid;

FILE *inputPipe;

int status;

  uname(&un);   /* Get the structure which contains the cpu id number */

  sprintf( cmd, "echo 0X%2.2s\n", un.machine+8 );  /* fields 8&9 are 2 digit id */
  sprintf( id, "0X%2.2s" , un.machine+8 );
  inputPipe=popen(cmd, "r");
  if (inputPipe == NULL)  {
        strcpy(Host, "unknown (id=-1)");      /* If failed to get cpu id, print -1 */
        return Host;
  }
  fscanf(inputPipe, "%X",&cpuid);             /* Read the CPU id as Hex. */
  status = pclose(inputPipe);



  /* Map "cpuid" from "uname" to real product names               */
  /* NOTE: As new CPU Models are added, need to update this list. */

  switch ( cpuid )
  {
        case 0x4D:      strcpy( Host, "RISC System/6000 Model 40P"); break;
        case 0x43:      strcpy( Host, "RISC System/6000 Model M20"); break;
        case 0x45:      strcpy( Host, "RISC System/6000 Model 220"); break;
        case 0x41:      strcpy( Host, "RISC System/6000 Model 220"); break;
        case 0x47:      strcpy( Host, "RISC System/6000 Model 230"); break;
        case 0x46:      strcpy( Host, "RISC System/6000 Model 25T"); break;
        case 0x49:      strcpy( Host, "RISC System/6000 Model 25T80"); break;
        case 0x31:      strcpy( Host, "RISC System/6000 Model 320"); break;
        case 0x35:      strcpy( Host, "RISC System/6000 Model 32H"); break;
        case 0x37:      strcpy( Host, "RISC System/6000 Model 340"); break;
        case 0x38:      strcpy( Host, "RISC System/6000 Model 350"); break;
        case 0x77:      strcpy( Host, "RISC System/6000 Model 355"); break;
        case 0x76:      strcpy( Host, "RISC System/6000 Model 36T/365"); break;
        case 0x75:      strcpy( Host, "RISC System/6000 Model 37T/375"); break;
        case 0x58:      strcpy( Host, "RISC System/6000 Model 3AT"); break;
        case 0x57:      strcpy( Host, "RISC System/6000 Model 3BT"); break;
        case 0x59:      strcpy( Host, "RISC System/6000 Model 3CT"); break;
        case 0x42:      strcpy( Host, "RISC System/6000 Model 41T"); break;
        case 0x91:      strcpy( Host, "RISC System/6000 Model 42T"); break;
        case 0x30:      strcpy( Host, "RISC System/6000 Model 520"); break;
        case 0x34:      strcpy( Host, "RISC System/6000 Model 52H"); break;
        case 0x10:      strcpy( Host, "RISC System/6000 Model 530/730"); break;
        case 0x18:      strcpy( Host, "RISC System/6000 Model 53H"); break;
        case 0x14:      strcpy( Host, "RISC System/6000 Model 540"); break;
        case 0x1C:      strcpy( Host, "RISC System/6000 Model 550"); break;
        case 0x5C:      strcpy( Host, "RISC System/6000 Model 560"); break;
        case 0x67:      strcpy( Host, "RISC System/6000 Model 570"); break;
        case 0x66:      strcpy( Host, "RISC System/6000 Model 580"); break;
        case 0x70:      strcpy( Host, "RISC System/6000 Model 590"); break;
        case 0x20:      strcpy( Host, "RISC System/6000 Model 930"); break;
        case 0x2E:      strcpy( Host, "RISC System/6000 Model 950"); break;
        case 0x63:      strcpy( Host, "RISC System/6000 Model 970B"); break;
        case 0x64:      strcpy( Host, "RISC System/6000 Model 980"); break;
        default:        sprintf( Host, "unknown (CPU_id=%s)",id); break;
  }

  return(strdup(Host));  /* Return the string we have created to the Host Model */
}


/******************************************************************************
 *
 * GetHostCPU - return string naming the host CPU
 * 
 * Description:
 * return string identifying the host platform's CPU. The string
 * is allocated with malloc, it should be freed when no longer in use.
 * 
 * Returns:
 *  pointer to allocated string
 * 
 * Side Effects:
 *  string allocation, string should be freed when no longer needed.
 * 
 * Errors:
 *  no errors, if function fails the string "unknown" is returned
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Mon Aug  8 16:30:53 1994
 *    Initial Release
 *  Revision 1: Author: Rob Putney     Date: Mon Feb 27 18:36:00 1995
 *    Change to be AIX Specific.  Will use the info in /usr/include/sys/systemcfg.h
 *
 *****************************************************************************/

static char *
GetHostCPU(void)
{

  static char hostcpu[50];
  /* Values for   implementation field */
  #define POWER_RS1       0x0001          /* RS1 class CPU */
  #define POWER_RSC       0x0002          /* RSC class CPU */
  #define POWER_RS2       0x0004          /* RS2 class CPU */
  #define POWER_601       0x0008          /* 601 class CPU */
  #define POWER_603       0x0020          /* 603 class CPU */
  #define POWER_604       0x0010          /* 604 class CPU */
  #define POWER_620       0x0040          /* 620 class CPU */

  switch ( _system_configuration.implementation ) {
        case POWER_RS1 :        strcpy( hostcpu, "POWER"); break;
        case POWER_RSC :        strcpy( hostcpu, "POWER RSC"); break;
        case POWER_RS2 :        strcpy( hostcpu, "POWER2"); break;
        case POWER_601 :        strcpy( hostcpu, "PowerPC 601"); break;
        case POWER_603 :        strcpy( hostcpu, "PowerPC 603"); break;
        case POWER_604 :        strcpy( hostcpu, "PowerPC 604"); break;
        case POWER_620 :        strcpy( hostcpu, "PowerPC 620"); break;
        default :               sprintf( hostcpu, "unknown (CPU_Class=%s)",
                                 _system_configuration.implementation); break;
  }
  return(strdup(hostcpu));
}


/******************************************************************************
 *
 * GetHostCPUCount - return string indicating number of host CPU's 
 *  
 * Description:
 * return string indicating number of host CPU's. The string "unknown" is
 * returned if the count is not determinable. The string is allocated with
 * malloc, it should be freed when no longer in use.
 *  
 * Returns:
 *  pointer to allocated string
 *  
 * Side Effects:
 *  string allocation, string should be freed when no longer needed.
 *  
 * Errors:
 *  no errors, if function fails the string "unknown" is returned
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Mon Feb 27 14:18:41 EST 1995
 *    Initial Release
 *  Revision 1: Author: Rob Putney     Date: Wed Mar 01 15:16:47 EST 1995
 *    Initial Release
 *
 *****************************************************************************/
static char *
GetHostCPUCount(void)
{
  static char NumberCPUs[5];     /* character string to return  */

  /* _system_configuration.ncpus = 1 = UniProcessor, n = n-way MP */

  sprintf( NumberCPUs, "%d",_system_configuration.ncpus);
  return(strdup(NumberCPUs));
}


/******************************************************************************
 *
 * GetHostPrimaryCacheSize - return string indicating host's primary cache (KB)
 *  
 * Description:
 * return string indicating the number of kilobytes of primary cache on the
 * host's CPU, or "unknown" if not determinable. The string
 * is allocated with malloc, it should be freed when no longer in use.
 *  
 * Returns:
 *  pointer to allocated string
 *  
 * Side Effects:
 *  string allocation, string should be freed when no longer needed.
 *  
 * Errors:
 *  no errors, if function fails the string "unknown" is returned
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Mon Feb 27 14:18:41 EST 1995
 *    Initial Release
 *  Revision 1: Author: Rob Putney     Date: Wed Mar 01 13:16:12 EST 1995
 *    Change to AIX specific code.  Make use of the structure in 
 *    /usr/include/sys/systemcfg.h.  There is more information in the
 *    AIX version 4.1 than in 3.2.
 *
 *****************************************************************************/
static char *
GetHostPrimaryCacheSize(void)
{
  static char L1CacheSize[50];
  int dcache_size;
  int icache_size;

  dcache_size = (_system_configuration.dcache_size / 1024);  /* conv dcache size bytes to KB */
  icache_size = (_system_configuration.icache_size / 1024);  /* conv icache size bytes to KB */

  switch ( _system_configuration.cache_attrib ) {
        case 0 :    strcpy( L1CacheSize, "No Cache Present"); break;
        case 1 :    sprintf( L1CacheSize, "%d/%d (D/I)",dcache_size,icache_size); 
                    break; 	/* seperate data & instr caches */
        case 3 :    sprintf( L1CacheSize, "%d (D+I)",dcache_size); 
		    break; 	/* combined data & instr cache  */
        default :   sprintf( L1CacheSize, "unknown (cache_attrib=%s)",
                                 _system_configuration.cache_attrib); break;
  }
  return(strdup(L1CacheSize));

}


/******************************************************************************
 *
 * GetHostSecondaryCacheSize - return string indicating host's secondary cache (KB)
 *  
 * Description:
 * return string indicating the number of kilobytes of secondary cache on the
 * host's CPU, or "unknown" if not determinable. The string
 * is allocated with malloc, it should be freed when no longer in use.
 *  
 * Returns:
 *  pointer to allocated string
 *  
 * Side Effects:
 *  string allocation, string should be freed when no longer needed.
 *  
 * Errors:
 *  no errors, if function fails the string "unknown" is returned
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Mon Feb 27 14:18:41 EST 1995
 *    Initial Release
 *  Revision 1: Author: Rob Putney     Date: Wed Mar 01 14:01:12 EST 1995
 *    Change to AIX specific code.  Make use of the structure in 
 *    /usr/include/sys/systemcfg.h.
 *
 *****************************************************************************/
static char *
GetHostSecondaryCacheSize(void)
{
  static char L2CacheSize[50];   /* character string to return i.e. 512KB */
  int L2_cache_size;             /* integer value of cache size           */

  L2_cache_size = (_system_configuration.L2_cache_size / 1024);  /* conv L2 cache size bytes to KB */

  sprintf( L2CacheSize, "%d",L2_cache_size);
  return(strdup(L2CacheSize));
}


/******************************************************************************
 *
 * GetWindowSystem - return string naming the windowing system
 *  
 * Description:
 * return string identifying the windowing system in use on the target
 * device. The string is allocated with malloc, it should be freed when no
 * longer in use.
 *  
 * Returns:
 *  pointer to allocated string
 *  
 * Side Effects:
 *  string allocation, string should be freed when no longer needed.
 *  
 * Errors:
 *  no errors, if function fails the string "unknown" is returned
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Mon Aug  8 16:30:53 1994
 *    Initial Release
 *
 *****************************************************************************/
static char *
GetWindowSystem(void)
{
  char buf[128];
  
#if defined(XWINDOWS)
  sprintf(buf, "X Window System V%d", X_PROTOCOL);
  return(strdup(buf));
#elif defined(WIN32)
  return(strdup("Win32"));
#else
  return(strdup("unknown"));
#endif
}


/******************************************************************************
 *
 * GetDriverVersion - return string identifying the graphics driver
 *  
 * Description:
 * Return string identifying the graphics driver version. If there is no
 * graphics driver than the string "NA" is returned. If a graphics driver
 * exists, but the function cannot identify it then the string "unknown" is
 * returned.  The string is allocated with malloc, it should be freed when no
 * longer in use.
 *  
 * Returns:
 *  pointer to allocated string
 *  
 * Side Effects:
 *  string allocation, string should be freed when no longer needed.
 *  
 * Errors:
 *  no errors, if function fails the string "unknown" is returned
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Mon Feb 27 15:00:18 EST 1995
 *    Initial Release
 *  Revision 1: Author: Rob Putney     Date: Wed Mar 01 13:16:32 EST 1995
 *    Change to AIX specific.
 *
 *****************************************************************************/
static char *
GetDriverVersion(void)
{
  return(strdup("NA")); /* AIX does not have 'graphics drivers'           */
                        /* The closest thing is system side libraries and */
                        /* microcode, but this is given by the operating  */
                        /* system and PTFs which are present.             */
}

/******************************************************************************
 *
 * GetHostOperatingSystem - return string naming the host CPU operating system
 *  
 * Description:
 *  return the type of the host operating system as a string. This is to
 *  identify what type of operating system this program is running under. The
 *  string is allocated with malloc, it should be freed when no longer in use.
 *  
 * Returns:
 *  pointer to allocated string
 *  
 * Side Effects:
 *  string allocation, string should be freed when no longer needed.
 *  
 * Errors:
 *  no errors, if function fails the string "unknown" is returned
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Mon Aug  8 16:30:53 1994
 *    Initial Release
 *  Revision 1: Author: Rob Putney     Date: Tue Feb 28 17:18:00 1995
 *    Make AIX specific.
 *
 *****************************************************************************/
static char *
GetHostOperatingSystem(void)
{
  struct utsname uName;
  int status;

  status = uname(&uName);
  if (status < 0) {
    return(strdup("unknown"));
  }
  else {
    return(strdup(uName.sysname));
  }
}


/******************************************************************************
 *
 * GetHostOperatingSystemRelease - return string naming operating system release
 *  
 * Description:
 *  return the release/version of the host operating system as a string. This
 *  is to identify what version of the operating system this program is
 *  running under. The string is allocated with malloc, it should be freed
 *  when no longer in use.
 *  
 * Returns:
 *  pointer to allocated string
 *  
 * Side Effects:
 *  string allocation, string should be freed when no longer needed.
 *  
 * Errors:
 *  no errors, if function fails the string "unknown" is returned
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Mon Aug  8 16:30:53 1994
 *    Initial Release
 *  Revision 1: Author: Rob Putney     Date: Tue Feb 28 17:50:00 1995
 *    Make AIX specific.
 *
 *****************************************************************************/
static char *
GetHostOperatingSystemRelease(void)
{
  struct utsname uName;
  int status;
  int releaseLen, versionLen;
  char *s;

  status = uname(&uName);
  if (status < 0) {
    return(strdup("unknown"));
  }
  else {
    releaseLen = strlen(uName.release);
    versionLen = strlen(uName.version);
    /* 1 extra char for dot, 1 extra char for null terminator */
    s = malloc(releaseLen + versionLen + 2);
    *s = 0;
    strcpy(s, uName.version);   /* Should print version release i.e. 3.2  or 4.1 */
    strcat(s, ".");
    strcat(s, uName.release);
    return(s);
  }
}


/******************************************************************************
 *
 * GetHostName - return string naming the host
 *  
 * Description:
 *  return the name of this host as a string. This is to identify which
 *  platform this program is running on. The string is allocated with malloc,
 *  it should be freed when no longer in use.
 *  
 * Returns:
 *  pointer to allocated string
 *  
 * Side Effects:
 *  string allocation, string should be freed when no longer needed.
 *  
 * Errors:
 *  no errors, if function fails the string "unknown" is returned
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Mon Aug  8 16:30:53 1994
 *    Initial Release
 *  Revision 1: Author: Rob Putney     Date: Tue Feb 28 17:19:00 1995
 *    Make AIX specific.
 *
 *****************************************************************************/
static char *
GetHostName(void)
{
  struct utsname uName;
  int status;

  status = uname(&uName);
  if (status < 0) {
    return(strdup("unknown"));
  }
  else {
    return(strdup(uName.nodename));
  }
}


/******************************************************************************
 *
 * GetDateTime - get the current month, day, year, hour, minute
 *  
 * Description:
 *  Get the current  day, month, year, hour, minute. Each is a pointer to an
 *  integer. If the pointer is NULL then no value is returned for that
 *  parameter, otherwise an assignment is made to the integer pointed to by
 *  the parameter.
 *
 *  month:  in the range [1-12] 1=January, 12=December
 *  day:    in the range [1-31]
 *  year:   as a 4 digit number, e.g. 1994
 *  hour:   in the range [0-23]
 *  min:    in the range [0-59]
 *  
 * Returns:
 *  0 for success, error code otherwise
 *  
 * Side Effects:
 *  None
 *  
 * Errors:
 *  return non-zero on failure
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Wed Aug 17 13:08:36 1994
 *    Initial Release
 *  Revision 1: Author: Rob Putney     Date: Wed Mar 01 14:46:46 1995
 *    Made AIX specific change; sys_errlist[errno] not defined in AIX
 *    so just use errno.
 *
 *****************************************************************************/

int
GetDateTime(int *month, int *day, int *year, int *hour, int *minute)
{
  time_t timeT;
  struct tm *timeTm;

  time(&timeT);
  timeTm = localtime(&timeT);
  if (timeTm == NULL) {
    fprintf(stderr, "Error calling localtime: %d\n", errno);
    return(errno);
  }
  if (month)  *month  = timeTm->tm_mon + 1;
  if (day)    *day    = timeTm->tm_mday;
  if (year)   *year   = timeTm->tm_year + 1900;
  if (hour)   *hour   = timeTm->tm_hour;
  if (minute) *minute = timeTm->tm_min;
  return(0);
}


/******************************************************************************
 *
 * GetOpenGLClientVendor - return string naming the client library vendor
 *  
 * Description:
 *  return the name of the vendor supplying the OpenGL client library.  The
 *  string is allocated with malloc,it should be freed when no longer in use.
 *  
 * Returns:
 *  pointer to allocated string
 *  
 * Side Effects:
 *  string allocation, string should be freed when no longer needed.
 *  
 * Errors:
 *  no errors, if function fails the string "unknown" is returned
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Mon Aug  8 16:30:53 1994
 *    Initial Release
 *  Revision 1: Author: Rob Putney     Date: Wed Mar  1 13:01:03 1995
 *    Make code AIX specific. When GLX 1.1 is available, uncomment the
 *    #define GLX_VERSION_1_1 at the top of this file.
 *
 *****************************************************************************/
static char *
GetOpenGLClientVendor(void)
{
#if defined(XWINDOWS)
    Display *dpy;

    dpy = auxXDisplay();
#if defined(GLX_VERSION_1_0)
    return(strdup("IBM"));
#endif
#if defined(GLX_VERSION_1_1)
    return(GetShortVendorName((char *)glXGetClientString(dpy, GLX_VENDOR)));
#endif
#else
    return(strdup("IBM"));
#endif
}


/******************************************************************************
 *
 * GetOpenGLClientVersion - return string naming the client library version
 *  
 * Description:
 *  return the version of the OpenGL client library.  The string is allocated
 *  with malloc,it should be freed when no longer in use.
 *  
 * Returns:
 *  pointer to allocated string
 *  
 * Side Effects:
 *  string allocation, string should be freed when no longer needed.
 *  
 * Errors:
 *  no errors, if function fails the string "unknown" is returned
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Mon Aug  8 16:30:53 1994
 *    Initial Release
 *  Revision 1: Author: Rob Putney     Date: Wed Mar  1 13:01:03 1995
 *    Make code AIX specific. When GLX 1.1 is available, uncomment the
 *    #define GLX_VERSION_1_1 at the top of this file.
 *
 *****************************************************************************/
static char *
GetOpenGLClientVersion(void)
{
#if defined(XWINDOWS)
    Display *dpy;

    dpy = auxXDisplay();
#if defined(GLX_VERSION_1_1)
    return((char *)glXGetClientString(dpy, GLX_VERSION));
#else
    return(strdup("1.0"));
#endif
#else
    return(strdup("unknown"));
#endif
}


/******************************************************************************
 *
 * GetOpenGLClientExtensions - return string naming the client extensions
 *  
 * Description:
 *  return the extensions supported in the OpenGL client library.  The string
 *  is allocated with malloc,it should be freed when no longer in use.
 *  
 * Returns:
 *  pointer to allocated string
 *  
 * Side Effects:
 *  string allocation, string should be freed when no longer needed.
 *  
 * Errors:
 *  no errors, if function fails the string "unknown" is returned
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Mon Aug  8 16:30:53 1994
 *    Initial Release
 *  Revision 1: Author: Rob Putney     Date: Wed Mar  1 17:38:03 1995
 *    Make code AIX specific. When GLX 1.1 is available, uncomment the
 *    #define GLX_VERSION_1_1 at the top of this file.
 *
 *****************************************************************************/
static char *
GetOpenGLClientExtensions(void)
{
#if defined(XWINDOWS)
    Display *dpy;

    dpy = auxXDisplay();
#if defined(GLX_VERSION_1_1)
    return((char *)glXGetClientString(dpy, GLX_EXTENSIONS));
#else
    return(strdup("None"));   /* No GLX Extensions for Version 1.0 */
#endif
#else
    return(strdup("unknown"));
#endif
}



/******************************************************************************
 *
 * GetOpenGLBufferInfo - inquire the OpenGL buffer configuration
 *  
 * Description:
 *  Given a specfic rendering target and visual description, return the OpenGL
 *  buffer configuration. This information includes:
 *  
 * Returns:
 *  0 for success, error code otherwise
 *  
 * Side Effects:
 *  None
 *  
 * Errors:
 *  None
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Thu Aug 18 10:18:45 1994
 *    Initial Release
 *
 *****************************************************************************/

#if defined(XWINDOWS)
static int
GetOpenGLBufferConfig(Display *dpy, XVisualInfo *visInfo, EnvironmentInfo *info)
{
  BufferConfig *config = &info->bufConfig;

  glXGetConfig(dpy, visInfo, GLX_DOUBLEBUFFER, &config->doubleBuffer);
  glXGetConfig(dpy, visInfo, GLX_STEREO, &config->stereo);
  glXGetConfig(dpy, visInfo, GLX_RGBA, &config->rgba);

  if (config->rgba) {
    glXGetConfig(dpy, visInfo, GLX_RED_SIZE, &config->redSize);
    glXGetConfig(dpy, visInfo, GLX_GREEN_SIZE, &config->greenSize);
    glXGetConfig(dpy, visInfo, GLX_BLUE_SIZE, &config->blueSize);
    glXGetConfig(dpy, visInfo, GLX_ALPHA_SIZE, &config->alphaSize);

    glXGetConfig(dpy, visInfo, GLX_ACCUM_RED_SIZE, &config->accumRedSize);
    glXGetConfig(dpy, visInfo, GLX_ACCUM_GREEN_SIZE, &config->accumGreenSize);
    glXGetConfig(dpy, visInfo, GLX_ACCUM_BLUE_SIZE, &config->accumBlueSize);
    glXGetConfig(dpy, visInfo, GLX_ACCUM_ALPHA_SIZE, &config->accumAlphaSize);

    config->indexSize = -1;
  }
  else {
    config->redSize = -1;
    config->greenSize = -1;
    config->blueSize = -1;
    config->alphaSize = -1;

    config->accumRedSize = -1;
    config->accumGreenSize = -1;
    config->accumBlueSize = -1;
    config->accumAlphaSize = -1;

    glXGetConfig(dpy, visInfo, GLX_BUFFER_SIZE, &config->indexSize);
  }

  glXGetConfig(dpy, visInfo, GLX_DEPTH_SIZE, &config->depthSize);
  glXGetConfig(dpy, visInfo, GLX_STENCIL_SIZE, &config->stencilSize);
  glXGetConfig(dpy, visInfo, GLX_AUX_BUFFERS, &config->auxBuffers);
  glXGetConfig(dpy, visInfo, GLX_LEVEL, &config->level);

#if defined(XWINDOWS)
  config->visualId = visInfo->visualid;
#if defined(__cplusplus) || defined(c_plusplus)
  config->visualClass = visInfo->c_class;
#else
  config->visualClass = visInfo->class;
#endif
#endif

}
#elif defined(SOME_OTHER_WINDOW_SYSTEM)
#else
#error "unknown window system"
#endif


/******************************************************************************
 *
 * GetXEnvironInfo - Get all enironment info specfic to X Windows
 *  
 * Description:
 *  
 *  
 * Returns:
 *  0 success, error code otherwise
 *  
 * Side Effects:
 *  None
 *  
 * Errors:
 *  errors with bad configurations
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Thu Aug 18 16:51:10 1994
 *    Initial Release
 *  Revision 1: Author: Rob Putney     Date: Wed Mar 01 15:13:16 1995
 *    Change to AIX specific; problem with dpy structre, 'pSmt' not
 *    part of structure.
 *
 *****************************************************************************/

#if defined(XWINDOWS)
static int
GetXEnvironInfo(EnvironmentInfo *info)
{
  Display *dpy;
  Window win;
  int i, status;
  int screen;
  Screen *pScreen;
  XWindowAttributes winAttr;
  XVisualInfo visualTemplate, *visInfo;
  VisualID visualId;
  int nVisuals;
  char buf[256];
  int glxMajorVersion, glxMinorVersion;
  char *s;
  
  dpy = auxXDisplay();
  if (!dpy) {
    fprintf(stderr, "GetXEnvironInfo: no display set\n");
    return(1);
  }

  win = auxXWindow();
  if (!win) {
    fprintf(stderr, "GetXEnvironInfo: no window set\n");
    return(1);
  }

  status = XGetWindowAttributes(dpy, win, &winAttr);
  if (!status) {
    fprintf(stderr, "Error calling XGetWindowAttributes\n");
  }

  /* XXX - should we be using aux functions to get screen number, visual id? */

  /* Find the screen number given a screen ptr. */
  pScreen = winAttr.screen;
  for (screen = 0; screen < ScreenCount(dpy); screen++) {
    if (pScreen == &dpy->screens[screen]) {
      break;
    }
  }
  if (screen >= ScreenCount(dpy)) {
    fprintf(stderr, "Error, cannot find screen\n");
  }

  /* Find the visualInfo given a visualId */
  visualId = winAttr.visual->visualid;
  visualTemplate.visualid = visualId;
  visualTemplate.screen = screen;
  visInfo = XGetVisualInfo(dpy,VisualScreenMask|VisualIDMask,
                           &visualTemplate, &nVisuals);
  if (visInfo == NULL || nVisuals != 1) {
    fprintf(stderr, "Error calling XGetVisualInfo\n");
  }
  else {
    GetOpenGLBufferConfig(dpy, visInfo, info);
  }

  info->windowWidth = winAttr.width;
  info->windowHeight = winAttr.height;
  
  info->screenNumber = screen;

  info->display = strdup(DisplayString(dpy));

  if (glXQueryVersion(dpy, &glxMajorVersion, &glxMinorVersion)) {
    sprintf(buf, "%d.%d", glxMajorVersion, glxMinorVersion);
  }
  else {
    sprintf(buf, "GLX not available");
  }
  info->glxVersion = strdup(buf);

#if defined(GLX_VERSION_1_1)
  s = (char *)glXQueryServerString(dpy, screen, GLX_VENDOR);
  info->glServerVendor = s ? GetShortVendorName(s) : strdup("unknown");
#else
  info->glServerVendor = GetShortVendorName(ServerVendor(dpy));
#endif

#if defined(GLX_VERSION_1_1)
  s = (char *) glXQueryServerString(dpy, screen, GLX_VERSION);
  info->glServerVersion = strdup(s ? s : "unknown");
#else
  info->glServerVersion = strdup(info->glxVersion);
#endif

#if defined(GLX_VERSION_1_1)
  s = (char *) glXQueryServerString(dpy, screen, GLX_EXTENSIONS);
  info->glServerExtensions = strdup(s ? s : "unknown");
#else
  info->glServerExtensions = strdup("unknown");
#endif

#if defined(GLX_VERSION_1_1)
  s = (char *)glXQueryExtensionsString(dpy, screen);
  info->glxExtensions = strdup(s ? s : "unknown");
#else
  info->glxExtensions = strdup("unknown");
#endif

  info->screenWidth = DisplayWidth(dpy, screen);
  info->screenHeight = DisplayHeight(dpy, screen);

/*
  if (dpy->pSmt) {
    info->sharedMemConnection = TRUE;
*/
  if (dpy->shmTrans) {
    info->sharedMemConnection = TRUE;

  }
  else {
    info->sharedMemConnection = FALSE;
  }

  XFree((void *)visInfo);
  return(0);
}
#elif defined(SOME_OTHER_WINDOW_SYSTEM)
#else
#error "unknown window system"
#endif



/******************************************************************************
 *
 * GetEnvironment - return info about environment test was run in
 *  
 * Description:
 *  This function fills in an environment info record with every pertinent
 *  piece of information about the condiditons under which the test was run.
 *  
 * Returns:
 *  0 success, error code otherwise
 *  
 * Side Effects:
 *  None
 *  
 * Errors:
 *  errors generally only with bad configurations, too many to list
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Thu Aug 18 16:52:35 1994
 *    Initial Release
 *
 *****************************************************************************/

int
GetEnvironment(EnvironmentInfo *info)
{
  GLenum windType;

  FreeEnvironmentData(info);

  GetDateTime(&info->month, &info->day, &info->year, NULL, NULL);
  info->host = GetHostName();
  info->hostOperatingSystem = GetHostOperatingSystem();
  info->hostOperatingSystemVersion = GetHostOperatingSystemRelease();
  info->hostVendor = GetHostVendor();
  info->hostModel = GetHostModel();
  info->hostCPU = GetHostCPU();
  info->hostCPUCount = GetHostCPUCount();
  info->hostPrimaryCacheSize = GetHostPrimaryCacheSize();
  info->hostSecondaryCacheSize = GetHostSecondaryCacheSize();
  info->windowSystem = GetWindowSystem();
  info->driverVersion = GetDriverVersion();
  info->hostMemorySize = GetHostMemorySize() / 1024; /* kilobytes to MB */
  info->glVendor = GetShortVendorName((char *)glGetString(GL_VENDOR));
  info->glVersion = strdup((char *)glGetString(GL_VERSION));
  info->glRenderer = strdup((char *)glGetString(GL_RENDERER));
  info->glExtensions = strdup((char *)glGetString(GL_EXTENSIONS));
  info->glClientVendor = GetOpenGLClientVendor();
  info->glClientVersion = GetOpenGLClientVersion();
  info->glClientExtensions = GetOpenGLClientExtensions();

#ifdef GLU_VERSION_1_1
  info->gluVersion = strdup((char *)gluGetString(GLU_VERSION));;
  info->gluExtensions = strdup((char *)gluGetString(GLU_EXTENSIONS));;
#else
  info->gluVersion = strdup("1.0");     /* Hard code this for now */
  info->gluExtensions = strdup("None"); /* No gluGetString in 1.0 */
#endif

#if defined(XWINDOWS)
  GetXEnvironInfo(info);
#endif

  /*
   * Direct rendering is a "request", not a guarantee. We need to check the
   * direct render bit for each test because the GL context is only set up
   * in the above conditional block. If we didn't check it for each test
   * then the value for direct rendering store in the test descriptor would
   * be the "request" value, not the actual value.
   */

  windType = auxGetDisplayMode();
  if (windType & AUX_DIRECT)
    info->directRender = True;
  else
    info->directRender = False;
}



/******************************************************************************
 *
 * FreeEnvironmentData - frees all dynamic data in EnvironmentInfo struct
 *  
 * Description:
 *  Free all the dynamically allocated data in an EnvironmentInfo struct. Each
 *  pointer in the struct is assigned the value of NULL after its data has
 *  been freed. The struct itself is not freed.
 *  
 * Returns:
 *  void
 *  
 * Side Effects:
 *  free dynamically allocated memory
 *  
 * Errors:
 *  None
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Tue Sep 13 17:31:19 1994
 *    Initial Release
 *
 *****************************************************************************/

void
FreeEnvironmentData(EnvironmentInfo *info)
{
  if (info->host != NULL) free(info->host);
  if (info->hostOperatingSystem != NULL) free(info->hostOperatingSystem);
  if (info->hostOperatingSystemVersion != NULL) free(info->hostOperatingSystemVersion);
  if (info->hostVendor != NULL) free(info->hostVendor);
  if (info->hostModel != NULL) free(info->hostModel);
  if (info->hostCPU != NULL) free(info->hostCPU);
  if (info->hostCPUCount != NULL) free(info->hostCPUCount);
  if (info->hostPrimaryCacheSize != NULL) free(info->hostPrimaryCacheSize);
  if (info->hostSecondaryCacheSize != NULL) free(info->hostSecondaryCacheSize);
  if (info->windowSystem != NULL) free(info->windowSystem);
  if (info->driverVersion != NULL) free(info->driverVersion);
  if (info->glVendor != NULL) free(info->glVendor);
  if (info->glVersion != NULL) free(info->glVersion);
  if (info->glRenderer != NULL) free(info->glRenderer);
  if (info->glExtensions != NULL) free(info->glExtensions);
  if (info->glClientVendor != NULL) free(info->glClientVendor);
  if (info->glClientVersion != NULL) free(info->glClientVersion);
  if (info->glClientExtensions != NULL) free(info->glClientExtensions);
  if (info->gluVersion != NULL) free(info->gluVersion);
  if (info->gluExtensions != NULL) free(info->gluExtensions);
#if defined(XWINDOWS)
  if (info->display != NULL) free(info->display);
  if (info->glServerVendor != NULL) free(info->glServerVendor);
  if (info->glServerVersion != NULL) free(info->glServerVersion);
  if (info->glServerExtensions != NULL) free(info->glServerExtensions);
  if (info->glxVersion != NULL) free(info->glxVersion);
  if (info->glxExtensions != NULL) free(info->glxExtensions);
#endif
  NullEnvironmentData(info);
}


/******************************************************************************
 *
 * NullEnvironmentData - sets all dynamic data ptrs in EnvironmentInfo to NULL
 *  
 * Description:
 *  Sets all the pointers to dynamically allocated data in an EnvironmentInfo
 *  struct to NULL. The data pointed to by the pointers are not freed, use the
 *  function FreeEnvironmentData for that purpose.
 *  
 * Returns:
 *  void
 *  
 * Side Effects:
 *  None
 *  
 * Errors:
 *  None
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Tue Sep 13 17:31:19 1994
 *    Initial Release
 *
 *****************************************************************************/

void
NullEnvironmentData(EnvironmentInfo *info)
{
    info->host = NULL;
    info->hostOperatingSystem = NULL;
    info->hostOperatingSystemVersion = NULL;
    info->hostVendor = NULL;
    info->hostModel = NULL;
    info->hostCPU = NULL;
    info->hostCPUCount = NULL;
    info->hostPrimaryCacheSize = NULL;
    info->hostSecondaryCacheSize = NULL;
    info->windowSystem = NULL;
    info->driverVersion = NULL;
    info->glVendor = NULL;
    info->glVersion = NULL;
    info->glRenderer = NULL;
    info->glExtensions = NULL;
    info->glClientVendor = NULL;
    info->glClientVersion = NULL;
    info->glClientExtensions = NULL;
    info->gluVersion = NULL;
    info->gluExtensions = NULL;
#if defined(XWINDOWS)
    info->display = NULL;
    info->glServerVendor = NULL;
    info->glServerVersion = NULL;
    info->glServerExtensions = NULL;
    info->glxVersion = NULL;
    info->glxExtensions = NULL;
#endif
}



/******************************************************************************
 *
 * PrintEnvironment - print contents of EnvironmentInfo struct
 *  
 * Description:
 *  Print the contents of EnvironmentInfo struct. The stream parameter Points
 *  to a FILE structure specifying an open stream to which output will be
 *  written. The title parameter is a pointer to a string which will be output
 *  before any of the EnvironmentInfo data is output, The title string may be
 *  NULL in which case no title string will be written. The leader parameter
 *  is a pointer to a string which will be output before each field in the
 *  EnvironmentInfo struct. The nameWidth parameter is an integer parameter
 *  specifying how pad the name of each field. This can be used to cause the
 *  values to line up in a column. A positive value left justifies, a negative
 *  value right justifies. The suffix parameter is a pointer to a string which
 *  will be output after all of the EnvironmentInfo data is output, The suffix
 *  string may be NULL in which case no suffix string will be written.
 *  
 * Returns:
 *  void
 *  
 * Side Effects:
 *  file stream output
 *  
 * Errors:
 *  None
 *
 * Revision History:
 *  Revision 0: Author: John R. Dennis Date: Wed Sep 14 09:02:57 1994
 *    Initial Release
 *
 *****************************************************************************/

void
PrintEnvironment(FILE *stream, EnvironmentInfo *info, char *title,
                 char *leader, int nameWidth, char *suffix)
{
  if (title) fprintf(stream, "%s", title);

  fprintf(stream, "%s%*s%d\n", leader, nameWidth,
         "Month", info->month);
  fprintf(stream, "%s%*s%d\n", leader, nameWidth,
         "Day", info->day);
  fprintf(stream, "%s%*s%d\n", leader, nameWidth,
         "Year", info->year);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "Host", info->host);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "Operating System", info->hostOperatingSystem);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "Operating System Version", info->hostOperatingSystemVersion);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "Host Vendor", info->hostVendor);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "Host Model", info->hostModel);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "Host CPU", info->hostCPU);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "Host CPU Count", info->hostCPUCount);
  fprintf(stream, "%s%*s%d\n", leader, nameWidth,
         "Host Memory Size (MB)", info->hostMemorySize);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "Host Primary Cache Size (KB)", info->hostPrimaryCacheSize);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "Host Secondary Cache Size (KB)", info->hostSecondaryCacheSize);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "Window System", info->windowSystem);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
          "Driver Version", info->driverVersion);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "OpenGL Vendor", info->glVendor);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "OpenGL Version", info->glVersion);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "OpenGL Extensions", info->glExtensions);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "OpenGL Renderer", info->glRenderer);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "OpenGL Client Vendor", info->glClientVendor);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "OpenGL Client Version", info->glClientVersion);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "OpenGL Client Extensions", info->glClientExtensions);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "GLU Version", info->gluVersion);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "GLU Extensions", info->gluExtensions);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "Direct Rendering", info->directRender ? "True" : "False");
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "Double Buffer", info->bufConfig.doubleBuffer ? "True" : "False");
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "Stereo", info->bufConfig.stereo ? "True" : "False");
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "RGBA", info->bufConfig.rgba ? "True" : "False");
  fprintf(stream, "%s%*s%d\n", leader, nameWidth,
         "Color Index Size", info->bufConfig.indexSize);
  fprintf(stream, "%s%*s%d\n", leader, nameWidth,
         "Red Size", info->bufConfig.redSize);
  fprintf(stream, "%s%*s%d\n", leader, nameWidth,
         "Green Size", info->bufConfig.greenSize);
  fprintf(stream, "%s%*s%d\n", leader, nameWidth,
         "Blue Size", info->bufConfig.blueSize);
  fprintf(stream, "%s%*s%d\n", leader, nameWidth,
         "Alpha Size", info->bufConfig.alphaSize);
  fprintf(stream, "%s%*s%d\n", leader, nameWidth,
         "Accum Red Size", info->bufConfig.accumRedSize);
  fprintf(stream, "%s%*s%d\n", leader, nameWidth,
         "Accum Green Size", info->bufConfig.accumGreenSize);
  fprintf(stream, "%s%*s%d\n", leader, nameWidth,
         "Accum Blue Size", info->bufConfig.accumBlueSize);
  fprintf(stream, "%s%*s%d\n", leader, nameWidth,
         "Accum Alpha Size", info->bufConfig.accumAlphaSize);
  fprintf(stream, "%s%*s%d\n", leader, nameWidth,
         "Depth Size", info->bufConfig.depthSize);
  fprintf(stream, "%s%*s%d\n", leader, nameWidth,
         "Stencil Size", info->bufConfig.stencilSize);
  fprintf(stream, "%s%*s%d\n", leader, nameWidth,
         "Auxiliary Buffer Count", info->bufConfig.auxBuffers);
  fprintf(stream, "%s%*s%d\n", leader, nameWidth,
         "Frame BufferLevel", info->bufConfig.level);
#if defined(XWINDOWS)
  fprintf(stream, "%s%*s%#X\n", leader, nameWidth,
         "Visual ID", info->bufConfig.visualId);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "Visual Class", 
         info->bufConfig.visualClass == StaticGray  ? "StaticGray"  :
         info->bufConfig.visualClass == GrayScale   ? "GrayScale"   :
         info->bufConfig.visualClass == StaticColor ? "StaticColor" :
         info->bufConfig.visualClass == PseudoColor ? "PseudoColor" :
         info->bufConfig.visualClass == TrueColor   ? "TrueColor"   :
         info->bufConfig.visualClass == DirectColor ? "DirectColor" :
         "unknown");
  fprintf(stream, "%s%*s%d\n", leader, nameWidth,
         "Window Width (pixels)", info->windowWidth);
  fprintf(stream, "%s%*s%d\n", leader, nameWidth,
         "Window Height (pixels)", info->windowHeight);
  fprintf(stream, "%s%*s%d\n", leader, nameWidth,
         "Screen Width (pixels)", info->screenWidth);
  fprintf(stream, "%s%*s%d\n", leader, nameWidth,
         "Screen Height (pixels)", info->screenHeight);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "Display", info->display);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "OpenGL Server Vendor", info->glServerVendor);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "OpenGL Server Version", info->glServerVersion);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "OpenGL Server Extensions", info->glServerExtensions);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "GLX Server Version", info->glxVersion);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "GLX Server Extensions", info->glxExtensions);
  fprintf(stream, "%s%*s%d\n", leader, nameWidth,
         "Screen Number", info->screenNumber);
  fprintf(stream, "%s%*s%s\n", leader, nameWidth,
         "Shared Memory Connection",
         info->sharedMemConnection ? "True" : "False");
#endif
  if (suffix) fprintf(stream, "%s", suffix);
}

