home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / DEF.ZIP / DEF.C < prev    next >
C/C++ Source or Header  |  1989-11-24  |  21KB  |  610 lines

  1. /*****************************************************************************
  2.  
  3.    NAME:          DEF.C
  4.  
  5.    DESCRIPTION:   This module will re-create a module definition file (DEF)
  6.                   for the indicated EXE file.
  7.  
  8.                   Output is sent to STDOUT and may be redirected.
  9.  
  10.                   Still To Do:
  11.  
  12.                      - go out to the entry table for STACKPARAMS on
  13.                        IOPL segments
  14.  
  15.                      - better error checking
  16.  
  17.                      - cleanup
  18.  
  19.    ASSUMPTIONS:   1. Program doesn't import more than 1000 items
  20.                   2. The program is in a valid format
  21.                   3. The program was built with at least 1.1 SDK
  22.                   4. The internal name for a given import is the same
  23.                      as that used by the exporter.
  24.  
  25.    WARNINGS:      This program is fresh out of the oven. I have not done
  26.                   exhaustive testing with it...
  27.  
  28.                   If you find any bugs or know of a better way to do
  29.                   something, I'd appreciate hearing from you.
  30.                   
  31.                   My testing consisted of...
  32.  
  33.                      FOR %1 in (\os2\*.exe)     DO DEF %1
  34.                      FOR %1 in (\os2\dll*.dll)  DO DEF %1
  35.                      FOR %1 in (\os2\*.sys)     DO DEF %1
  36.  
  37.                   ...none of which generated GP faults anyway.
  38.  
  39.                   This program *definitely* needs to be upgraded to 1.2
  40.                   but this is a four day weekend and the building is locked.
  41.  
  42.    REFERENCES:    Advanced OS/2 Programming - Ray Duncan
  43.                   PCMagnet - Charles Petzold
  44.  
  45.    AUTHOR:        Charlie Schmitt
  46.                   11/24/89
  47.                   PCMagnet ID 70022, 773
  48.  
  49. *****************************************************************************/
  50.  
  51. /*****************************************************************************
  52.                       I N C L U D E   F I L E S
  53. *****************************************************************************/
  54.  
  55. #define  INCL_DOS
  56. #define  INCL_WIN
  57.  
  58. #include <os2.h>
  59. #include <stdio.h>
  60. #include <string.h>
  61. #include "def.h"
  62. #include "dosexprt.h"
  63.  
  64. /*****************************************************************************
  65.                         D E F I N I T I O N S
  66. *****************************************************************************/
  67.  
  68. #define  LIBRARY  0x8000
  69.  
  70. #define  IsLibrary()    (new_exe.flags & LIBRARY)
  71.  
  72. /*****************************************************************************
  73.                         G L O B A L   D A T A
  74. *****************************************************************************/
  75.  
  76. HAB            hab;
  77. NEW_EXE        new_exe;                /* new exe header */
  78. OLD_EXE        old_exe;                /* old exe header */
  79. ULONG          ulFilePtr;              /* current file position */
  80. USHORT         cbBytes;                /* number of bytes moved */
  81. USHORT         rc;                     /* return code */
  82. HFILE          hf;                     /* file handle */
  83. USHORT         usAction;               /* UNUSED, required for DosOpen */
  84. CHAR           *file=__FILE__;         /* for use with error_exit() */
  85.  
  86. SEL            selImptab;              /* imported names table selector */
  87. CHAR           *imptab;                /* imported names table ptr */
  88. USHORT         imptab_size;            /* size of imported names table */
  89. ULONG          imptab_offset;          /* location of imported names table */
  90.  
  91. SEL            selRestab;              /* resident names table selector */
  92. CHAR           *restab;                /* resident names table ptr */
  93. USHORT         restab_size;            /* size of resident names table */
  94. ULONG          restab_offset;          /* location of resident names table */
  95.  
  96. SEL            selSegtab;              /* segment table selector */
  97. CHAR           *segtab;                /* segment table ptr */
  98. USHORT         segtab_size;            /* size of segment table */
  99. ULONG          segtab_offset;          /* location of segment table */
  100.  
  101. SEL            selNRestab;             /* non-resident names table selector */
  102. CHAR           *nrestab;               /* non-resident names table ptr */
  103. USHORT         nrestab_size;           /* size of non-resident names table */
  104. ULONG          nrestab_offset;         /* loc. of non-resident names table */
  105.  
  106. SEL            selModtab;              /* module reference table selector */
  107. USHORT         *modtab;                /* module reference table ptr */
  108. USHORT         modtab_size;            /* size of module reference table */
  109. ULONG          modtab_offset;          /* loc. of module reference table */
  110.  
  111. SEL            selReloctab;            /* relocation table selector */
  112. PIMP_ORD       reloctab;               /* relocation table pointer */
  113. USHORT         reloctab_size;          /* size of relocation table */
  114. ULONG          reloctab_offset;        /* loc. of relocation table */
  115.  
  116. CHAR           szName[256];            /* generic name */
  117. CHAR           szComment[256];         /* import table comment */
  118. CHAR           *p;                     /* work pointer */
  119. CHAR           szTmp[256];             /* work buffer */
  120. CHAR           szAtomName[256];        /* atom name string */
  121. USHORT         i,j,k,l;                /* work var */
  122. PNEW_SEG       pseg_entry;             /* ptr to a segment table entry */
  123. USHORT         flag;                   /* another work var. */
  124. PUSHORT        iptr;                   /* pointer to an int */
  125. HATOMTBL       hAtomTbl;               /* atom table handle */
  126. ATOM           atom, atomDOS;
  127.  
  128. /*****************************************************************************
  129.                                 main()
  130. *****************************************************************************/
  131.  
  132. void main(USHORT argc, CHAR *argv[])
  133. {
  134.    USHORT code_segs, data_segs;
  135.    USHORT num_relocs;
  136.    ULONG  ulLong;
  137.    USHORT usAppType;
  138.  
  139.    if (argc != 2)
  140.       {
  141.       fprintf(stderr, "Usage: def <fspec>\n");
  142.       DosExit(EXIT_PROCESS, 0);
  143.       }
  144.  
  145.    /***** SETCOM40 brought us here !! *****/
  146.  
  147.    if (rc=DosQAppType(argv[1], &usAppType))
  148.       error_exit(rc, "DosQAppType", __LINE__, file);
  149.  
  150.    if (usAppType == 32)
  151.       {
  152.       fprintf(stderr, "Specified file is not valid EXE\n");
  153.       DosExit(EXIT_PROCESS, 0);
  154.       }
  155.  
  156.    /***** Open the specified executable *****/
  157.  
  158.    if (rc=DosOpen(argv[1], &hf, &usAction, 0L, FILE_READONLY, FILE_OPEN,
  159.          OPEN_SHARE_DENYWRITE, 0L))
  160.       error_exit(rc, "DosOpen", __LINE__, file);
  161.  
  162.    /***** Read in the old exe header information *****/
  163.  
  164.    if (rc=DosRead(hf, &old_exe, sizeof(old_exe), &cbBytes))
  165.       error_exit(rc, "DosRead", __LINE__, file);
  166.  
  167.    /***** Perform some crude validation *****/
  168.  
  169.    if ( (old_exe.magic != 0x5a4d) || (old_exe.lfanew == 0L) )
  170.       {
  171.       fprintf(stderr, "Specified file is not valid EXE\n");
  172.       DosExit(EXIT_PROCESS, 0);
  173.       }
  174.  
  175.    /***** Isolate the new exe header information *****/
  176.  
  177.    if (rc=DosChgFilePtr(hf, old_exe.lfanew, FILE_BEGIN, &ulFilePtr))
  178.       error_exit(rc, "DosChgFilePtr", __LINE__, file);
  179.  
  180.    /***** Read in the new exe header *****/
  181.  
  182.    if (rc=DosRead(hf, &new_exe, sizeof(new_exe), &cbBytes))
  183.       error_exit(rc, "DosRead", __LINE__, file);
  184.  
  185.    /***** Perform some crude validation *****
  186.  
  187.    if (new_exe.magic != 0x454e)
  188.       {
  189.       fprintf(stderr, "Specified file is not valid EXE\n");
  190.       DosExit(EXIT_PROCESS, 0);
  191.       }
  192.  
  193.    /***** Allocate and read in the Imported Names Table *****/
  194.  
  195.    CreateTable(hf, imptab_size = new_exe.enttab - new_exe.imptab,
  196.                imptab_offset = old_exe.lfanew + (long)new_exe.imptab,
  197.                &imptab, &selImptab);
  198.  
  199.    /***** Allocate and read in the Resident Name Table *****/
  200.  
  201.    CreateTable(hf, restab_size = new_exe.modtab - new_exe.restab,
  202.                restab_offset = old_exe.lfanew + (long)new_exe.restab,
  203.                &restab, &selRestab);
  204.  
  205.    /***** Allocate and read in the Non-Resident Names Table *****/
  206.  
  207.    CreateTable(hf, nrestab_size = new_exe.cbnrestab,
  208.                nrestab_offset = new_exe.nrestab,
  209.                &nrestab, &selNRestab);
  210.  
  211.    /***** Allocate and read in the Segment Table *****/
  212.  
  213.    CreateTable(hf, segtab_size = new_exe.rsrctab - new_exe.segtab,
  214.                segtab_offset = old_exe.lfanew + (long)new_exe.segtab,
  215.                &segtab, &selSegtab);
  216.  
  217.    /***** Allocate and read in the Module Reference Table *****/
  218.  
  219.    CreateTable(hf, modtab_size = new_exe.imptab - new_exe.modtab,
  220.                modtab_offset = old_exe.lfanew + (long)new_exe.modtab,
  221.                (CHAR **)&modtab, &selModtab);
  222.  
  223.    /***** Get the module name *****/
  224.  
  225.    /***** SETCOM40.EXE reports 0 here (?) *****/
  226.  
  227.    if (restab_size)
  228.       getstring(restab, 0, szName);
  229.    else
  230.       strcpy(szName, "NONAME");
  231.  
  232.    printf("; %s.DEF\n\n", szName);
  233.  
  234.    /***** Output appropriate first line *****/
  235.  
  236.    if (IsLibrary())
  237.       {
  238.       p = (new_exe.flags & 0x0004) ? "INITINSTANCE" : "INITGLOBAL";
  239.       printf("%-14s %-14s %s\n", "LIBRARY", szName, p);
  240.       }
  241.    else
  242.       {
  243.       p = "UKNOWN";
  244.       if (new_exe.flags & 0x0700)
  245.          {
  246.          i = (new_exe.flags >> 8) & 0x03;
  247.          if (i == 3)
  248.             p = "WINDOWAPI";
  249.          else if (i == 2)
  250.             p = "WINDOWCOMPAT";
  251.          else
  252.             p = "NOTWINDOWCOMPAT";
  253.          }
  254.       printf("%-14s %-14s %s\n", "NAME", szName, p);
  255.       }
  256.  
  257.    /***** Now let's get the Description *****/
  258.  
  259.    if (nrestab_size)
  260.       getstring(nrestab, 0, szName);
  261.    else
  262.       strcpy(szName, "NO DESCRIPTION");
  263.    printf("%-14s '%s'\n", "DESCRIPTION", szName);
  264.  
  265.    /***** Output appropriate mode *****/
  266.    /***** NOTE: REALMODE really means both... *****/
  267.  
  268.    p = (new_exe.flags & 0x0008) ? "PROTMODE" : "REALMODE";
  269.    printf("%s\n", p);
  270.  
  271.    /***** Output DATA *****/
  272.  
  273.    flag = new_exe.flags & 0x0003;
  274.    if (flag == 0)
  275.       p = "NONE";
  276.    else if (flag == 1)
  277.       p = "SINGLE";
  278.    else if (flag == 2)
  279.       p = "MULTIPLE";
  280.    printf("%-14s %s\n", "DATA", p);
  281.  
  282.    /***** Output STACKSIZE *****/
  283.    
  284.    printf("%-14s %u\n", "STACKSIZE", new_exe.stack);
  285.  
  286.    /***** Output HEAPSIZE *****/
  287.    
  288.    printf("%-14s %u\n", "HEAPSIZE", new_exe.heap);
  289.  
  290.    /***** Output Operating System Type *****/
  291.  
  292.    if (new_exe.exetyp == 1)
  293.       p = "OS2";
  294.    else if (new_exe.exetyp == 2)
  295.       p = "WINDOWS";
  296.    else
  297.       p = "MSDOS4";
  298.    printf("%-14s %s\n", "EXETYPE", p);
  299.  
  300.    /***** It's time for the Segments *****/
  301.  
  302.    printf("\nSEGMENTS\n");
  303.  
  304.    code_segs = data_segs = 0;          /* init counters */
  305.    for (i=0; i < (segtab_size / sizeof(NEW_SEG)); i++)
  306.       {
  307.       pseg_entry = getsegment((PNEW_SEG)segtab, i);
  308.       flag = pseg_entry->flags;
  309.       if (flag & 0x0001)               /* DATA */
  310.          {
  311.          if (data_segs == 0)
  312.             strcpy(szName, "_DATA");
  313.          else
  314.             sprintf(szName, "_DATA%u", data_segs);
  315.          printf("\t%-8s CLASS 'DATA' ", szName);
  316.          strcpy(szName, (flag & 0x0010) ? " MOVEABLE" : " FIXED");
  317.          strcat(szName, (flag & 0x0020) ? " SHARED" : " NONSHARED");
  318.          strcat(szName, (flag & 0x0040) ?  " PRELOAD" : " LOADONCALL");
  319.          strcat(szName, (flag & 0x0080) ? " READONLY" : " READWRITE");
  320.          strcat(szName, !(flag & 0x0800) ? " IOPL" : " NOIOPL");
  321.          strcat(szName, (flag & 0x1000) ? " DISCARDABLE" : " NONDISCARDABLE");
  322.          data_segs++;
  323.          }
  324.       else                             /* CODE */
  325.          {
  326.          if (code_segs == 0)
  327.             strcpy(szName, "_TEXT");
  328.          else
  329.             sprintf(szName, "_TEXT%u", code_segs);
  330.          printf("\t%-8s CLASS 'CODE' ", szName);
  331.          strcpy(szName, (flag & 0x0010) ? " MOVEABLE" : " FIXED");
  332.          strcat(szName, (flag & 0x0020) ? " PURE" : " IMPURE");
  333.          strcat(szName, (flag & 0x0040) ?  " PRELOAD" : " LOADONCALL");
  334.          strcat(szName, (flag & 0x0080) ? " EXECUTEONLY" : " EXECUTEREAD");
  335.          strcat(szName, (flag & 0x0200) ? " CONFORMING" : " NONCONFORMING");
  336.          strcat(szName, !(flag & 0x0800) ? " IOPL" : " NOIOPL");
  337.          strcat(szName, (flag & 0x1000) ? " DISCARDABLE" : " NONDISCARDABLE");
  338.          code_segs++;
  339.          }
  340.  
  341.       /***** Kind of a Kludge *****/
  342.  
  343.       if (strlen(szName) > 49)
  344.          {
  345.          for (j=49; j; j--)
  346.             if (szName[j] == ' ')
  347.                break;
  348.          szName[j] = '\0';
  349.          printf("%s\n", szName);
  350.          printf("%30s %s\n", " ", &szName[j+1]);
  351.          }
  352.       else
  353.          printf("%s\n", szName);
  354.       }
  355.  
  356.    /***** It's time for the Exports *****/
  357.  
  358.    printf("\nEXPORTS\n");
  359.  
  360.    /***** First let's do the Resident Names Table *****/
  361.  
  362.    p = restab;
  363.    p += *p + 3;                        /* bypass the first entry */
  364.    while (p < restab + restab_size - 1)
  365.       {
  366.       getstring(p, 0, szName);         /* grab entry name */
  367.       p += *p + 1;                     /* bypass the length */
  368.       iptr = (USHORT *)p;              /* grab the ordinal */
  369.       printf("\t%-30s @%-3u    RESIDENTNAME\n", szName, *iptr);      
  370.       p += 2;                          /* move on to next table entry */
  371.       }
  372.  
  373.    /***** Now the Non-Resident Names Table *****/
  374.  
  375.    p = nrestab;
  376.    p += *p + 3;                        /* bypass the first entry */
  377.    while (p < nrestab + nrestab_size - 1)
  378.       {
  379.       getstring(p, 0, szName);         /* grab entry name */
  380.       p += *p + 1;                     /* bypass the length */
  381.       iptr = (USHORT *)p;              /* grab the ordinal */
  382.       printf("\t%-30s @%u\n", szName, *iptr);      
  383.       p += 2;                          /* move on to next table entry */
  384.       }
  385.  
  386.   /////////////////// NEED STACK PARAMS ///////////////////////////
  387.  
  388.    /***** It's time for the Imports *****/
  389.  
  390.    /***** The atom table will prevent dups *****/
  391.  
  392.    hAtomTbl = WinCreateAtomTable(0, 1000);
  393.    if (hAtomTbl == NULL)
  394.       error_exit(0, "WinCreateAtomTable", __LINE__, file);
  395.  
  396.    /***** Build string lookup tables *****/
  397.  
  398.    BuildImportedInfoTable();
  399.  
  400.    /***** Add DOSCALLS for DosCallsTbl[] lookup *****/
  401.  
  402.    if (!(atomDOS = WinAddAtom(hAtomTbl, "DOSCALLS")))
  403.       error_exit(0, "WinAddAtom", __LINE__, file);
  404.  
  405.    printf("\nIMPORTS\n");
  406.  
  407.    for (i=0; i < (segtab_size / sizeof(NEW_SEG)); i++)
  408.       {
  409.       pseg_entry = getsegment((PNEW_SEG)segtab, i);
  410.       if (pseg_entry->cbseg == 0)
  411.          continue;                     /* nothing to parse */
  412.  
  413.       if (pseg_entry->flags & 0x0001)
  414.          continue;                     /* data */
  415.  
  416.       if (!(pseg_entry->flags & 0x0100))
  417.          continue;                     /* no reloc information */
  418.  
  419.      /***** Isolate the segment's reloc information *****/
  420.  
  421.       ulLong = pseg_entry->sector * (ULONG)(1 << new_exe.calign);
  422.       if (rc=DosChgFilePtr(hf, ulLong + pseg_entry->cbseg,
  423.             FILE_BEGIN, &ulFilePtr))
  424.          error_exit(rc, "DosChgFilePtr", __LINE__, file);
  425.  
  426.       /***** Read in the number of entries in the table *****/
  427.  
  428.       if (rc=DosRead(hf, &num_relocs, sizeof(num_relocs), &cbBytes))
  429.          error_exit(rc, "DosRead", __LINE__, file);
  430.  
  431.       if (num_relocs == 0)
  432.          continue;                     /* double check */
  433.  
  434.       if (rc=DosAllocSeg(cbBytes = num_relocs * sizeof(IMP_ORD),
  435.                &selReloctab, 0))
  436.          error_exit(rc, "DosAllocSeg", __LINE__, file);
  437.  
  438.       reloctab = MAKEP(selReloctab, 0);
  439.  
  440.       if (rc=DosRead(hf, reloctab, cbBytes, &cbBytes))
  441.          error_exit(rc, "DosRead", __LINE__, file);
  442.  
  443.       /***** Step down the relocation table *****/
  444.  
  445.       for (j=0; j<num_relocs; j++, reloctab++)
  446.          {
  447.          if (reloctab->id == 0 || reloctab->id == 4)
  448.             continue;
  449.          l = reloctab->index;
  450.          k = modtab[l-1];
  451.          getstring(imptab + k, 0, szName);
  452.  
  453.          /***** Is the DLL DOSCALLS ? *****/
  454.  
  455.          p = "UNKNOWN";                       
  456.          if (atomDOS == WinFindAtom(hAtomTbl, szName))
  457.             {
  458.             if (reloctab->ordinal <= DOS_TABLE_ENTRIES)
  459.                p = DosCallsTbl[reloctab->ordinal].name;
  460.             }
  461.          else
  462.             {
  463.             p = TableLookup(szName, reloctab->ordinal);
  464.             }
  465.  
  466.          /***** Create the import string *****/
  467.  
  468.          if (reloctab->id == 1 || reloctab->id == 5)
  469.             {
  470.             sprintf(szAtomName, "%-29s= %s.%u", p, szName, reloctab->ordinal);
  471.             }
  472.          else
  473.             {
  474.             getstring(imptab + reloctab->ordinal, 0, szTmp);
  475.             sprintf(szAtomName, "%s.%s", szName, szTmp);
  476.             }
  477.  
  478.          /***** Only output import if original *****/
  479.  
  480.          if (!WinFindAtom(hAtomTbl, szAtomName))
  481.             {
  482.             printf("\t%s\n", szAtomName);
  483.             if (!WinAddAtom(hAtomTbl, szAtomName))
  484.                error_exit(0, "WinAddAtom", __LINE__, file);
  485.             }
  486.          }
  487.  
  488.       /***** DOne with this buffer, go get another *****/
  489.  
  490.       if (rc=DosFreeSeg(selReloctab))
  491.          error_exit(rc, "DosFreeSeg", __LINE__, file);
  492.       }
  493.  
  494.    /***** Having a problem with this call *****/
  495.  
  496. //   if (WinDestroyAtomTable(hAtomTbl))
  497. //      error_exit(0, "WinDestroyAtomTable", __LINE__, file);
  498.  
  499.    /***** Cleanup *****/
  500.  
  501.    /***** Free string lookup tables *****/
  502.  
  503.    FreeImportedInfoTable();
  504.  
  505.    if (selModtab)
  506.       if (rc=DosFreeSeg(selModtab))
  507.          error_exit(rc, "DosFreeSeg", __LINE__, file);
  508.  
  509.    if (selSegtab)
  510.       if (rc=DosFreeSeg(selSegtab))
  511.          error_exit(rc, "DosFreeSeg", __LINE__, file);
  512.  
  513.    if (selNRestab)
  514.       if (rc=DosFreeSeg(selNRestab))
  515.          error_exit(rc, "DosFreeSeg", __LINE__, file);
  516.  
  517.    if (selRestab)
  518.       if (rc=DosFreeSeg(selRestab))
  519.          error_exit(rc, "DosFreeSeg", __LINE__, file);
  520.  
  521.    if (selImptab)
  522.       if (rc=DosFreeSeg(selImptab))
  523.          error_exit(rc, "DosFreeSeg", __LINE__, file);
  524.  
  525.    if (rc=DosClose(hf))
  526.       error_exit(rc, "DosClose", __LINE__, file);
  527.  
  528.    /***** Make sure OS/2 1.2 flushs STDOUT properly when redirected ! *****/
  529.  
  530.    fclose(stdout);
  531.  
  532.    DosExit(EXIT_PROCESS, 0);
  533. }
  534.  
  535. /*****************************************************************************
  536.                              CreateTable()
  537. *****************************************************************************/
  538.  
  539. VOID CreateTable(HFILE hf, USHORT size, ULONG offset, CHAR **table, PSEL sel)
  540. {
  541.    if (size != 0)
  542.       {
  543.       if (rc=DosChgFilePtr(hf, offset, FILE_BEGIN, &ulFilePtr))
  544.          error_exit(rc, "DosChgFilePtr", __LINE__, file);
  545.  
  546.       if (rc=DosAllocSeg(size, sel, 0))
  547.          error_exit(rc, "DosAllocSeg", __LINE__, file);
  548.  
  549.       *table = MAKEP(*sel, 0);
  550.  
  551.       if (rc=DosRead(hf, *table, size, &cbBytes))
  552.          error_exit(rc, "DosRead", __LINE__, file);
  553.       }
  554.    return;
  555. }
  556.  
  557. /*****************************************************************************
  558.                              getstring()
  559. *****************************************************************************/
  560.  
  561. void getstring(CHAR *table, USHORT num, CHAR *buf)
  562. {
  563.    USHORT i, len;
  564.  
  565.    /***** Scan thru table of counted strings and return 'num' entry *****/
  566.  
  567.    for (i=0; ; i++)
  568.       {
  569.       len = *table++;                  /* get the string's size */
  570.       if (i==num)                      /* out if this is the one we want */
  571.          break;
  572.       table += len;                    /* move on to next table entry */
  573.       }
  574.    strncpy(buf, table, len);           /* copy entry to supplied buffer */
  575.    *(buf + len) = '\0';                /* make it ASCIIZ */
  576.    return;
  577. }
  578.  
  579. /*****************************************************************************
  580.                              getsegment()
  581. *****************************************************************************/
  582.  
  583. PNEW_SEG getsegment(PNEW_SEG table, USHORT num)
  584. {
  585.    USHORT i;
  586.  
  587.    /***** Loops thru the segment table till num entry *****/
  588.  
  589.    for (i=0; i < num; i++)
  590.       table++;
  591.    return(table);
  592. }
  593.  
  594. /*****************************************************************************
  595.                              error_exit()
  596. *****************************************************************************/
  597.  
  598. void error_exit(USHORT err, CHAR *msg, USHORT line, CHAR *file)
  599. {
  600.    fprintf(stderr, "FILE: %s LINE: %u - Error %u returned from %s\n",
  601.       file, line, err, msg);
  602.    DosExit(EXIT_PROCESS, 0);
  603. }
  604.  
  605. /*****************************************************************************
  606.                                 E N D
  607. *****************************************************************************/
  608.  
  609.  
  610.