/* This is part of ODS2 written by Paul Nankervis,
 * email address:  Paulnank@au1.ibm.com
 *
 * ODS2 is distributed freely for all members of the
 * VMS community to use. However all derived works
 * must maintain comments in their source to acknowledge
 * the contributions of the original author and
 * subsequent contributors.   This is free software; no
 * warranty is offered,  and while we believe it to be useful,
 * you use it at your own risk.
 */

#if !defined( DEBUG ) && defined( DEBUG_SHOWCMD )
#define DEBUG DEBUG_SHOWCMD
#else
#ifndef DEBUG
#define DEBUG 0
#endif
#endif

#include "cmddef.h"

#ifdef USE_LIBEDIT
#include <histedit.h>
#else
extern FILE *histfile;
#endif

#include "access.h"
#include "cache.h"
#include "direct.h"
#include "f11def.h"
#include "phyio.h"
#ifdef USE_VHD
#include "phyvhd.h"
#endif
#include "phyvirt.h"
#include "version.h"

extern defqualsp_t copy_defopt;
extern qual_t copyquals[];

extern defqualsp_t dir_defopt;
extern qual_t dirquals[];

extern defqualsp_t search_defopt;
extern qual_t searchquals[];

static unsigned show_defq( defqualsp_t defs,
                           qualp_t qualset, const char *name );

static unsigned show_stats( void );

static vmscond_t show_history( int qualc, char **qualv );

void show_version( int full );

static char *get_cwd( void );


/******************************************************************* doshow() */

#define hist_max OPT_SHARED_20
static uint32_t histmax;
static
qual_t histquals[] = { {"max-entries",    hist_max, 0,  DV(&histmax),
                        "commands history qual_maxent"},
                       {NULL, 0, 0, NV, NULL }
};

static
qual_t showkwds[] = { {"cwd",             0, ~0u, NV,
                       "commands show copy"},
                      {"default",         1, ~0u, NV,
                       "commands show default"},
                      {"devices",         2, ~0u, NV,
                       "commands show devices"},
                      {"qualifier_style", 3, ~0u, NV,
                       "commands show qstyle" },
                      {"statistics",      4, ~0u, NV,
                       "commands show statistics"},
                      {"terminal",        5, ~0u, NV,
                       "commands show terminal"},
                      {"time",            6, ~0u, NV,
                       "commands show time"},
                      {"verify",          7, ~0u, NV,
                       "commands show verify" },
                      {"version",         8, ~0u, NV,
                       "commands show version"},
                      {"volumes",         9, ~0u, NV,
                       "commands show volumes"},
                      {"directory_qualifiers",
                                         10, ~0u, NV,
                       "commands show directory"},
                      {"copy_qualifiers",
                                         11, ~0u, NV,
                       "commands show copy"},
                      {"message",        12, ~0u, NV,
                       "commands show message"},
                      {"search_qualifiers",
                                         13, ~0u, NV,
                       "commands show search"},
                      {"history",        14, ~0u, NV,
                       "commands show history"},
                      {"help_file",      15, ~0u, NV,
                       "commands show helpfile"},

                      {NULL,              0, 0, NV, NULL }
};
param_t showpars[] = { {"item_name", REQ, KEYWD, PA(showkwds), "" },
                       {"selector", OPT|NOLIM, STRING, NOPA, NULL},

                       {NULL,        0,   0,     NOPA,         NULL }
};

/*********************************************************** doshow() */

DECL_CMD(show) {
    options_t parnum;
    vmscond_t sts;

    UNUSED(qualc);
    UNUSED(qualv);

   if( $FAILS(sts = checkquals( &parnum, 0, showkwds, -1, argv+1 )) ) {
        return sts;
    }

    switch( parnum ) {
    default:
        return SS$_BADPARAM;
    case 0: {
        char *cwd;

        cwd = get_cwd();
        if( cwd == NULL ) {
            return SS$_BADPARAM;
        }
        printmsg( HELP_SHOWCWD, MSG_TEXT, cwd );
        free( cwd );
        return SS$_NORMAL;
    }
    case 1: {
        unsigned short curlen;
        char curdir[NAM$C_MAXRSS + 1] = { "" };
        struct dsc_descriptor curdsc;
        struct FAB fab;
        struct NAM nam;

        memset( &curdsc, 0, sizeof( curdsc ) );
        curdsc.dsc_w_length = NAM$C_MAXRSS;
        curdsc.dsc_a_pointer = curdir;

        if( $FAILS(sts = sys_setddir( NULL, &curlen, &curdsc )) ) {
            return printmsg( sts, 0 );
        }

        fab = cc$rms_fab;
        nam = cc$rms_nam;

        nam.nam$b_ess = sizeof(curdir) -1;
        nam.nam$l_esa = curdir;
        fab.fab$l_nam = &nam;
        fab.fab$l_fna = curdir;
        fab.fab$b_fns = (uint8_t)curlen;

        if( $FAILS(sts = sys_parse(&fab)) ) {
            curdir[curlen] = '\0';
            sts = printmsg( ODS2_INVDEF, 0, curdir );
        } else {
            curdir[nam.nam$b_dev+nam.nam$b_dir] = '\0';
            printf( "%s\n", curdir );
        }
        fab.fab$b_fns =
            nam.nam$b_ess =
            fab.fab$b_dns = 0;
        nam.nam$b_nop = NAM$M_SYNCHK;
        nam.nam$l_rlf = NULL;
        (void) sys_parse( &fab );                    /* Release context */
        return sts;
    }
    case 2:
        phyio_show( SHOW_DEVICES );
        virt_show();
        return SS$_NORMAL;
    case 3:
        return printmsg( HELP_SHOWQSTYLE, MSG_TEXT, (qstyle_c == '/')? "/DCL": "-unix" );
    case 4:
        return show_stats();
    case 5: {
        int twid, th;
        termchar( &twid, &th );
        return printmsg( HELP_TERMSIZE, MSG_TEXT, twid, th );
    }
    case 6: {
        char timstr[24] = { "" };
        unsigned short timlen;
        struct dsc_descriptor timdsc;

        memset( &timdsc, 0, sizeof( timdsc ) );
        timdsc.dsc_w_length = 20;
        timdsc.dsc_a_pointer = timstr;
        if( $FAILS(sts = sys_asctim( &timlen, &timdsc, NULL, 0 )) ) {
            printmsg(ODS2_VMSTIME, 0 );
            return printmsg( sts, MSG_CONTINUE, ODS2_VMSTIME );
        }
        timstr[timlen] = '\0';
        printf("  %s\n",timstr);
        return SS$_NORMAL;
    }
    case 7:
        return printmsg( verify_cmd? HELP_SHOWVERON: HELP_SHOWVEROFF, MSG_TEXT );
    case 8:
        show_version( 1 );
        return SS$_NORMAL;
    case 9:
        show_volumes();
        return SS$_NORMAL;
    case 10:
        return show_defq( dir_defopt, dirquals,
                          "directory" );
    case 11:
        return show_defq( copy_defopt, copyquals, "copy" );
    case 12:
        return show_message( argc, argv );
    case 13:
        return show_defq( search_defopt, searchquals, "search" );
    case 14:
        return show_history( qualc, qualv );
    case 15:
        return showhelpfile();
    }
    return SS$_NORMAL;
}

/************************************************************ show_defq() */

static unsigned show_defq( defqualsp_t defs,
                           qualp_t qualset, const char *name ) {
    int i;

    printmsg( HELP_SHOWDEFQ, MSG_TEXT, name );

    if( defs == NULL || defs->qualc == 0 ) {
        printmsg( HELP_SHOWNODEFQ, MSG_TEXT );
        return SS$_NORMAL;
    }

    for( i = 0; i < defs->qualc; i++ ) {
        qualp_t qp;
        char *qk, *qv;
        size_t len;

        len = strlen( defs->qualv[i] ) +1;
        if( (qk = malloc( len )) == NULL )
            return SS$_INSFMEM;
        memcpy( qk, defs->qualv[i], len );
        if( (qv = strchr( qk, '=' )) == NULL )
            qv = strchr( qk, ':');
        if( qv != NULL )
            *qv++ = '\0';

        for( qp = qualset; qp->name != NULL; qp++ ) {
            if( keycomp( qk, qp->name ) ) {
                printf( "      %c%s", qstyle_c, qp->name );
                if( qp->qtype != NOVAL && qv != NULL && *qv )
                    printf( "=%s", qv );
                putchar( '\n' );
                break;
            }
        }
        free( qk );
    }

    return SS$_NORMAL;
}

/****************************************************************** show_stats() */

/* dostats: print some simple statistics */

static unsigned show_stats( void ) {
    printmsg( HELP_SHOWSTATS, MSG_TEXT );
    direct_show();
    cache_show();
    phyio_show(SHOW_STATS);

    return SS$_NORMAL;
}

/*********************************************************** show_history() */
static vmscond_t show_history( int qualc, char **qualv ) {
    vmscond_t sts;
    options_t options;
#ifdef USE_LIBEDIT
    extern History  *edithist;
    HistEvent ev;
#else
    extern uint32_t cmd_histsize;
    uint32_t excess = 0;
    char *buf = NULL;
    size_t bufsize = 80;
#endif
    int nent;

    if( $FAILS(sts = checkquals( &options, 0, histquals, qualc, qualv )) ) {
        return sts;
    }
    sts = SS$_NORMAL;

#ifdef USE_LIBEDIT
    history( edithist, &ev, H_GETSIZE );
    nent = ev.num;
    if( nent < 0 ) {
        fprintf( stderr, "GETSIZE error (%d) %s\n", ev.num, ev.str );
        return sts;
    }
    if( !(options & hist_max) || histmax > (uint32_t)nent ) {
        histmax = nent;
    }
    if( histmax == 0 )
        return printmsg( HELP_SHOWNOCMDHIST, 0 );
    if( history( edithist, &ev, H_FIRST ) < 0 ) { /* Most recent cmd */
        fprintf( stderr, "FIRST error (%d) %s\n", ev.num, ev.str );
        return sts;
    }
    for( nent = 1; (uint32_t)nent < histmax; nent++ ) { /* Find MAXth older cmd */
        if( history( edithist, &ev, H_NEXT ) < 0 ) {
            fprintf( stderr, "NEXT error (%d) %s\n", ev.num, ev.str );
            return sts;
        }
    }
    for( nent = 0; (uint32_t)nent < histmax; nent++ ) {
        printmsg( HELP_SHOWCMDHISTLN, MSG_TEXT, ev.num, ev.str );
        history( edithist, &ev, H_PREV );
    }
    return sts;
#else
    if( histfile == NULL ) {
        printmsg( HELP_SHOWNOCMDHIST, 0 );
        return sts;
    }
    fseek( histfile, 0, SEEK_SET );
    for( nent = 0; ; ) { /* Count history lines */
        int c;

        c = getc( histfile );
        if( c == EOF ) break;
        if( c == '\n' ) ++nent;
    }
    if( !(options & hist_max) || histmax > (uint32_t)nent ) {
        histmax = nent;
    }
    if( histmax == 0 )
        return printmsg( HELP_SHOWNOCMDHIST, 0 );
    if( (uint32_t)nent > cmd_histsize )  /* Hide excess file lines if size reduced */
        excess = nent - cmd_histsize;
    if( histmax > (uint32_t)nent - excess )
        histmax = (uint32_t)nent - excess; /* Reduce request to visible window */
    histmax = nent - histmax;
    fseek( histfile, 0, SEEK_SET );
    for( nent = 0; (uint32_t)nent < histmax; ) { /* Skip old lines */
        int c;

        c = getc( histfile );
        if( c == EOF ) break;
        if( c == '\n' ) ++nent;
    }
    for( ; ; nent++ ) { /* Get full lines for display */
        char *line;

        if( (line = fgetline( histfile, FALSE, &buf, &bufsize )) == NULL )
            break;
        printmsg( HELP_SHOWCMDHISTLN, MSG_TEXT, nent + 1 - excess, line );
    }
    if( buf != NULL ) free( buf );
    fseek( histfile, 0, SEEK_END );
    return sts;
#endif
}

/*********************************************************** show_version() */

void show_version( int full ) {
    char *detail, *version;
#ifdef DEBUG_BUILD
    extern int vld_present;
#endif

#ifdef USE_LIBEDIT
    printmsg( HELP_SHOWVERSRL, MSG_TEXT, MNAME(MODULE_NAME), MODULE_IDENT,
              __DATE__, __TIME__, LIBEDIT_MAJOR, LIBEDIT_MINOR );
#else
    printmsg( HELP_SHOWVERS, MSG_TEXT, MNAME(MODULE_NAME), MODULE_IDENT,
              __DATE__, __TIME__ );
#endif

    if( !full ) return;

    /* Determine what platform and compiler were targeted for this image */

#ifdef _MSC_VER
#define strcat( _dst, _src ) strcat_s( (_dst), MAXSTRL, (_src) )
#endif
#define MAXSTRL 512

    if( (detail = malloc( MAXSTRL * 2 )) == NULL ) {
        return;
    }
    detail[0] = '\0';
    version = detail + MAXSTRL;
    version[0] = '\0';

    /* N.B. The order of detection is intended to deal with ambiguities,
     * and does not inidcate any particular preference or judgement.
     *
     * The detection scheme is an amalgam of tests from various sources.
     * It has not been run in all these environments by the author,
     * (or perhaps by anyone), so errors may exist and should be reported.
     * Additions are also welcome.
     */

#if defined __ANDROID__
    strcat( detail, "Android" );
#elif defined __linux__
    strcat( detail, "Linux" );
#elif defined __APPLE__
  #include "TargetConditionals.h"
  #if TARGET_IPHONE_SIMULATOR
    strcat( detail, "iOS Simulator" );
  #elif TARGET_OS_IPHONE
    strcat( detail, "iOS device" );
  #elif TARGET_OS_MAC
    strcat( detail, "OS X" );
  #else
    strcat( detail, "Unknown Apple platform" );
  #endif
#elif defined __CYGWIN__ && !defined _WIN32
    strcat( detail, "Cygwin" );
#elif defined _WIN64
    strcat( detail, "Windows (64-bit)" );
#elif defined _WIN32
    strcat( detail, "Windows (32-bit)" );
#elif defined VMS || defined __VMS
  #if defined __ia64__ || defined __ia64 || defined _M_IA64
    strcat( detail, "VMS (Itanium)" );
  #elif defined __ALPHA || defined __ALPHA_AXP
    strcat( detail, "VMS (Alpha)" );
  #elif defined VAX || defined __VAX
    strcat( detail, "VMS (VAX)" );
  #else
    strcat( detail, "VMS" );
  #endif
  #if defined __VMS_VER
    snprintf( detail + strlen( detail ), MAXSTRL - strlen( detail ),
              "%c%u.%u-%u%c",
             (__VMS_VER % 100u),
             (__VMS_VER / 10000000u),
             (__VMS_VER / 100000u) % 100u,
             (__VMS_VER / 10000u) % 10u,
             (__VMS_VER / 100u) % 100u );
  #endif
#elif defined __osf__
    strcat( detail, "DEC OSF/1 (Tru64)" );
#elif defined ultrix || defined __ultrix || defined __ultrix__
    strcat( detail, "Ultrix" );
#elif defined UNICOS
    strcat( detail, "UNICOS" );
#elif defined _CRAY || defined __crayx1
    strcat( detail, "UNICOSD/mp" );
#elif defined _AIX
  #if defined __64BIT__
    strcat( detail, "AIX (64-bit)" );
  #else
    strcat( detail, "AIX" );
  #endif
#elif defined(__sun) && defined(__SVR4)
    strcat( detail, "Solaris" );
#elif defined __hpux
    strcat( detail, "HP-UX" );
#elif defined __NetBSD__
    strcat( detail, "NetBSD" );
#elif defined __OpenBSD__
    strcat( detail, "OpenBSD" );

    /* Generic */
#elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
  #include <sys/param.h>
  #if defined(BSD)
    strcat( detail, "BSD Unix" );
  #endif
#elif defined_POSIX_VERSION
    strcat( detail, "POSIX" );
  #if(   _POSIX_VERSION >= 200809L )
    strcat( detail, ".1-2008" );
  #elif( _POSIX_VERSION >= 200112L )
    strcat( detail, ".1-2001" );
  #elif( _POSIX_VERSION >= 199506L )
    strcat( detail, ".1-1995" );
  #elif( _POSIX_VERSION >= 199309L )
    strcat( detail, ".1-1993" );
  #elif( _POSIX_VERSION >= 199009L )
    strcat( detail,".1-1990" );
  #elif( _POSIX_VERSION >= 198808L )
    strcat( detail,".1-1988" );
  #endif
#else
    strcat( detail, "Unknown platform" );
#endif

    /* Hardware */

#if defined(__powerpc__) || defined(__ppc__) || defined(__PPC__)
  #if defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) || \
      defined(__64BIT__) || defined(_LP64) || defined(__LP64__)
    strcat( detail, " PowerPC (64-bit)" );
  #else
    strcat( detail, " PowerPC (32-bit)" );
  #endif
#elif defined(__ia64) || defined(__itanium__) || defined(_M_IA64)
    #if !defined VMS && !defined __VMS
      strcat( detail, " Itanium " );
    #endif
#elif defined(__sparc)
      strcat( detail, " SPARC" );
#elif defined(__x86_64__) || defined(_M_X64)
      strcat( detail, " x86-64" );
#elif defined(__i386) || defined(_M_IX86)
      strcat( detail, " x86-32" );
#elif defined __alpha || defined __alpha || defined _M_ALPHA
  #if !defined VMS
      strcat( detail, " Alpha" );
  #endif
#elif defined VAX || defined __VAX
      strcat( detail, " VAX" );
#elif defined __aarch64__
      strcat( detail, " ARM64" );
#elif defined __thumb__
      strcat( detail, " ARM (Thumb)" );
#elif defined __arm__
      strcat( detail, " ARM " )
#elif defined __hppa__ || defined __HPPA__ || defined _hppa
      strcat( detail, " HP/PA RISC" );
#elif defined __m68k__ || defined M68000 || defined __MC68K__
      strcat( detail, " Motorola 68000" );
#elif defined __mips__ || defined __mips
      strcat( detail, " MIPS" );
#elif defined __THW_RS6000__
    strcat( detail, " RS6000" );
#elif defined __370__ || defined __THW_370__ || defined __s390__ || defined __s390x || \
      defined __zarch__ || defined __SYSC_ZARCH__
    strcat( detail, " System Z (370)" );
#endif

#if defined __STDC_VERSION__
  #if( __STDC_VERSION__ >= 201112L )
    strcat( detail, " C11" );
  #elif( __STDC_VERSION__ >= 199901L )
    strcat( detail, " C99" );
  #elif( __STDC_VERSION__ >= 199409L )
    strcat( detail, " C94" );
  #else
    strcat( detail, " C89/90" );
  #endif
#elif defined __STDC__
    strcat( detail, " C89/90" );
#else
    strcat( detail, " C" );
#endif

#if defined __LSB_VERSION__
    snprintf( detail + strlen( detail ), MAXSTRL - strlen( detail ),
              " LSB V%u.%u",
              (__LSB_VERSION__ / 10), (__LSB_VERSION__%10) );
#endif

#if ODS2_BIG_ENDIAN
    strcat( detail, " Big-Endian" );
#endif
    printmsg( HELP_SHOWVERPLATFM, MSG_TEXT, detail );

    /* Compiler */

    detail[0] = '\0';
#if defined __clang__
    strcat( detail, "Clang/LLVM" );
    snprintf( version, MAXSTRL, "%d.%d.%d",
              __clang_major__, __clang_minor__, __clang_patchlevel__ );
#elif defined(__ICC) || defined(__INTEL_COMPILER)
    strcat( detail, "ICC" );
    #if defined __VERSION__
        strcat( version, __VERSION__ );
    #endif
#elif defined(__HP_cc) || defined(__HP_aCC)
    strcat( detail, "HP C" );
    #if defined __HP_aCC && __HP_aCC > 1
        snprintf( version, MAXSTRL, "%02u.%02u.%02u",
                 (__HP_aCC / 10000u ),
                 (__HP_aCC / 100u ) % 100u,
                 (__HP_aCC % 100u ) % 100u );
    #elif defined __HP_cc && __HP_cc > 1
        snprintf( version, MAXSTRL, "%02u.%02u.%02u",
                 (__HP_cc / 10000u ),
                 (__HP_cc / 100u ) % 100u,
                 (__HP_cc % 100u ) % 100u );
    #endif
#elif defined(__IBMC__) || defined(__IBMCPP__)
    strcat( detail, "IBM XL C" );
    #if defined __COMPILER_VER__
        strcat( version, __COMPILER_VER__ );
    #elif defined __xlxc__
        strcat( version, __xlc__ );
    #else
        strcat( version, __IBMC__ );
    #endif
#elif defined __LCC__
    strcat( detail, "LCC" );
#elif defined ___llvm
    strcat( detail, "LLVM" );
#elif defined __MINGW64__
    strcat( detail, "MinGW-w64" );
    #if defined __MINGW64_VERSION_MAJOR
        snprintf( version, MAXSTRL, "%u.%u",
                  __MINGW64_VERSION_MAJOR, __MINGW64_VERSION_MINOR );
    #endif
#elif defined __MINGW32__
    strcat( detail, "MinGW" );
    #if defined __MINGW32_MAJOR_VERSIONR
        snprintf( version, MAXSTRL, "%u.%u",
                  __MINGW32_MAJOR_VERSION, __MINGW32_MINOR_VERSION );
    #endif
#elif defined(__PGI)
    strcat( detail, "PGCC" );
    snprintf( version, MAXSTRL, "%u.%u.%u",
              __PGIC__, __PGIC_MINOR, __PGIC_PATCHLEVEL__ );
#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
    strcat( detail, "Solaris Studio" );
    #if defined __SUNPRO_C
        snprintf( version, MAXSTRL, "%u.%u.%u",
                 (__SUNPRO_C / 0x100 ),
                 (__SUNPRO_C / 0x10 ) % 0x100,
                 (__SUNPRO_C % 0x10 ) );
    #elif defined __SUNPRO_CC
        snprintf( version, MAXSTRL, "%u.%u.%u",
                 (__SUNPRO_CC / 0x100 ),
                 (__SUNPRO_CC / 0x10 ) % 0x100,
                 (__SUNPRO_CC % 0x10 ) );
    #endif
#elif defined __sgi || defined sgi
    strcat( detail, "MIPSpro" );
    #if defined _SGI_COMPILER_VERSION
        snprintf( version, MAXSTRL, "%u.%u.%u",
                 (_SGI_COMPILER_VERSION / 100u),
                 (_SGI_COMPILER_VERSION / 10u) % 10u,
                 (_SGI_COMPILER_VERSION % 10u) );
    #elif defined _COMPILER_VERSION
        snprintf( version, MAXSTRL, "%u.%u.%u",
                 (_COMPILER_VERSION / 100u),
                 (_COMPILER_VERSION / 10u) % 10u,
                 (_COMPILER_VERSION % 10u) );
    #endif
#elif defined _MSC_FULL_VER
    strcat( detail, "Visual C++" );
    snprintf( version, MAXSTRL, "%d.%02d.%05d.%02d",
              (_MSC_FULL_VER / 10000000u),
              (_MSC_FULL_VER / 100000u ) % 100u,
              (_MSC_FULL_VER % 100000u ),
              _MSC_BUILD);
    #ifdef _MSC_VER
    { /* https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros */
        struct {
            char *ide; unsigned ver; } ides[] = {
            {"Visual Studio 6.0", 1200},
            {"Visual Studio .NET 2002 (7.0)",           1300},
            {"Visual Studio .NET 2003 (7.1)",           1310},
            {"Visual Studio 2005 (8.0)",                1400},
            {"Visual Studio 2008 (9.0)",                1500},
            {"Visual Studio 2010 (10.0)",               1600},
            {"Visual Studio 2012 (11.0)",               1700},
            {"Visual Studio 2013 (12.0)",               1800},
            {"Visual Studio 2015 (14.0)",               1900},
            {"Visual Studio 2017 RTW (15.0)",           1910},
            {"Visual Studio 2017 version 15.3",         1911},
            {"Visual Studio 2017 version 15.5",         1912},
            {"Visual Studio 2017 version 15.6",         1913},
            {"Visual Studio 2017 version 15.7",         1914},
            {"Visual Studio 2017 version 15.8",         1915},
            {"Visual Studio 2017 version 15.9",         1916},
            {"Visual Studio 2019 RTW (16.0)",           1920},
            {"Visual Studio 2019 version 16.1",         1921},
            {"Visual Studio 2019 version 16.2",         1922},
            {"Visual Studio 2019 version 16.3",         1923},
            {"Visual Studio 2019 version 16.4",         1924},
            {"Visual Studio 2019 version 16.5",         1925},
            {"Visual Studio 2019 version 16.6",         1926},
            {"Visual Studio 2019 version 16.7",         1927},
            {"Visual Studio 2019 version 16.8, 16.9",   1928},
            {"Visual Studio 2019 version 16.10, 16.11", 1929},
            {"Visual Studio 2022 RTW (17.0)",           1930},
            {"Visual Studio 2022 version 17.1",         1931},
            {"Visual Studio 2022 version 17.2",         1932},
            {"Visual Studio 2022 version 17.3",         1933},
            {"Visual Studio 2022+", 100000}, }, *ip;
        for( ip = ides; ip->ver < _MSC_VER; ip++ )
            ;
        strcat( version, ", " );
        strcat( version, ip->ide );
    }
    #endif
#elif defined _MSC_VER
    strcat( detail, "Visual Studio" );
    snprintf( version, MAXSTRL, "%d", _MSC_VER );
#elif defined __CC_ARM
    strcat( version, "ARM Compiler" );
    snprintf( version, MAXSTRL, "%u.%u.%u build %u",
             (__ARMCC_VERSION / 100000u ),
             (__ARMCC_VERSION / 10000u ) % 10u,
             (__ARMCC_VERSION / 1000u ) % 10u,
             (__ARMCC_VERSION % 1000u ) );
#elif defined __GNUC__
    strcat( detail, "gcc" );
    #if defined __VERSION__
        strcat( version, __VERSION__ );
    #else
        snprintf( version, MAXSTRL, "%d.%d", __GNUC__, __GNUC_MINOR__ );
    #endif
#elif defined __DECC__ || defined _DECC
    strcat( detail, "DEC C" );
    {
        char ct;
        switch( (__DECC_VER / 10000u) % 10u ) {
        case 6:
            ct = 'T'; break;
        case 8:
            ct = 'S'; break;
        case 9:
            ct = 'V'; break;
        default:
            ct = 'X'; break;
        }
        snprintf( version, MAXSTRL, "%c%u.%u-%03u", ct,
                 (__DECC_VER / 10000000u),
                 (__DECC_VER / 100000u) % 100u,
                 (__DECC_VER % 10000u) );
    }
#elif defined VAXC || defined VAX11C || defined __VAXC || defined __VAX11C
    strcat( detail, "VAX C" );
#endif
    if( !version[0] )
        strcat( version, "unknown" );
#undef strcat
#undef MAXSTRL

    printmsg( HELP_SHOWVERCOMPLR, MSG_TEXT, detail, version );

    free( detail ); /* includes version */
    detail = version = NULL;

#ifdef DEBUG_BUILD
    if( vld_present )
        printmsg( HELP_SHOWVLD, MSG_TEXT );
#endif
#ifdef USE_WNASPI32
    printmsg( HELP_SHOWVERSCSI, MSG_TEXT );
#endif
    putchar( ' ');
    phyio_show( SHOW_FILE64 );
#ifdef USE_VHD
    putchar( ' ');
    (void) phyvhd_available( TRUE );
#else
    printmsg( HELP_SHOWVERNOVHD, MSG_TEXT );
#endif
    return;
}

/*********************************************************** get_cwd() */

static char *get_cwd( void ) {
    size_t size = 32;
    char *buf = NULL;
    while( 1 ) {
        char *nbuf;
        nbuf = (char *)realloc( buf, size );
        if( nbuf == NULL ) {
            free( buf );
            return NULL;
        }
        buf = nbuf;
        if( getcwd( buf, size ) != NULL )
            break;
        if( errno != ERANGE ) {
            printmsg( ODS2_OSERROR, 0, strerror( errno ) );
            return NULL;
        }
        size *= 2;
    }
    return buf;
}
