/*
 *@@sourcefile debug.c:
 *      this file contains debugging functions for the
 *      exception handlers in except.c.
 *
 *      This code is capable of unwinding the stack from
 *      a given address and trying to get function names
 *      and source line numbers, either from the respective
 *      module's debug code (if present) or from a SYM file,
 *      which is searched for in the directory of the module
 *      or in ?:\OS2\PDPSI\PMDF\WARP4.
 *
 *      This file incorporates code from the following:
 *      -- Marc Fiammante, John Currier, Kim Rasmussen,
 *         Anthony Cruise (EXCEPT3.ZIP package for a generic
 *         exception handling DLL, available at Hobbes).
 *
 *      Usage: All OS/2 programs.
 *
 *      Note: Version numbering in this file relates to XWorkplace version
 *            numbering.
 *
 *@@changed V0.9.0 [umoeller]: made some declarations C++-compatible
 *@@changed V0.9.1 (2000-01-30) [umoeller]: greatly cleaned up this file
 *
 *@@header "helpers\debug.h"
 */

/*
 *      This file Copyright (C) 1992-2010 Ulrich M”ller,
 *                                        Kim Rasmussen,
 *                                        Marc Fiammante,
 *                                        John Currier,
 *                                        Anthony Cruise.
 *      This file is part of the "XWorkplace helpers" source package.
 *      This is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published
 *      by the Free Software Foundation, in version 2 as it comes in the
 *      "COPYING" file of the XWorkplace main distribution.
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 */

//#define DEBUG_SYMDUMP // enable to dump sym file to log V1.0.0 (2002-08-21) [paperino]

#define OS2EMX_PLAIN_CHAR
    // this is needed for "os2emx.h"; if this is defined,
    // emx will define PSZ as _signed_ char, otherwise
    // as unsigned char

#define INCL_DOSPROCESS
#define INCL_DOSMODULEMGR
#define INCL_DOSMISC
#define INCL_DOSERRORS
#include <os2.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define DONT_REPLACE_MALLOC
#include "setup.h"                      // code generation and debugging options

#include "helpers\debug.h"
#include "helpers\dosh.h"

#pragma hdrstop

#include <fcntl.h>
#ifdef __EMX__
    #include <sys\types.h>      // required for sys\stat.h; UM 99-10-22
#endif
#include <sys\stat.h>
#include <share.h>
#include <io.h>

#ifndef DWORD
#define DWORD unsigned long
#endif
#ifndef WORD
#define WORD  unsigned short
#endif

#pragma stack16(512)
#define HF_STDERR 2

/*
 *@@category: Helpers\Control program helpers\Exceptions/debugging
 *      See except.c and debug.c.
 */

/* ******************************************************************
 *
 *   Global variables
 *
 ********************************************************************/

// this specifies whether we're dealing with 32-bit code;
// this gets changed whenever 16-bit count is detected
STATIC BOOL     f32bit = TRUE;

/*
 * Global variables for Read32PmDebug:
 *
 */

ULONG           func_ofs;
ULONG           pubfunc_ofs;
char            func_name[128];
ULONG           var_ofs = 0;

struct {
    BYTE            name[128];
    ULONG           stack_offset;
    USHORT          type_idx;
} autovar_def[100];

#pragma pack(1)

BYTE           *type_name[] =
{
    "8 bit signed                     ",
    "16 bit signed                    ",
    "32 bit signed                    ",
    "Unknown (0x83)                   ",
    "8 bit unsigned                   ",
    "16 bit unsigned                  ",
    "32 bit unsigned                  ",
    "Unknown (0x87)                   ",
    "32 bit real                      ",
    "64 bit real                      ",
    "80 bit real                      ",
    "Unknown (0x8B)                   ",
    "64 bit complex                   ",
    "128 bit complex                  ",
    "160 bit complex                  ",
    "Unknown (0x8F)                   ",
    "8 bit boolean                    ",
    "16 bit boolean                   ",
    "32 bit boolean                   ",
    "Unknown (0x93)                   ",
    "8 bit character                  ",
    "16 bit characters                ",
    "32 bit characters                ",
    "void                             ",
    "15 bit unsigned                  ",
    "24 bit unsigned                  ",
    "31 bit unsigned                  ",
    "Unknown (0x9B)                   ",
    "Unknown (0x9C)                   ",
    "Unknown (0x9D)                   ",
    "Unknown (0x9E)                   ",
    "Unknown (0x9F)                   ",
    "near pointer to 8 bit signed     ",
    "near pointer to 16 bit signed    ",
    "near pointer to 32 bit signed    ",
    "Unknown (0xA3)                   ",
    "near pointer to 8 bit unsigned   ",
    "near pointer to 16 bit unsigned  ",
    "near pointer to 32 bit unsigned  ",
    "Unknown (0xA7)                   ",
    "near pointer to 32 bit real      ",
    "near pointer to 64 bit real      ",
    "near pointer to 80 bit real      ",
    "Unknown (0xAB)                   ",
    "near pointer to 64 bit complex   ",
    "near pointer to 128 bit complex  ",
    "near pointer to 160 bit complex  ",
    "Unknown (0xAF)                   ",
    "near pointer to 8 bit boolean    ",
    "near pointer to 16 bit boolean   ",
    "near pointer to 32 bit boolean   ",
    "Unknown (0xB3)                   ",
    "near pointer to 8 bit character  ",
    "near pointer to 16 bit characters",
    "near pointer to 32 bit characters",
    "near pointer to void             ",
    "near pointer to 15 bit unsigned  ",
    "near pointer to 24 bit unsigned  ",
    "near pointer to 31 bit unsigned  ",
    "Unknown (0xBB)                   ",
    "Unknown (0xBC)                   ",
    "Unknown (0xBD)                   ",
    "Unknown (0xBE)                   ",
    "Unknown (0xBF)                   ",
    "far pointer to 8 bit signed      ",
    "far pointer to 16 bit signed     ",
    "far pointer to 32 bit signed     ",
    "Unknown (0xC3)                   ",
    "far pointer to 8 bit unsigned    ",
    "far pointer to 16 bit unsigned   ",
    "far pointer to 32 bit unsigned   ",
    "Unknown (0xC7)                   ",
    "far pointer to 32 bit real       ",
    "far pointer to 64 bit real       ",
    "far pointer to 80 bit real       ",
    "Unknown (0xCB)                   ",
    "far pointer to 64 bit complex    ",
    "far pointer to 128 bit complex   ",
    "far pointer to 160 bit complex   ",
    "Unknown (0xCF)                   ",
    "far pointer to 8 bit boolean     ",
    "far pointer to 16 bit boolean    ",
    "far pointer to 32 bit boolean    ",
    "Unknown (0xD3)                   ",
    "far pointer to 8 bit character   ",
    "far pointer to 16 bit characters ",
    "far pointer to 32 bit characters ",
    "far pointer to void              ",
    "far pointer to 15 bit unsigned   ",
    "far pointer to 24 bit unsigned   ",
    "far pointer to 31 bit unsigned   ",
};

// Thanks to John Currier:
// Do not call 16 bit code in myHandler function to prevent call
// to __EDCThunkProlog and problems is guard page exception handling
// Also reduce the stack size to 1K for true 16 bit calls.
// 16 bit calls thunk will now only occur on fatal exceptions
#pragma stack16(1024)

// ------------------------------------------------------------------
// Last 8 bytes of 16:16 file when CODEVIEW debugging info is present
#pragma pack(1)
struct _eodbug
{
    unsigned short  dbug;       // 'NB' signature
    unsigned short  ver;        // version
    unsigned long   dfaBase;    // size of codeview info
} G_eodbug;

#define         DBUGSIG         0x424E
#define         SSTMODULES      0x0101
#define         SSTPUBLICS      0x0102
#define         SSTTYPES        0x0103
#define         SSTSYMBOLS      0x0104
#define         SSTSRCLINES     0x0105
#define         SSTLIBRARIES    0x0106
#define         SSTSRCLINES2    0x0109
#define         SSTSRCLINES32   0x010B

typedef struct _SYMBASE
{
    unsigned short  dbug;       // 'NB' signature
    unsigned short  ver;        // version
    unsigned long   lfoDir;     // file offset to dir entries
} SYMBASE;

typedef struct _SSDIR
{
    unsigned short  sst;        // SubSection Type
    unsigned short  modindex;   // Module index number
    unsigned long   lfoStart;   // Start of section
    unsigned short  cb;         // Size of section
} SSDIR;

typedef struct _SSDIR32
{
    unsigned short  sst;        // SubSection Type
    unsigned short  modindex;   // Module index number
    unsigned long   lfoStart;   // Start of section
    unsigned long   cb;         // Size of section
} SSDIR32;

typedef struct _SSMODULE
{
    unsigned short  csBase;     // code segment base
    unsigned short  csOff;      // code segment offset
    unsigned short  csLen;      // code segment length
    unsigned short  ovrNum;     // overlay number
    unsigned short  indxSS;     // Index into sstLib or 0
    unsigned short  reserved;
    char            csize;      // size of prefix string
} SSMODULE;

typedef struct _SSMOD32
{
    unsigned short  csBase;     // code segment base
    unsigned long   csOff;      // code segment offset
    unsigned long   csLen;      // code segment length
    unsigned long   ovrNum;     // overlay number
    unsigned short  indxSS;     // Index into sstLib or 0
    unsigned long   reserved;
    char            csize;      // size of prefix string
} SSMOD32;

typedef struct _SSPUBLIC
{
    unsigned short  offset;
    unsigned short  segment;
    unsigned short  type;
    char            csize;
} SSPUBLIC;

typedef struct _SSPUBLIC32
{
    unsigned long   offset;
    unsigned short  segment;
    unsigned short  type;
    char            csize;
} SSPUBLIC32;

typedef struct _SSLINEENTRY32
{
    unsigned short  LineNum;
    unsigned short  FileNum;
    unsigned long   Offset;
} SSLINEENTRY32;

typedef struct _FIRSTLINEENTRY32
{
    unsigned short  LineNum;
    unsigned char   entry_type;
    unsigned char   reserved;
    unsigned short  numlines;
    unsigned short  segnum;
} FIRSTLINEENTRY32;

typedef struct _SSFILENUM32
{
    unsigned long   first_displayable;  // Not used
    unsigned long   number_displayable;     // Not used
    unsigned long   file_count; // number of source files
} SSFILENUM32;

/*
 *@@ XDEBUGINFO:
 *      buffers for Read... funcs.
 *
 *@@added V0.9.4 (2000-06-15) [umoeller]
 */

typedef struct _XDEBUGINFO
{
    char            szNrFile[300];      // receives source file
    char            szNrLine[300];      // receives line number
    char            szNrPub[300];       // receives function name

    struct new_seg  *pseg;
    struct o32_obj  *pobj;              // flat .EXE object table entry

    SYMBASE         base;

    SSDIR           *pDirTab;
    SSDIR32         *pDirTab32;
    unsigned char   *pEntTab;
    unsigned long   lfaBase;
    SSMOD32         ssmod32;
    SSPUBLIC32      sspub32;

    SSMODULE        ssmod;
    SSPUBLIC        sspub;
} XDEBUGINFO, *PXDEBUGINFO;

#pragma pack()

/* ******************************************************************
 *
 *   PART 1: ANALYZE DEBUG CODE
 *
 ********************************************************************/

STATIC int Read16CodeView(FILE *LogFile, PXDEBUGINFO pxdi, int fh, int TrapSeg, int TrapOff, CHAR *FileName);
STATIC int Read32PmDebug(FILE *LogFile, PXDEBUGINFO pxdi, int fh, int TrapSeg, int TrapOff, CHAR *FileName);

/*
 *@@ WriteAddressInfo:
 *      formats and writes a line into the trap log
 *      file.
 *
 *      This gets called for each line from the
 *      stack dump. At this point, the line in the
 *      trap log already has:
 *
 +          CS:EIP  : 000109FF  XMLVIEW :0
 +                                          ^^^ and we write here
 *      After this call, we have.
 *
 +          CS:EIP  : 000109FF  XMLVIEW :0  xxx.c  123 ConfirmCreate__Fv
 +                                          ^^^ and we write here
 *
 *@@added V0.9.12 (2001-05-12) [umoeller]
 */

STATIC VOID WriteDebugInfo(FILE *LogFile,              // in: open log file
                           PXDEBUGINFO pxdi)           // in: debug info
{
    fprintf(LogFile,
            "%s%s%s",
            pxdi->szNrFile,
            pxdi->szNrLine,
            pxdi->szNrPub);
}

/*
 *@@ dbgPrintDebugInfo:
 *      this is the main entry point into analyzing debug
 *      code.
 *
 *      This analyzes a given address and tries to find
 *      debug code descriptions for this address. If found,
 *      the information is written to the given log file.
 *
 *      Gets called from dbgPrintStack.
 *
 *      This returns NO_ERROR if the could was successfully
 *      analyzed or something != 0 if we failed.
 *
 *      New with V0.84.
 */

APIRET dbgPrintDebugInfo(FILE *LogFile,         // out: log file to write to
                         CHAR *FileName,        // in: EXE/DLL module file name
                         ULONG Object,          // in: trapping object (from DosQueryModFromEIP)
                         ULONG TrapOffset)      // in: trapping address (from DosQueryModFromEIP)
{
    APIRET                  rc = 0;
    int                     ModuleFile = 0;
    static struct exe_hdr   OldExeHeader;
    static struct new_exe   NewExeHeader;

    ULONG                   ulSegment = Object + 1;     // segment no. is object no. + 1

    XDEBUGINFO              xdi;
    memset(&xdi, 0, sizeof(xdi));

    // open the module file for reading to analyze the code
    ModuleFile = sopen(FileName, O_RDONLY | O_BINARY, SH_DENYNO);

    if (ModuleFile != -1)
    {
        // file found:
        // read old Exe header
        if (read(ModuleFile, (void*)&OldExeHeader, 64) == -1L)
        {
            fprintf(LogFile, "errno %d reading old exe header\n", errno);
            close(ModuleFile);
            return 2;
        }
        // seek to new Exe header
        if (lseek(ModuleFile, (long)E_LFANEW(OldExeHeader), SEEK_SET) == -1L)
        {
            fprintf(LogFile, "errno %d seeking to new exe header\n", errno);
            close(ModuleFile);
            return 3;
        }
        if (read(ModuleFile, (void *)&NewExeHeader, 64) == -1L)
        {
            fprintf(LogFile, "errno %d reading new exe header\n", errno);
            close(ModuleFile);
            return 4;
        }

        // check EXE signature
        if (NE_MAGIC(NewExeHeader) == E32MAGIC)
        {
            /*
             * flat 32 executable:
             *
             */

            // do analysis for 32-bit code
            if (!(rc = Read32PmDebug(LogFile,
                                     &xdi,                // output
                                     ModuleFile,
                                     ulSegment,
                                     TrapOffset,
                                     FileName)))
                WriteDebugInfo(LogFile, &xdi);

            close(ModuleFile);

            // rc !=0 try with DBG file
            if (rc != 0)
            {
                strcpy(FileName + strlen(FileName) - 3, "DBG");     // Build DBG File name
                ModuleFile = sopen(FileName, O_RDONLY | O_BINARY, SH_DENYNO);
                if (ModuleFile != -1)
                {
                    if (!(rc = Read32PmDebug(LogFile,
                                             &xdi,
                                             ModuleFile,
                                             ulSegment,
                                             TrapOffset,
                                             FileName)))
                        WriteDebugInfo(LogFile, &xdi);

                    close(ModuleFile);
                }
            }

            return rc;
        }
        else
        {
            if (NE_MAGIC(NewExeHeader) == NEMAGIC)
            {
                /*
                 * 16:16 executable:
                 *
                 */

                if ((xdi.pseg = (struct new_seg *)calloc(NE_CSEG(NewExeHeader),
                                                         sizeof(struct new_seg)))
                            == NULL)
                {
                    fprintf(LogFile, "Out of memory!");
                    close(ModuleFile);
                    return -1;
                }
                if (  lseek(ModuleFile,
                            E_LFANEW(OldExeHeader) + NE_SEGTAB(NewExeHeader),
                            SEEK_SET) == -1L)
                {
                    fprintf(LogFile, "Error %u seeking segment table in %s\n", errno, FileName);
                    free(xdi.pseg);
                    close(ModuleFile);
                    return 9;
                }

                if (read(ModuleFile,
                         (void *)xdi.pseg,
                         NE_CSEG(NewExeHeader) * sizeof(struct new_seg))
                      == -1)
                {
                    fprintf(LogFile, "Error %u reading segment table from %s\n", errno, FileName);
                    free(xdi.pseg);
                    close(ModuleFile);
                    return 10;
                }

                if (!(rc = Read16CodeView(LogFile,
                                          &xdi,
                                          ModuleFile,
                                          ulSegment,
                                          TrapOffset,
                                          FileName)))
                    WriteDebugInfo(LogFile, &xdi);

                free(xdi.pseg);
                close(ModuleFile);

                // rc !=0 try with DBG file
                if (rc != 0)
                {
                    strcpy(FileName + strlen(FileName) - 3, "DBG");     // Build DBG File name
                    ModuleFile = sopen(FileName,
                                       O_RDONLY | O_BINARY, SH_DENYNO);
                    if (ModuleFile != -1)
                    {
                        if (!(rc = Read16CodeView(LogFile,
                                                  &xdi,
                                                  ModuleFile,
                                                  ulSegment,
                                                  TrapOffset,
                                                  FileName)))
                            WriteDebugInfo(LogFile, &xdi);

                        close(ModuleFile);
                    }
                }
                return rc;
            }
            else
            {
                /*
                 * Unknown executable:
                 *
                 */

                fprintf(LogFile, "Error, could not find exe signature");
                close(ModuleFile);
                return 11;
            }
        }
    } // end if (ModuleFile != -1)
    else
    {
        fprintf(LogFile, "Error %d opening module file %s", errno, FileName);
        return 1;
    }                           // endif

    // return 0;        we never get here
}

char            fname[128],
                ModName[80];
char            ename[128],
                dummy[128];

#define MAX_USERDEFS 300        // raised from 150 V0.9.1 (2000-01-30) [umoeller]
#define MAX_POINTERS 300        // raised from 150 V0.9.1 (2000-01-30) [umoeller]

USHORT          userdef_count;
USHORT          pointer_count;

struct one_userdef_rec
{
    USHORT          idx;
    USHORT          type_index;
    BYTE            name[33];
} one_userdef[MAX_USERDEFS];

struct one_pointer_rec
{
    USHORT          idx;
    USHORT          type_index;
    BYTE            type_qual;
    BYTE            name[33];
} one_pointer[MAX_POINTERS];

/*
 * Read32PmDebug:
 *      parses 32-bit debug code.
 *      Called from dbgPrintDebugInfo for 32-bit modules.
 */

STATIC int Read32PmDebug(FILE *LogFile,        // in: text log file to write to
                         PXDEBUGINFO pxdi,
                         int ModuleFile,       // in: module file opened with sopen()
                         int TrapSeg,
                         int TrapOff,
                         CHAR *FileName)
{
    static unsigned int CurrSymSeg, NrSymbol,
                        /* offset, */ NrPublic,
                        NrFile, NrLine, /* NrEntry */
                        numdir, namelen,
                        numlines /* , line */;
    static int      ModIndex;
    static int      bytesread, i, j;
    static SSLINEENTRY32 LineEntry;
    static SSFILENUM32 FileInfo;
    static FIRSTLINEENTRY32 FirstLine;
    static BYTE     dump_vars = FALSE;
    static USHORT   idx;
    static BOOL     read_types;
    static LONG     lSize;

    ModIndex = 0;
    // See if any CODEVIEW info
    if (lseek(ModuleFile, -8L, SEEK_END) == -1)
    {
        fprintf(LogFile, "Error %u seeking CodeView table in %s\n", errno, FileName);
        return 18;
    }

    if (read(ModuleFile,
             (void *)&G_eodbug, 8)
            == -1)
    {
        fprintf(LogFile, "Error %u reading debug info from %s\n", errno, FileName);
        return 19;
    }
    if (G_eodbug.dbug != DBUGSIG)
    {
        // fprintf(LogFile,"\nNo CodeView information stored.\n");
        return 100;
    }

    if (    (pxdi->lfaBase = lseek(ModuleFile,
                                   -(LONG)G_eodbug.dfaBase,
                                   SEEK_END))
         == -1L)
    {
        fprintf(LogFile, "Error %u seeking base codeview data in %s\n", errno, FileName);
        return 20;
    }

    if (read(ModuleFile,
             (void *)&pxdi->base, 8)
        == -1)
    {
        fprintf(LogFile, "Error %u reading base codeview data in %s\n", errno, FileName);
        return 21;
    }

    if (lseek(ModuleFile,
              pxdi->base.lfoDir - 8 + 4,
              SEEK_CUR)
        == -1)
    {
        fprintf(LogFile, "Error %u seeking dir codeview data in %s\n", errno, FileName);
        return 22;
    }

    if (read(ModuleFile,
             (void *)&numdir, 4)
        == -1)
    {
        fprintf(LogFile, "Error %u reading dir codeview data in %s\n", errno, FileName);
        return 23;
    }

    // Read dir table into buffer
    if (    (pxdi->pDirTab32 = (SSDIR32*)calloc(numdir,
                                             sizeof(SSDIR32)))
        == NULL)
    {
        fprintf(LogFile, "Out of memory!");
        return -1;
    }

    if (read(ModuleFile,
             (void*)pxdi->pDirTab32,
             numdir * sizeof(SSDIR32))
        == -1)
    {
        fprintf(LogFile, "Error %u reading codeview dir table from %s\n", errno, FileName);
        free(pxdi->pDirTab32);
        return 24;
    }

    i = 0;
    while (i < numdir)
    {
        if (pxdi->pDirTab32[i].sst != SSTMODULES)
        {
            i++;
            continue;
        }

        NrPublic = 0x0;
        NrSymbol = 0;
        NrLine = 0x0;
        NrFile = 0x0;
        CurrSymSeg = 0;
        // point to subsection
        lseek(ModuleFile,
              pxdi->pDirTab32[i].lfoStart + pxdi->lfaBase,
              SEEK_SET);
        read(ModuleFile,
             (void*)&pxdi->ssmod32.csBase,
             sizeof(SSMOD32));
        read(ModuleFile,
             (void*)ModName,
             (unsigned)pxdi->ssmod32.csize);
        ModIndex = pxdi->pDirTab32[i].modindex;
        ModName[pxdi->ssmod32.csize] = '\0';
        i++;

        read_types = FALSE;

        while (     (pxdi->pDirTab32[i].modindex == ModIndex)
                 && (i < numdir)
              )
        {
            // point to subsection
            lseek(ModuleFile,
                  pxdi->pDirTab32[i].lfoStart + pxdi->lfaBase,
                  SEEK_SET);

            switch (pxdi->pDirTab32[i].sst)
            {
                case SSTPUBLICS:
                    bytesread = 0;
                    while (bytesread < pxdi->pDirTab32[i].cb)
                    {
                        bytesread += read(ModuleFile,
                                          (void *)&pxdi->sspub32.offset,
                                          sizeof(pxdi->sspub32));
                        bytesread += read(ModuleFile,
                                          (void*)ename,
                                          (unsigned)pxdi->sspub32.csize);
                        ename[pxdi->sspub32.csize] = '\0';
                        if (    (pxdi->sspub32.segment == TrapSeg)
                             && (pxdi->sspub32.offset <= TrapOff)
                             && (pxdi->sspub32.offset >= NrPublic)
                           )
                        {
                            NrPublic = pubfunc_ofs = pxdi->sspub32.offset;
                            read_types = TRUE;
                            sprintf(pxdi->szNrPub,
                                    "%s %s (%s)\n",
                                    (pxdi->sspub32.type == 1)
                                            ? " Abs"
                                            : " ",
                                    ename,
                                    ModName
                                );
                            // but continue, because there might be a
                            // symbol that comes closer
                        }
                    }
                    break;

                // Read symbols, so we can dump the variables on the stack
                case SSTSYMBOLS:
                    if (TrapSeg != pxdi->ssmod32.csBase)
                        break;

                    bytesread = 0;
                    while (bytesread < pxdi->pDirTab32[i].cb)
                    {
                        static USHORT   usLength;
                        static BYTE     b1,
                                        b2;
                        static BYTE     bType;
                                       // *ptr;
                        static ULONG    ofs;
                        // static ULONG    last_addr = 0;
                        static BYTE     str[256];
                        static struct symseg_rec symseg;
                        static struct symauto_rec symauto;
                        static struct symproc_rec symproc;

                        // Read the length of this subentry
                        bytesread += read(ModuleFile, &b1, 1);
                        if (b1 & 0x80)
                        {
                            bytesread += read(ModuleFile, &b2, 1);
                            usLength = ((b1 & 0x7F) << 8) + b2;
                        }
                        else
                            usLength = b1;

                        ofs = tell(ModuleFile);

                        bytesread += read(ModuleFile, &bType, 1);

                        switch (bType)
                        {
                            case SYM_CHANGESEG:
                                read(ModuleFile, &symseg, sizeof(symseg));
                                CurrSymSeg = symseg.seg_no;
                                break;

                            case SYM_PROC:
                            case SYM_CPPPROC:
                                read(ModuleFile, &symproc, sizeof(symproc));
                                read(ModuleFile, str, symproc.name_len);
                                str[symproc.name_len] = 0;

                                if ((CurrSymSeg == TrapSeg) &&
                                    (symproc.offset <= TrapOff) &&
                                    (symproc.offset >= NrSymbol))
                                {

                                    dump_vars = TRUE;
                                    var_ofs = 0;
                                    NrSymbol = symproc.offset;
                                    func_ofs = symproc.offset;

                                    strcpy(func_name, str);
                                }
                                else
                                {
                                    dump_vars = FALSE;
                                }
                                break;

                            case SYM_AUTO:
                                if (!dump_vars)
                                    break;

                                read(ModuleFile, &symauto, sizeof(symauto));
                                read(ModuleFile, str, symauto.name_len);
                                str[symauto.name_len] = 0;

                                strcpy(autovar_def[var_ofs].name, str);
                                autovar_def[var_ofs].stack_offset = symauto.stack_offset;
                                autovar_def[var_ofs].type_idx = symauto.type_idx;
                                var_ofs++;
                                break;

                        }

                        bytesread += usLength;

                        lseek(ModuleFile, ofs + usLength, SEEK_SET);
                    }
                    break;

                case SSTTYPES:
                    // if (ModIndex != TrapSeg)
                    if (!read_types)
                        break;

                    bytesread = 0;
                    idx = 0x200;
                    userdef_count = 0;
                    pointer_count = 0;
                    while (bytesread < pxdi->pDirTab32[i].cb)
                    {
                        static struct type_rec type;
                        static struct type_userdefrec udef;
                        static struct type_pointerrec point;
                        static ULONG    ofs;
                        static BYTE     str[256];

                        // Read the length of this subentry
                        ofs = tell(ModuleFile);

                        read(ModuleFile, &type, sizeof(type));
                        bytesread += sizeof(type);

                        switch (type.type)
                        {
                            case TYPE_USERDEF:
                                if (userdef_count >= MAX_USERDEFS)
                                    break;

                                read(ModuleFile, &udef, sizeof(udef));
                                read(ModuleFile, str, udef.name_len);
                                str[udef.name_len] = 0;

                                // Insert userdef in table
                                one_userdef[userdef_count].idx = idx;
                                one_userdef[userdef_count].type_index = udef.type_index;
                                memcpy(one_userdef[userdef_count].name,
                                       str,
                                       _min(udef.name_len + 1, 32));
                                one_userdef[userdef_count].name[32] = 0;
                                userdef_count++;
                                break;

                            case TYPE_POINTER:
                                if (pointer_count >= MAX_POINTERS)
                                    break;

                                read(ModuleFile, &point, sizeof(point));
                                read(ModuleFile, str, point.name_len);
                                str[point.name_len] = 0;

                                // Insert userdef in table
                                one_pointer[pointer_count].idx = idx;
                                one_pointer[pointer_count].type_index = point.type_index;
                                memcpy(one_pointer[pointer_count].name,
                                       str,
                                       _min(point.name_len + 1, 32));
                                one_pointer[pointer_count].name[32] = 0;
                                one_pointer[pointer_count].type_qual = type.type_qual;
                                pointer_count++;
                                break;
                        }

                        ++idx;

                        bytesread += type.length;

                        lseek(ModuleFile, ofs + type.length + 2, SEEK_SET);
                    }
                    break;

                case SSTSRCLINES32:
                    if (TrapSeg != pxdi->ssmod32.csBase)
                        break;

                    // read first line
                    do
                    {
                        read(ModuleFile, (void *)&FirstLine, sizeof(FirstLine));

                        if (FirstLine.LineNum != 0)
                        {
                            fprintf(LogFile, "Missing Line table information\n");
                            break;
                        }       // endif
                        numlines = FirstLine.numlines;
                        // Other type of data skip 4 more bytes
                        if (FirstLine.entry_type < 4)
                        {
                            read(ModuleFile, (void *)&lSize, 4);
                            if (FirstLine.entry_type == 3)
                                lseek(ModuleFile, lSize, SEEK_CUR);
                        }
                    }
                    while (FirstLine.entry_type == 3);

                    for (j = 0; j < numlines; j++)
                    {
                        switch (FirstLine.entry_type)
                        {
                            case 0:
                                read(ModuleFile, (void *)&LineEntry, sizeof(LineEntry));
                                // Changed by Kim Rasmussen 26/06 1996 to ignore linenumber 0
                                // if (LineEntry.Offset+ssmod32.csOff<=TrapOff && LineEntry.Offset+ssmod32.csOff>=NrLine) {
                                if (    (LineEntry.LineNum)
                                     && (LineEntry.Offset + pxdi->ssmod32.csOff
                                            <= TrapOff)
                                     && (LineEntry.Offset + pxdi->ssmod32.csOff >= NrLine)
                                   )
                                {
                                    NrLine = LineEntry.Offset;
                                    NrFile = LineEntry.FileNum;
                                    /*pOffset =sprintf(szNrLine,"%04X:%08X  line #%hu ",
                                     * ssmod32.csBase,LineEntry.Offset,
                                     * LineEntry.LineNum); */
                                    sprintf(pxdi->szNrLine, "% 6hu", LineEntry.LineNum);
                                }
                                break;

                            case 1:
                                lseek(ModuleFile, sizeof(struct linlist_rec), SEEK_CUR);
                                break;

                            case 2:
                                lseek(ModuleFile, sizeof(struct linsourcelist_rec), SEEK_CUR);
                                break;

                            case 3:
                                lseek(ModuleFile, sizeof(struct filenam_rec), SEEK_CUR);
                                break;

                            case 4:
                                lseek(ModuleFile, sizeof(struct pathtab_rec), SEEK_CUR);
                                break;

                        }
                    }

                    if (NrFile != 0)
                    {
                        // file found:
                        read(ModuleFile, (void*)&FileInfo, sizeof(FileInfo));
                        namelen = 0;
                        for (j = 1; j <= FileInfo.file_count; j++)
                        {
                            namelen = 0;
                            read(ModuleFile, (void *)&namelen, 1);
                            read(ModuleFile, (void *)ename, namelen);
                            if (j == NrFile)
                                break;
                        }
                        ename[namelen] = '\0';
                        //  pOffset=sprintf(szNrLine+pOffset," (%s) (%s)\n",ename,ModName);
                        sprintf(pxdi->szNrFile, "%11.11s ", ename);
                    }
                    else
                    {
                        // strcat(szNrLine,"\n"); avoid new line for empty name fill
                        strcpy(pxdi->szNrFile, "file?       ");
                    } // endif
                    break;
            } // end switch

            i++;
        } // end while modindex
    } // End While i < numdir

    free(pxdi->pDirTab32);

    return 0;
}

/*
 * Read16CodeView:
 *      parses 16-bit debug code.
 *      Called from dbgPrintDebugInfo for 16-bit modules.
 */

STATIC int Read16CodeView(FILE *LogFile,       // in: text log file to write to
                          PXDEBUGINFO pxdi,
                          int fh,
                          int TrapSeg,
                          int TrapOff,
                          CHAR *FileName)
{
    static unsigned short int offset,
                    NrPublic, NrLine,
                    numdir,
                    namelen, numlines,
                    line;
    static int      ModIndex;
    static int      bytesread, i, j;

    ModIndex = 0;
    // See if any CODEVIEW info
    if (lseek(fh, -8L, SEEK_END) == -1)
    {
        fprintf(LogFile, "Error %u seeking CodeView table in %s\n", errno, FileName);
        return 18;
    }

    if (read(fh, (void *)&G_eodbug, 8) == -1)
    {
        fprintf(LogFile, "Error %u reading debug info from %s\n", errno, FileName);
        return 19;
    }
    if (G_eodbug.dbug != DBUGSIG)
    {
        // fprintf(LogFile,"\nNo CodeView information stored.\n");
        return 100;
    }

    if ((pxdi->lfaBase = lseek(fh, -(LONG)G_eodbug.dfaBase, SEEK_END)) == -1L)
    {
        fprintf(LogFile, "Error %u seeking base codeview data in %s\n", errno, FileName);
        return 20;
    }

    if (read(fh, (void *)&pxdi->base, 8) == -1)
    {
        fprintf(LogFile, "Error %u reading base codeview data in %s\n", errno, FileName);
        return 21;
    }

    if (lseek(fh, pxdi->base.lfoDir - 8, SEEK_CUR) == -1)
    {
        fprintf(LogFile, "Error %u seeking dir codeview data in %s\n", errno, FileName);
        return 22;
    }

    if (read(fh, (void *)&numdir, 2) == -1)
    {
        fprintf(LogFile, "Error %u reading dir codeview data in %s\n", errno, FileName);
        return 23;
    }

    // Read dir table into buffer
    if ((pxdi->pDirTab = (SSDIR*)calloc(numdir, sizeof(SSDIR))) == NULL)
    {
        fprintf(LogFile, "Out of memory!");
        return -1;
    }

    if (read(fh, (void*)pxdi->pDirTab, numdir * sizeof(SSDIR)) == -1)
    {
        fprintf(LogFile, "Error %u reading codeview dir table from %s\n", errno, FileName);
        free(pxdi->pDirTab);
        return 24;
    }

    i = 0;
    while (i < numdir)
    {
        if (pxdi->pDirTab[i].sst != SSTMODULES)
        {
            i++;
            continue;
        }
        NrPublic = 0x0;
        NrLine = 0x0;
        // point to subsection
        lseek(fh, pxdi->pDirTab[i].lfoStart + pxdi->lfaBase, SEEK_SET);
        read(fh, (void *)&pxdi->ssmod.csBase, sizeof(SSMODULE));
        read(fh, (void *)ModName, (unsigned)pxdi->ssmod.csize);
        ModIndex = pxdi->pDirTab[i].modindex;
        ModName[pxdi->ssmod.csize] = '\0';
        i++;
        while (pxdi->pDirTab[i].modindex == ModIndex && i < numdir)
        {
            // point to subsection
            lseek(fh, pxdi->pDirTab[i].lfoStart + pxdi->lfaBase, SEEK_SET);
            switch (pxdi->pDirTab[i].sst)
            {
                case SSTPUBLICS:
                    bytesread = 0;
                    while (bytesread < pxdi->pDirTab[i].cb)
                    {
                        bytesread += read(fh, (void *)&pxdi->sspub.offset, sizeof(pxdi->sspub));
                        bytesread += read(fh, (void *)ename, (unsigned)pxdi->sspub.csize);
                        ename[pxdi->sspub.csize] = '\0';
                        if ((pxdi->sspub.segment == TrapSeg) &&
                            (pxdi->sspub.offset <= TrapOff) &&
                            (pxdi->sspub.offset >= NrPublic))
                        {
                            NrPublic = pxdi->sspub.offset;
                            sprintf(pxdi->szNrPub, "%s %s (%s) %04hX:%04hX\n",
                                    (pxdi->sspub.type == 1) ? " Abs" : " ", ename,
                                    ModName, // ()
                                    pxdi->sspub.segment,
                                    pxdi->sspub.offset
                                );
                        }
                    }
                    break;

                case SSTSRCLINES2:
                case SSTSRCLINES:
                    if (TrapSeg != pxdi->ssmod.csBase)
                        break;
                    namelen = 0;
                    read(fh, (void *)&namelen, 1);
                    read(fh, (void *)ename, namelen);
                    ename[namelen] = '\0';
                    // skip 2 zero bytes
                    if (pxdi->pDirTab[i].sst == SSTSRCLINES2)
                        read(fh, (void *)&numlines, 2);
                    read(fh, (void *)&numlines, 2);
                    for (j = 0; j < numlines; j++)
                    {
                        read(fh, (void *)&line, 2);
                        read(fh, (void *)&offset, 2);
                        if (offset <= TrapOff && offset >= NrLine)
                        {
                            NrLine = offset;
                            sprintf(pxdi->szNrFile, "% 12.12s ", ename);
                            sprintf(pxdi->szNrLine, "% 6hu", line);
                            /*sprintf(szNrLine,"%04hX:%04hX  line #%hu  (%s) (%s)\n",
                             * ssmod.csBase,offset,line,ModName,ename); */
                        }
                    }
                    break;
            }                   // end switch
            i++;
        }                       // end while modindex
    }                           // End While i < numdir

    free(pxdi->pDirTab);

    return 0;
}

/* ******************************************************************
 *
 *   PART 2: ANALYZE VARIABLES
 *
 ********************************************************************/

/*
 * var_value:
 *      writes a description of a variable type to
 *      the specified buffer, depending on "type".
 *
 *@@changed V0.9.1 (2000-01-30) [umoeller]: changed prototype to use external buffer
 */

STATIC VOID var_value(void *varptr,        // in: address of the variable on the stack
                      char *pszBuf,        // out: information
                      BYTE type)           // in: type; if >= 32, we'll call DosQueryMem
{
    ULONG           Size = 1,
                    Attr = 0;

    if (DosQueryMem(varptr, &Size, &Attr) != NO_ERROR)
    {
        sprintf(pszBuf, "type %d, DosQueryMem failed", type);
        return;
    }

    if ((Attr & PAG_READ) == 0)
    {
        sprintf(pszBuf, "type %d, read-access to value denied", type);
        return;
    }

    if (type == 0)
        sprintf(pszBuf, "%hd", *(signed char*)varptr);
    else if (type == 1)
        sprintf(pszBuf, "%hd", *(signed short*)varptr);
    else if (type == 2)
        sprintf(pszBuf, "%ld", *(signed long*)varptr);
    else if (type == 4)
        sprintf(pszBuf, "%hu", *(BYTE*) varptr);
    else if (type == 5)
        sprintf(pszBuf, "%hu", *(USHORT*)varptr);
    else if (type == 6)
        sprintf(pszBuf, "0x%lX (%lu)", *((ULONG*)varptr), *((ULONG*)varptr));
    else if (type == 8)
        sprintf(pszBuf, "%f", *(float*)varptr);
    else if (type == 9)
        sprintf(pszBuf, "%f", *(double*)varptr);
    else if (type == 10)
        sprintf(pszBuf, "%f", (double)(*(long double*)varptr));
    else if (type == 16)
        sprintf(pszBuf, "%s", *(char*)varptr ? "TRUE" : "FALSE");
    else if (type == 17)
        sprintf(pszBuf, "%s", *(short*)varptr ? "TRUE" : "FALSE");
    else if (type == 18)
        sprintf(pszBuf, "%s", *(long*)varptr ? "TRUE" : "FALSE");
    else if (type == 20)
        sprintf(pszBuf, "%c", *(char*)varptr);
    else if (type == 21)
        sprintf(pszBuf, "%hd", (*(short*)varptr));
    else if (type == 22)
        sprintf(pszBuf, "%ld", *(long*)varptr);
    else if (type == 23)
        sprintf(pszBuf, "void");
    else if (type >= 32)
    {
        sprintf(pszBuf, "0x%p", (void*)(*(ULONG*)varptr));
        if (Attr & PAG_FREE)
        {
            strcat(pszBuf, " unallocated memory");
        }
        else
        {
            if ((Attr & PAG_COMMIT) == 0x0U)
            {
                strcat(pszBuf, " uncommitted");
            }               // endif
            if ((Attr & PAG_WRITE) == 0x0U)
            {
                strcat(pszBuf, " unwritable");
            }               // endif
            if ((Attr & PAG_READ) == 0x0U)
            {
                strcat(pszBuf, " unreadable");
            }               // endif
        }                   // endif
    }                       // endif
    else
        sprintf(pszBuf, "Unknown type %d", type);
}

/*
 * search_userdefs:
 *      searches the table of userdef's-
 *      Return TRUE if found.
 */

STATIC BOOL search_userdefs(FILE *LogFile,     // in: text log file to write to
                            ULONG stackofs,
                            USHORT var_no)
{
    USHORT          pos;

    for (pos = 0;
         pos < userdef_count;
         pos++)
    {
        if (one_userdef[pos].idx == autovar_def[var_no].type_idx)
        {
            if (    (one_userdef[pos].type_index >= 0x80)
                // &&  (one_userdef[pos].type_index <= 0xDA)
               )
            {
                static char sszVar3[500] = "complex";
                if (one_userdef[pos].type_index <= 0xDA)
                    var_value((void*)(stackofs + autovar_def[var_no].stack_offset),
                              sszVar3,
                              one_userdef[pos].type_index - 0x80);

                fprintf(LogFile,
                        "     %- 6ld %- 20.20s %- 33.33s %s (user)\n",
                        autovar_def[var_no].stack_offset,       // stack offset
                        autovar_def[var_no].name,               // identifier
                        one_userdef[pos].name,                  // type name
                        sszVar3                                 // composed by var_value
                       );
                return TRUE;
            }
            else
                return FALSE;
        }
    }

    return FALSE;
}

/*
 * search_pointers:
 *
 */

STATIC BOOL search_pointers(FILE *LogFile,     // in: text log file to write to
                            ULONG stackofs,
                            USHORT var_no)
{
    USHORT          pos, upos;
    static BYTE     str[35];
    static char     sszVar[500];

    // BYTE            type_index;

    for (pos = 0;
         (   (pos < pointer_count)
          && (one_pointer[pos].idx != autovar_def[var_no].type_idx)
         );
         pos++);

    if (pos < pointer_count)
    {
        if (    (one_pointer[pos].type_index >= 0x80)
             && (one_pointer[pos].type_index <= 0xDA)
           )
        {
            strcpy(str, type_name[one_pointer[pos].type_index - 0x80]);
            strcat(str, " *");
            var_value((void*)(stackofs + autovar_def[var_no].stack_offset),
                      sszVar,
                      32);
            fprintf(LogFile, "     %- 6ld %- 20.20s %- 33.33s %s (ptr1)\n",
                    autovar_def[var_no].stack_offset,
                    autovar_def[var_no].name,
                    str,
                    sszVar);
            return TRUE;
        }
        else
        {
            // If the result isn't a simple type, look for it in the other lists
            for (upos = 0;
                 (   (upos < userdef_count)
                  && (one_userdef[upos].idx != one_pointer[pos].type_index)
                 );
                 upos++)
                ;

            if (upos < userdef_count)
            {
                strcpy(str, one_userdef[upos].name);
                strcat(str, " *");
                var_value((void *)(stackofs + autovar_def[var_no].stack_offset),
                          sszVar,
                          32);
                fprintf(LogFile, "     %- 6ld %- 20.20s %- 33.33s %s (ptr2)\n",
                        autovar_def[var_no].stack_offset,
                        autovar_def[var_no].name,
                        str,
                        sszVar);
                return TRUE;
            }
            else
            {
                // if it isn't a userdef, for now give up and just print
                // as much as we know
                sprintf(str, "Pointer to type 0x%X", one_pointer[pos].type_index);

                var_value((void *)(stackofs + autovar_def[var_no].stack_offset),
                          sszVar,
                          32);
                fprintf(LogFile, "     %- 6ld %- 20.20s %- 33.33s %s (ptr3)\n",
                        autovar_def[var_no].stack_offset,
                        autovar_def[var_no].name,
                        str,
                        sszVar);

                return TRUE;
            }
        }
    }

    return FALSE;
}

/*
 *@@ dbgPrintVariables:
 *      Dumps variables for the specified stack offset
 *      to the specified log file.
 *
 *      New with V0.84.
 */

void dbgPrintVariables(FILE *LogFile,   // in: text log file to write to
                       ULONG stackofs)
{
    USHORT          n; // , pos;
    BOOL            AutoVarsFound = FALSE;

    if (/* 1 || */ func_ofs == pubfunc_ofs)
    {
        for (n = 0;
             n < var_ofs;
             n++)
        {
            if (AutoVarsFound == FALSE)
            {
                AutoVarsFound = TRUE;
                fprintf(LogFile, "     List of auto variables at EBP %p in %s:\n",
                        (PVOID)stackofs,
                        func_name);
                fprintf(LogFile, "     Offset Name                 Type                              Value            \n");
                fprintf(LogFile, "     ÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\n");
            }

            // If it's one of the simple types
            if (   (autovar_def[n].type_idx >= 0x80)
                && (autovar_def[n].type_idx <= 0xDA)
                )
            {
                static char sszVar2[500];

                var_value((void *)(stackofs + autovar_def[n].stack_offset),
                          sszVar2,
                          autovar_def[n].type_idx - 0x80);

                fprintf(LogFile, "     %- 6ld %- 20.20s %- 33.33s %s (simple)\n",
                        autovar_def[n].stack_offset,
                        autovar_def[n].name,
                        type_name[autovar_def[n].type_idx - 0x80],
                        sszVar2);
            }
            else
            {                   // Complex type, check if we know what it is
                if (!search_userdefs(LogFile, stackofs, n))
                {
                    if (!search_pointers(LogFile, stackofs, n))
                    {
                        fprintf(LogFile, "     %- 6ld %-20.20s 0x%X (unknown)\n",
                                autovar_def[n].stack_offset,
                                autovar_def[n].name,
                                autovar_def[n].type_idx);
                    }
                }
            }
        }
        /* if (AutoVarsFound == FALSE)
        {
            fprintf(LogFile, "  No auto variables found in %s.\n", func_name);
        } */
        fprintf(LogFile, "\n");
    }
}

/* ******************************************************************
 *
 *   PART 3: ANALYZE SYMBOL (.SYM) FILE
 *
 ********************************************************************/

/*
 *@@ dbgPrintSYMInfo:
 *      this gets called by dbgPrintStack if dbgPrintDebugInfo
 *      failed (because no debug code was found) to check if
 *      maybe a SYM file with the same filename exists and try
 *      to get the info from there.
 *
 *      This gets called for every line of the stack
 *      walk, but only if getting the information from
 *      the debug code failed, e.g. because no debug code
 *      was available for an address.
 *
 *      The file pointer is in the "Source file" column
 *      every time this gets called.
 *
 *      New with V0.84.
 *
 *      Returns 0 if reading the SYM file was successful.
 *
 *@@changed V0.9.1 (2000-01-30) [umoeller]: added return code; this used to be VOID
 */

int dbgPrintSYMInfo(FILE *LogFile,      // in: text log file to write to
                    CHAR *SymFileName,  // in: SYM file name (can be fully q'fied)
                    ULONG Object,
                    ULONG TrapOffset)
{
    static FILE    *SymFile;
    static MAPDEF   MapDef;
    static SEGDEF   SegDef;
    static SYMDEF32 SymDef32;
    static SYMDEF16 SymDef16;
    static char     Buffer[256];
    static int      SegNum, SymNum, LastVal;
    static unsigned long int SegOffset,
                    SymOffset, SymPtrOffset;

    // open .SYM file
#ifdef DEBUG_SYMDUMP    // V1.0.0 (2002-08-21) [paperino]
    fprintf(LogFile,"Dump of '%s' for object %d\n",SymFileName,Object);
#endif
    SymFile = fopen(SymFileName, "rb");
    if (SymFile == 0)
        return 2;

    // read in first map definition
    fread(&MapDef, sizeof(MAPDEF), 1, SymFile);
#ifdef DEBUG_SYMDUMP
    Buffer[0] = MapDef.achModName[0];
    fread(&Buffer[1], 1, MapDef.cbModName-1, SymFile);
    Buffer[MapDef.cbModName] = 0x00;
    fprintf(LogFile,"Module name '%s'\n",Buffer);
#endif

    SegOffset = SEGDEFOFFSET(MapDef);
#ifdef DEBUG_SYMDUMP
    fprintf(LogFile,"SegOffset %0x\n",SegOffset);
#endif

    // go thru all segments
    for (SegNum = 0;
         SegNum < MapDef.cSegs;
         SegNum++)
    {
#ifdef DEBUG_SYMDUMP
        fprintf(LogFile,"Scanning segment #%d Offset %08X\n",SegNum,SegOffset);
#endif
        if (fseek(SymFile, SegOffset, SEEK_SET))
            // seek error
            return 3;

        // read in segment definition
        fread(&SegDef, sizeof(SEGDEF), 1, SymFile);
        Buffer[0] = SegDef.achSegName[0];
        fread(&Buffer[1], 1, SegDef.cbSegName-1, SymFile);
        Buffer[SegDef.cbSegName] = 0x00;
#ifdef DEBUG_SYMDUMP
        fprintf(LogFile,"Segment name '%s', number %d, flags %02x\n",Buffer,SegNum,SegDef.bFlags);
#endif

        if (SegNum == Object)
        {
            // stack object found:
            Buffer[0] = 0x00;
            LastVal = 0;

            // go thru all symbols in this object
#ifdef DEBUG_SYMDUMP
            fprintf(LogFile,"Scanning #%d symbols\n",SegDef.cSymbols);
#endif
            for (SymNum = 0; SymNum < SegDef.cSymbols; SymNum++)
            {
                // fixed syms > 64 K V1.0.0 (2002-08-21) [paperino]
                if (SegDef.bFlags & 0x01)
                {
                    // 32-bit symbol:
                    fread(&SymDef32, sizeof(SYMDEF32), 1, SymFile);
                    if (SymDef32.wSymVal > TrapOffset)
                    {
                        // symbol found
                        fprintf(LogFile,
                                "between %s + 0x%lX ",
                                Buffer,
                                TrapOffset - LastVal);
                        /* fprintf(LogFile, "(ppLineDef: 0x%lX) ",
                                    LINEDEFOFFSET(SegDef)
                                    ); */
                        fprintf(LogFile, "\n");
                    }

                    LastVal = SymDef32.wSymVal;
                    Buffer[0] = SymDef32.achSymName[0];
                    fread(&Buffer[1], 1, SymDef32.cbSymName-1, SymFile);
                    Buffer[SymDef32.cbSymName] = 0x00;
#ifdef DEBUG_SYMDUMP
                    fprintf(LogFile,"32 Bit Symbol Address %08p <%s> \n",SymDef32.wSymVal,Buffer);
#endif

                    if (SymDef32.wSymVal > TrapOffset)
                    {
                        // symbol found, as above
                        fprintf(LogFile,
                                "                                     "
                                "and %s - 0x%lX ",
                                Buffer,
                                LastVal - TrapOffset);
                        fprintf(LogFile, "\n");
                        break;
                    }
                }
                else
                {
                    // 16-bit symbol:
                    fread(&SymDef16, sizeof(SYMDEF16), 1, SymFile);
                    if (SymDef16.wSymVal > TrapOffset)
                    {
                        fprintf(LogFile,
                                "between %s + %lX\n",
                                Buffer,
                                TrapOffset - LastVal);
                    }
                    LastVal = SymDef16.wSymVal;
                    Buffer[0] = SymDef16.achSymName[0];
                    fread(&Buffer[1], 1, SymDef16.cbSymName-1, SymFile);
                    Buffer[SymDef16.cbSymName] = 0x00;
                    if (SymDef16.wSymVal > TrapOffset)
                    {
                        fprintf(LogFile,
                                "                                     "
                                "and %s - %lX\n",
                                Buffer,
                                LastVal - TrapOffset);
                        break;
                    }
#ifdef DEBUG_SYMDUMP
                    fprintf(LogFile,"16 Bit Symbol <%s> Address %p\n",Buffer,SymDef16.wSymVal);
#endif
                }               // endif
            }
            break;
        }                       // endif
        SegOffset = NEXTSEGDEFOFFSET(SegDef);
    }                           // endwhile

    fclose(SymFile);

    return 0;         // no error
}

/* ******************************************************************
 *
 *   PART 4: dbgPrintStack
 *
 ********************************************************************/

/*
 *@@ dbgPrintStackFrame:
 *      parses and dumps one stack frame.
 *      Called from excPrintStackFrame.
 *
 *      This calls dbgPrintDebugInfo and, if
 *      that fails, dbgPrintSYMInfo.
 *
 *@@added V0.9.2 (2000-03-10) [umoeller]
 *@@changed V0.9.3 (2000-04-10) [umoeller]: added support for non-Warp 4 SYM files
 *@@changed V0.9.3 (2000-04-26) [umoeller]: this broke Warp 4 FP 13, fixed
 *@@changed V1.0.12 (2017-01-15) [rwalsh]: improve handling for Warp 4.5 SYM files
 */

BOOL dbgPrintStackFrame(FILE *LogFile,
                        PSZ pszModuleName,  // in: module name (fully q'fied)
                        ULONG ulObject,
                        ULONG ulOffset)
{
    APIRET  arc = 0;
    PSZ     pszFilename;
    PSZ     pszVersionDir;
    ULONG   aulBuf[3];
    CHAR    szSymName[CCHMAXPATH];
    CHAR    szSymPath[CCHMAXPATH];

    // look for .sym in the module's directory
    strcpy(szSymName, pszModuleName);
    strcpy(szSymName + strlen(szSymName) - 3, "SYM");
    if (!dbgPrintSYMInfo(LogFile, szSymName, ulObject, ulOffset))
        return TRUE;

    pszFilename = strrchr(szSymName, '\\');
    if (!pszFilename)
    {
        fprintf(LogFile, "Cannot find symbol file for %s\n", pszModuleName);
        return FALSE;
    }
    pszFilename++;

    // check the SYM files in the \OS2 directory,
    // depending on the OS/2 version level:
    DosQuerySysInfo(QSV_VERSION_MAJOR, QSV_VERSION_MINOR,
                    &aulBuf, sizeof(aulBuf));

    // Warp 3 is reported as 20.30
    // Warp 4 is reported as 20.40
    // WSEB   is reported as 20.45
    // (NOTE: Warp 4 FP 13 now returns 45 also,
    // but the SYM files are still in the WARP4 directory)

    // try the directories used by the "real" Warp 4.5 first
    if (aulBuf[1] == 45)
    {
        sprintf(szSymPath, "%c:\\OS2\\PDPSI\\PMDF\\%s\\%s",
                doshQueryBootDrive(), "WARP45", pszFilename);
        if (!dbgPrintSYMInfo(LogFile, szSymPath, ulObject, ulOffset))
            return TRUE;

        // use the nbr of processors to decide which directory to search
        if (DosQuerySysInfo(QSV_NUMPROCESSORS, QSV_NUMPROCESSORS,
                            &aulBuf[2], sizeof(aulBuf[2]))
            || aulBuf[2] == 1)
            pszVersionDir = "WARP45_U";
        else
            pszVersionDir = "WARP45_S";

        sprintf(szSymPath, "%c:\\OS2\\PDPSI\\PMDF\\%s\\%s",
                doshQueryBootDrive(), pszVersionDir, pszFilename);
        if (!dbgPrintSYMInfo(LogFile, szSymPath, ulObject, ulOffset))
            return TRUE;
    }

    // deal with Warp 3 and Warp4 pre- and post-FP13
    if (aulBuf[1] == 30)
        pszVersionDir = "WARP3";
    else
        pszVersionDir = "WARP4";

    sprintf(szSymPath, "%c:\\OS2\\PDPSI\\PMDF\\%s\\%s",
            doshQueryBootDrive(), pszVersionDir, pszFilename);
    arc = dbgPrintSYMInfo(LogFile, szSymPath, ulObject, ulOffset);

    if (arc == 2)
        fprintf(LogFile, "Cannot find symbol file %s\n",
                szSymName);
    else if (arc != 0)
        fprintf(LogFile, "Error %lu reading symbol file %s\n",
                arc, szSymName);

    return (arc == NO_ERROR);
}

/*
 *@@ dbgPrintStack:
 *      this takes stack data from the TIB and
 *      context record data structures and tries
 *      to analyse what the different stack frames
 *      point to.
 *
 *      For each stack frame, this calls dbgPrintDebugInfo,
 *      and, if that fails, dbgPrintSYMInfo.
 *
 *      New with V0.84.
 *
 *@@changed V0.9.2 (2000-03-08) [umoeller]: now searching OS2\PDPSI\PMDF for SYM files also
 *@@changed XWP V1.0.9 (2010-04-16) [pr]: fixed module name display bugs
 */

VOID dbgPrintStack(FILE *LogFile,           // in: text log file to write to
                   PUSHORT StackBottom,
                   PUSHORT StackTop,
                   PUSHORT Ebp,
                   PUSHORT ExceptionAddress)
{
    PUSHORT         RetAddr = 0;
    PUSHORT         LastEbp = 0;
    APIRET          rc = 0;
    ULONG           Size = 0,
                    Attr = 0;
    USHORT          Cs = 0,
                    Ip = 0,
                    // Bp,
                    Sp = 0;
    char            Name[CCHMAXPATH];
    HMODULE         hMod = 0;
    ULONG           ObjNum = 0;
    ULONG           Offset = 0;
    BOOL            fExceptionAddress = TRUE;   // Use Exception Addr 1st time thru

    // Note: we can't handle stacks bigger than 64K for now...
    Sp = (USHORT) (((ULONG) StackBottom) >> 16);
    // Bp = ;

    if (!f32bit)
        Ebp = (PUSHORT) MAKEULONG(((USHORT)(ULONG)Ebp), Sp);

    fprintf(LogFile, "\n\nCall stack:\n");
    fprintf(LogFile, "                                        Source    Line      Nearest\n");
    fprintf(LogFile, "   EBP      Address    Module  Obj#      File     Numbr  Public Symbol\n");
    fprintf(LogFile, " ÄÄÄÄÄÄÄÄ  ÄÄÄÄÄÄÄÄ-  ÄÄÄÄÄÄÄÄ ÄÄÄÄ  ÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄÄÄÄ  ÄÄÄÄÄÄÄÄÄÄÄÄ-\n");

    do
    {
        Size = 10;
        rc = DosQueryMem((PVOID) (Ebp + 2), &Size, &Attr);
        if (rc != NO_ERROR)
        {
            fprintf(LogFile, "Invalid EBP %8.8lX (DosQueryMem returned %lu)\n", (ULONG)Ebp, rc);
            break;
        }
        if (!(Attr & PAG_COMMIT))
        {
            fprintf(LogFile, "Invalid EBP %8.8lX (not committed)\n", (ULONG)Ebp);
            break;
        }
        if (Size < 10)
        {
            fprintf(LogFile, "Invalid EBP %8.8lX (mem block size < 10)\n", (ULONG)Ebp);
            break;
        }

        if (fExceptionAddress)
            RetAddr = ExceptionAddress;
        else
            RetAddr = (PUSHORT) (*((PULONG) (Ebp + 2)));

        if (RetAddr == (PUSHORT) 0x00000053)
        {
            // For some reason there's a "return address" of 0x53 following
            // EBP on the stack and we have to adjust EBP by 44 bytes to get
            // at the real return address.  This has something to do with
            // thunking from 32bits to 16bits...
            // Serious kludge, and it's probably dependent on versions of C(++)
            // runtime or OS, but it works for now!
            Ebp += 22;
            RetAddr = (PUSHORT) (*((PULONG) (Ebp + 2)));
        }

        // Get the (possibly) 16bit CS and IP
        if (fExceptionAddress)
        {
            Cs = (USHORT) (((ULONG) ExceptionAddress) >> 16);
            Ip = (USHORT) (ULONG) ExceptionAddress;
        }
        else
        {
            Cs = *(Ebp + 2);
            Ip = *(Ebp + 1);
        }

        // if the return address points to the stack then it's really just
        // a pointer to the return address (UGH!).
        if (    (USHORT) (((ULONG) RetAddr) >> 16) == Sp
           )
            RetAddr = (PUSHORT) (*((PULONG) RetAddr));

        if (Ip == 0 && *Ebp == 0)
        {
            // End of the stack so these are both shifted by 2 bytes:
            Cs = *(Ebp + 3);
            Ip = *(Ebp + 2);
        }

        // 16bit programs have on the stack:
        //   BP:IP:CS
        //   where CS may be thunked
        //
        //         in dump                 swapped
        //    BP        IP   CS          BP   CS   IP
        //   4677      53B5 F7D0        7746 D0F7 B553
        //
        // 32bit programs have:
        //   EBP:EIP
        // and you'd have something like this (with SP added) (not
        // accurate values)
        //
        //         in dump               swapped
        //      EBP       EIP         EBP       EIP
        //   4677 2900 53B5 F7D0   0029 7746 D0F7 B553
        //
        // So the basic difference is that 32bit programs have a 32bit
        // EBP and we can attempt to determine whether we have a 32bit
        // EBP by checking to see if its 'selector' is the same as SP.
        // Note that this technique limits us to checking stacks < 64K.
        //
        // Soooo, if IP (which maps into the same USHORT as the swapped
        // stack page in EBP) doesn't point to the stack (i.e. it could
        // be a 16bit IP) then see if CS is valid (as is or thunked).
        //
        // Note that there's the possibility of a 16bit return address
        // that has an offset that's the same as SP so we'll think it's
        // a 32bit return address and won't be able to successfully resolve
        // its details.
        if (Ip != Sp)
        {
            if (DOS16SIZESEG(Cs, &Size) == NO_ERROR)
            {
                RetAddr = (USHORT * _Seg16) MAKEULONG(Ip, Cs);
                f32bit = FALSE;
            }
            else if (DOS16SIZESEG((Cs << 3) + 7, &Size) == NO_ERROR)
            {
                Cs = (Cs << 3) + 7;
                RetAddr = (USHORT * _Seg16) MAKEULONG(Ip, Cs);
                f32bit = FALSE;
            }
            else
                f32bit = TRUE;
        }
        else
            f32bit = TRUE;


        // "EBP" column
        if (fExceptionAddress)
            fprintf(LogFile, " Trap  ->  ");
        else
            fprintf(LogFile, " %8.8lX  ", (ULONG)Ebp);

        // "Address" column
        if (f32bit)
            fprintf(LogFile, ":%8.8lX  ", (ULONG)RetAddr);
        else
            fprintf(LogFile, "%04.04X:%04.04X  ", Cs, Ip);

        // Version check omitted; the following requires
        // OS/2 2.10 or later (*UM)
        // if (Version[0] >= 20 && Version[1] >= 10)
        {
            // Make a 'tick' sound to let the user know we're still alive
            DosBeep(2000, 10);

            Size = 10;    // Inserted by Kim Rasmussen 26/06 1996 to avoid error 87 when Size is 0

            // "Module"/"Object" columns
            rc = DosQueryMem((PVOID) RetAddr, &Size, &Attr);
            if (rc != NO_ERROR || !(Attr & PAG_COMMIT))
            {
                fprintf(LogFile, "Invalid RetAddr: %8.8lX\n", (ULONG)RetAddr);
                break;          // avoid infinite loops
            }
            else
            {
                rc = DOSQUERYMODFROMEIP(&hMod,
                                        &ObjNum,
                                        sizeof(Name), Name,
                                        &Offset,
                                        (PVOID)RetAddr);
                if (    (rc == NO_ERROR)
                     // && (ObjNum != -1)
                   )
                {
                    DosQueryModuleName(hMod, sizeof(Name), Name);
                    // print module and object
                    fprintf(LogFile, "%-8s %04lX  ", Name, ObjNum + 1);

                    if (strlen(Name) > 3)
                    {
                        // look for embedded debug info;
                        // if that fails, look for a .sym file
                        if (dbgPrintDebugInfo(LogFile,
                                              Name,
                                              ObjNum,
                                              Offset))
                            dbgPrintStackFrame(LogFile,
                                               Name,
                                               ObjNum,
                                               Offset);
                    }
                }
                else
                    fprintf(LogFile,
                            "DosQueryModFromEIP failed, returned %lu\n",
                            rc);
            }
        }

        if (    ((*Ebp) == 0)
             && ((*Ebp + 1) == 0)
           )
        {
            fprintf(LogFile, "End of call stack\n");
            break;
        }

        if (!fExceptionAddress)
        {
            LastEbp = Ebp;
#if 0
            Ebp = (PUSHORT) MAKEULONG(Bp, Sp);
#else // Inserted by Kim Rasmussen 26/06 1996 to allow big stacks
            if (f32bit)
                Ebp = (PUSHORT) *(PULONG) LastEbp;
            else
                Ebp = (PUSHORT) MAKEULONG((*Ebp), Sp);
#endif
            if (f32bit)
            {
                dbgPrintVariables(LogFile, (ULONG) Ebp);
            }                   // endif

            if (Ebp < LastEbp)
            {
                fprintf(LogFile, "... lost stack chain - new EBP below previous\n");
                break;
            }
        }
        else
            fExceptionAddress = FALSE;

        Size = 4;
        rc = DosQueryMem((PVOID) Ebp, &Size, &Attr);
        if ((rc != NO_ERROR) || (Size < 4))
        {
            fprintf(LogFile, "... lost stack chain - invalid EBP: %8.8lX\n", (ULONG)Ebp);
            break;
        }
    } while (TRUE);

    fprintf(LogFile, "\n");
}

