/*** ERROR.C -- error handling functions *************************************
*
*	Copyright (c) 1988-1990, Microsoft Corporation.  All rights reserved.
*
* Revision History:
*  01-Feb-1994 HV Move messages to external file.
*  29-Oct-1993 HV Change the version scheme.
*  15-Oct-1993 HV Use tchar.h instead of mbstring.h directly, change STR*() to _ftcs*()
*  18-May-1993 HV Change reference to messageTable[] to __MSGTAB.  The new
*                  (and more standard) mkmsg.exe output __MSGTAB instead
*                  of messageTable.  See message.h
*  10-May-1993 HV Add include file mbstring.h
*                 Change the str* functions to STR*
*  08-Jun-1992 SS add IDE feedback support
*  08-Jun-1992 SS Port to DOSX32
*  23-Feb-1990 SB Version No correctly displayed
*  31-Jan-1990 SB Debug version changes
*  05-Jan-1990 SB Rev no in std format; #ifdef LEADING_ZERO for old code
*  07-Dec-1989 SB Changed CopyRight Years to 1988-90; Add #ifdef DEBUG_HEAP
*  06-Nov-1989 SB Error messages now show NMAKE and not argv0
*  04-Sep-1989 SB heapdump() has two extra parameters
*  18-Aug-1989 SB For -z nothing done by makeMessage; Fix later
*  05-Jul-1989 SB localize rest of the messages in makeError()
*  14-Jun-1989 SB modified to localize all messages and auto update version no
*  05-Jun-1989 SB modified heapdump(), has a previous member in the list too
*  14-Apr-1989 SB modified heapdump() for better error messages when DEBUG
*  05-Apr-1989 SB made functions NEAR; all funcs to one code segment
*		  modified heapdump() to give better heap violations
*  22-Mar-1989 SB del call to unlinkTmpFiles() ;add call to delScriptFiles().
*  17-Mar-1989 SB heapdump() has an additional check built-in
*  10-Mar-1989 SB Changed makeMessage() for -z to get echo CMD's into PWB.SHL
*  16-Feb-1989 SB changed makeError() and makeMessage() to handle -help
*  09-Jan-1989 SB changes in makeError() to handle -help correctly
*  05-Dec-1988 SB Added CDECL for makeError(), makeMessage(); Pascal calling
*		  #ifdef'd heapdump prototype
*  20-Oct-1988 SB Changed some eoln comments to be in the same column
*  12-Oct-1988 SB Made GetFarMsg() to be Model independent & efficient
*  17-Aug-1988 RB Clean up.
*  24-Jun-1988 rj Added doError flag to unlinkTmpFiles call.
*
******************************************************************************/

#include "nmake.h"
#include "nmmsg.h"
#include "proto.h"
#include "globals.h"
#include "grammar.h"
#pragma hdrstop
#include "version.h"

#define FATAL	    1					/* error levels for  */
#define ERROR	    2					/* systems lanuguages*/
#define RESERVED    3					/* products	     */
#define WARNING     4

#define CopyRightYrs "1988-93"
#define NmakeStr "NMAKE"
/* Hacked to work upto build no 9 */
#ifdef FINAL_RELEASE
#define paste(a, b, c) #a "." #b
#else // !FINAL_RELEASE
#define paste(a, b, c) #a "." #b "." #c
#endif // FINAL_RELEASE
#define VERSION(major, minor, buildno) paste(major, minor, buildno)

extern char	* startupDir;
LOCAL  char	* NEAR	 getFarMsg(unsigned);

void CDECL NEAR
makeError (
    unsigned lineNumber,
    unsigned msg,
    ...)
{
    unsigned exitCode = 2,			       /* general program err */
	     level;
    va_list args;				       /* More arguments      */

    va_start (args, msg);		   /* Point 'args' at first extra arg */

    if (ON(gFlags,F1_CRYPTIC_OUTPUT)
	&& (msg / 1000) == WARNING)
	return;
    displayBanner();
    if (lineNumber)
	fprintf(stderr,"%s(%d) : ",fName,lineNumber);
    else fprintf(stderr,"%s : ",NmakeStr);
    switch (level = msg / 1000) {
	case FATAL:	makeMessage(FATAL_ERROR_MESSAGE);
			if (msg == OUT_OF_MEMORY) exitCode = 4;
			break;
	case ERROR:	makeMessage(ERROR_MESSAGE);
			break;
	case WARNING:	makeMessage(WARNING_MESSAGE);
			break;
	default:	break;
    }
    fprintf(stderr," U%04d: ",msg);		       /* U for utilities     */
    vfprintf(stderr,getFarMsg(msg),args);
    putc('\n',stderr);
    fflush(stderr);
#ifdef DEBUG_ERRORS
    if (msg == 1010) heapdump(__FILE__, __LINE__);
#endif
    if (level == FATAL) {
#ifdef DOS
	//change directory to startup directory; ignore error code ... we
	//cannot handle it
	chdir(startupDir);
#endif
fprintf(stderr,"Stop.\n");
	delScriptFiles();
#ifdef NMK_DEBUG
	fprintf(stderr, "Exiting with an error ...\n");
#endif
#if !defined(NDEBUG) && !defined(NT_BUILD)
    printStats();
#endif
	exit(exitCode);
    }
}


void CDECL NEAR
makeMessage(
    unsigned msg,
    ...)
{
    va_list args;
    FILE *stream = stdout;

    va_start (args, msg);

    if (msg != USER_MESSAGE && ON(gFlags,F1_CRYPTIC_OUTPUT))
	return;
    displayBanner();
    if (msg >= FATAL_ERROR_MESSAGE && msg <= COPYRIGHT_MESSAGE_2)
	stream = stderr;
    if (msg == COPYRIGHT_MESSAGE_1)
	putc('\n',stream);
    vfprintf(stream,getFarMsg(msg),args);
    if ((msg < COMMANDS_MESSAGE || msg > STOP_MESSAGE) && msg != MESG_LAST)
	putc('\n',stream);
    fflush(stream);
}

#define MAX_IDE_STRING 132

void CDECL NEAR
makeIdeMessage(unsigned uIdeCode, unsigned uMsg,...)
{
#if 0		//We don't use this anymore
    static BOOL fIdeFeedback = FALSE;
    char buffer[MAX_IDE_STRING];
    char * pszWrite = buffer;

    if (!uIdeCode) { // first call
	char _FAR * pszIdeFlags;

	if ((pszIdeFlags = getenv ("_MSC_IDE_FLAGS")) &&
#ifdef FLAT
	     _ftcsstr (pszIdeFlags, "-FEEDBACK"))
#else
	     _fstrstr (pszIdeFlags, (char far *) "-FEEDBACK"))
#endif
	    fIdeFeedback = TRUE;
	}


    if (!fIdeFeedback)
	return;

    *pszWrite++ = '@';
    *pszWrite++ = 'I';

    switch (uIdeCode) {
	case 0:
	    *pszWrite++ = '0';
	    break;
	case 1:
	    *pszWrite++ = '1';
	    break;
	case 2:
	    *pszWrite++ = '2';
	    break;
	case 3:
	    *pszWrite++ = '3';
	    break;

	default:
	    return;
    }

    if (uIdeCode) {
	va_list args;

	va_start (args, uMsg);
	_vsnprintf(pszWrite, (size_t) MAX_IDE_STRING - 4, getFarMsg(uMsg), args);
	buffer[MAX_IDE_STRING - 1] = '\0';

	pszWrite = buffer + _ftcslen (buffer);
    }


    if (uMsg != COPYRIGHT_MESSAGE_2)
	*pszWrite++ = '\n';

    *pszWrite = '\0';


    _write(_fileno(stderr), buffer, (unsigned) (pszWrite-buffer));
#endif
}

/***  getFarMsg - get message from far segment
 *
 * Purpose:
 *  Gets an error message from the far message segment by either returning
 *  a pointer to the message (C and L model) or copying to a static near
 *  buffer and returning a pointer to the buffer (other models).
 *
 * Input:
 *  n = error message #, defined as a symbolic constant
 *
 * Output:
 *  Returns a pointer to the message buffer. In Compact and Large Models
 *  it returns a far pointer and in others it returns a near pointer.
 *
 * Notes:
 *  Previously the routine always copied to a static near buffer. Now the
 *  action has been changed to returning a far pointer in case of Compact
 *  and Large models and it copies to near buffer otherwise, returning
 *  a pointer to the buffer. [sundeep]
 *
 *
 */

LOCAL char * NEAR
getFarMsg(n)
unsigned n;
{
    char _FAR *p;
#ifndef FLAT
#if !(defined(M_I86CM) || defined(M_I86LM))	 /* if not C or L model */
    char *q;
    static char buf[80];
    register int i;

    i = sizeof(buf);
#endif
#endif

    p = get_err(n);
#if !defined (FLAT) && !(defined(M_I86CM) || defined(M_I86LM))	 /* if not C or L model */
    if (p == 0L)
	*buf  = '\0';
    else					 /* copy to near buffer */
	for (q = buf; q < buf + i && (*q++ = *p++);)
	    ;
    if (q == buf + i)
	q[-1] = '\0';
    return(buf);
#else						 /* if compact or large model */
    return(p);
#endif
}


/*** displayBanner - display SignOn Banner *************************************
*
* Scope:
*  Global
*
* Purpose:
*  Displays SignOn Banner (Version & Copyright Message)
*
* Input:
*
* Output:
*
* Errors/Warnings:
*
* Assumes:
*  If rup is 0 then build version is to be suppressed
*
* Modifies Globals:
*  bannerDisplayed -- Set to TRUE
*
* Uses Globals:
*
* Notes:
*  1> Display Banner to stderr for compatibility with Microsoft C Compiler.
*  2> rmj, rmm, rup are set by SLM as #define's in VERSION.H
*  3> szCopyrightYrs is a macro set in this file
*
*******************************************************************************/


void NEAR
displayBanner()
{
#ifdef LEADING_ZEROES
    char szRevisionNo[5];				      /* For '.nnn\0' */
    char szMinorVersion[3];				      /* For 'nn\0'   */
#endif
    if (bannerDisplayed)
	return;
    bannerDisplayed = TRUE;

#ifdef LEADING_ZEROES
    szMinorVersion[0] = '0';
    szMinorVersion[1] = '0';
    itoa(rmm, szMinorVersion + (rmm < 10 ? 1 : 0), 10);
    szRevisionNo[0] = rup ? '.' : '\0'; 	/* if 0 Revision is not shown */
    szRevisionNo[1] = '0';
    szRevisionNo[2] = '0';
    itoa(rup, szRevisionNo + (rup < 10 ? 3 : (rup < 100 ? 2 : 1)), 10);
    makeMessage(COPYRIGHT_MESSAGE_1, rmj, szMinorVersion, szRevisionNo);
#endif
    makeIdeMessage(1, COPYRIGHT_MESSAGE_1, VERSION(rmj, rmm, rup));
    makeIdeMessage(2, COPYRIGHT_MESSAGE_2, CopyRightYrs);

    makeMessage(COPYRIGHT_MESSAGE_1, VERSION(rmj, rmm, rup));
    makeMessage(COPYRIGHT_MESSAGE_2, CopyRightYrs);

    fflush(stderr);
}

#ifdef HEAP
void NEAR
heapdump(file, line)
char *file;
int line;
{
    struct _heapinfo h, prev;
    int status;
    BOOL fDetails = FALSE;

#ifdef TEST_RECURSION
    fDetails = TRUE;
#endif

    h._pentry = NULL;
    prev = h;

    printf("heapdump at %s(%d): ", file, line);
    if (fDetails) putchar('\n');

    while((status = _heapwalk(&h)) == _HEAPOK) {
	if (fDebug || fDetails)
	    printf("%6s block at %p of size %4.4X\n",
		   (h._useflag == _USEDENTRY) ? "USED" : "FREE",
		   h._pentry,
		   h._size);
	if (!h._size)
	    if (h._useflag == _USEDENTRY)
		printf("**** Error, Block of size 0 in use at %p ***\n",
		       h._pentry);
	    else if (fDebug)
		printf("** Plausible error, free block of size 0 at %p **\n",
		       h._pentry);
	prev = h;
    }
    switch (status) {
	case _HEAPEMPTY:  printf("OK - empty heap\n\n");     break;
	case _HEAPEND:		    printf("OK - end of heap\n\n");	break;
	case _HEAPBADBEGIN: printf("ERROR - bad pointer to heap at %p\n\n",
				   h._pentry);	break;
	case _HEAPBADNODE:  printf("ERROR - bad node in heap at %p\n\n",
				   h._pentry);	break;
    }
    fflush(stdout);
}
#endif

/*** usage - prints the usage message  ***************************************
*
* Scope:
*  Extern
*
* Purpose:
*   Prints a usage message
*
* Input: none
* Output: to screen
*
* Assumes:
*   The usage messages are in order between MESG_FIRST and MESG_LAST in the
* messages file.
*
* Notes:
*
*******************************************************************************/
void CDECL NEAR
usage (void)
{
    unsigned mesg;
    BOOL    fOut = FALSE;
    char buf1[80];
    char buf2[80];

    for (mesg = MESG_FIRST; mesg < MESG_A; ++mesg)
	makeMessage(mesg, "NMAKE");

    for (mesg = MESG_A; mesg < MESG_LAST; mesg++)
	{


#if defined(FLAT) || !defined(DOS)
	if (mesg == MESG_M)
	    mesg++;
#endif

#if !defined(SELF_RECURSE)
	if (mesg == MESG_V)
	    mesg++;
#endif
	if (mesg == MESG_LAST)
	    break;

	if (!fOut)
	    _ftcscpy (buf1, getFarMsg(mesg));
	else
	    {
	    _ftcscpy (buf2, getFarMsg(mesg));
	    makeMessage (MESG_OPTIONS, buf1, buf2);
	    }

	fOut = (BOOL) !fOut;
	}


    if (fOut)
	makeMessage (MESG_OPTIONS, buf1, "");

    makeMessage (MESG_LAST);
}