/*@z28.c:Error Service:ErrorInit(), ErrorSeen()@******************************/
/*                                                                           */
/*  THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.43)                       */
/*  COPYRIGHT (C) 1991, 2008 Jeffrey H. Kingston                             */
/*                                                                           */
/*  Jeffrey H. Kingston (jeff@it.usyd.edu.au)                                */
/*  School of Information Technologies                                       */
/*  The University of Sydney 2006                                            */
/*  AUSTRALIA                                                                */
/*                                                                           */
/*  This program 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; either Version 3, or (at your option)      */
/*  any later version.                                                       */
/*                                                                           */
/*  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.                             */
/*                                                                           */
/*  You should have received a copy of the GNU General Public License        */
/*  along with this program; if not, write to the Free Software              */
/*  Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA   */
/*                                                                           */
/*  FILE:         z28.c                                                      */
/*  MODULE:       Error Service                                              */
/*  EXTERNS:      ErrorInit(), Error(), ErrorSeen()                          */
/*                                                                           */
/*****************************************************************************/
#include "externs.h"

#define	MAX_BLOCKS	 20		/* max number of error blocks        */
#define	MAX_ERRORS	 20		/* max number of held error messages */

static BOOLEAN	print_block[MAX_BLOCKS];	/* TRUE if print this block  */
static int	start_block[MAX_BLOCKS];	/* first message of block    */
static char	message[MAX_ERRORS][MAX_BUFF];	/* the error messages        */
static int	message_fnum[MAX_ERRORS];	/* file number of error mess */
static FILE	*fp;				/* file pointer of log file  */
static BOOLEAN	error_seen;			/* TRUE after first error    */
static int	block_top;			/* first free error block    */
static int	mess_top;			/* first free error message  */
static int	CurrentFileNum;


/*****************************************************************************/
/*                                                                           */
/*  void ErrorInit()                                                         */
/*                                                                           */
/*  Initialize this module (must be called before anything else).            */
/*                                                                           */
/*****************************************************************************/

void ErrorInit(void)
{
  fp = NULL;
  error_seen = FALSE;
  block_top = 0;
  mess_top = 0;
  CurrentFileNum = -1;
}


/*****************************************************************************/
/*                                                                           */
/*  ErrorSetFile(str)                                                        */
/*                                                                           */
/*  Open log file str and initialise this module.                            */
/*                                                                           */
/*****************************************************************************/

void ErrorSetFile(FULL_CHAR *str)
{ if( fp != NULL )
    Error(28, 1, "-e argument appears twice in command line", FATAL, no_fpos);
  fp = StringFOpen(str, WRITE_FILE);
  if( fp == NULL )
    Error(28, 2, "cannot open error file %s", FATAL, no_fpos, str);
} /* end ErrorSetFile */


/*****************************************************************************/
/*                                                                           */
/*  BOOLEAN ErrorSeen()                                                      */
/*                                                                           */
/*  TRUE once an error has been found.                                       */
/*                                                                           */
/*****************************************************************************/

BOOLEAN ErrorSeen(void)
{ return error_seen;
} /* end ErrorSeen */


/*****************************************************************************/
/*                                                                           */
/*  PrintFileBanner(fnum)                                                    */
/*                                                                           */
/*  If fnum was not the subject of the previous call to PrintFileBanner,     */
/*  print a file banner for fnum.                                            */
/*                                                                           */
/*****************************************************************************/

static void PrintFileBanner(int fnum)
{
  if( fnum != CurrentFileNum )
  { fprintf(fp, "lout%s:%s", EchoFileSource(fnum), (char *) STR_NEWLINE);
    CurrentFileNum = fnum;
  }
} /* end PrintFileBanner */


/*@::EnterErrorBlock(), LeaveErrorBlock()@************************************/
/*                                                                           */
/*  EnterErrorBlock(ok_to_print)                                             */
/*                                                                           */
/*  Start off a new block of error messages.  If ok_to_print, they do not    */
/*  need to be held for a later commit.                                      */
/*                                                                           */
/*****************************************************************************/

void EnterErrorBlock(BOOLEAN ok_to_print)
{ if( block_top < MAX_BLOCKS )
  { print_block[block_top] = ok_to_print;
    start_block[block_top] = mess_top;
    block_top++;
  }
  else Error(28, 3, "too many levels of error messages", FATAL, no_fpos);
} /* end EnterErrorBlock */


/*****************************************************************************/
/*                                                                           */
/*  LeaveErrorBlock(commit)                                                  */
/*                                                                           */
/*  Finish off a block of error messages.  If commit is true, print them,    */
/*  otherwise discard them.                                                  */
/*                                                                           */
/*****************************************************************************/

void LeaveErrorBlock(BOOLEAN commit)
{ int i;
  debug0(DYY, D, "  leaving error block");
  assert( block_top > 0, "LeaveErrorBlock: no matching EnterErrorBlock!" );
  assert( commit || !print_block[block_top - 1], "LeaveErrorBlock: commit!" );
  if( fp == NULL )  fp = stderr;
  if( commit )
  { for( i = start_block[block_top - 1];  i < mess_top;  i++ )
    {
      if( AltErrorFormat )
      { fputs(message[i], fp);
      }
      else
      { PrintFileBanner(message_fnum[i]);
        fputs(message[i], fp);
      }
    }
  }
  block_top--;
  mess_top = start_block[block_top];
} /* end LeaveErrorBlock */


/*****************************************************************************/
/*                                                                           */
/*  CheckErrorBlocks()                                                       */
/*                                                                           */
/*  Check (at end of run) that all error blocks have been unstacked.         */
/*                                                                           */
/*****************************************************************************/

void CheckErrorBlocks(void)
{ assert( block_top == 0, "CheckErrorBlocks: block_top != 0!" );
} /* end CheckErrorBlocks */


/*@::Error()@*****************************************************************/
/*                                                                           */
/*  Error(etype, pos, str, p1, p2, p3, p4, p5, p6)                           */
/*                                                                           */
/*  Report error of type etype at position *pos in input.                    */
/*  The error message is str with parameters p1 - p6.                        */
/*                                                                           */
/*****************************************************************************/

POINTER Error(int set_num, int msg_num, char *str, int etype, FILE_POS *pos, ...)
{
  va_list ap;
  char val[MAX_BUFF];
  va_start(ap, pos);
  vsprintf(val, condcatgets(MsgCat, set_num, msg_num, str), ap);
  if( fp == NULL )  fp = stderr;
  switch( etype )
  {

    case INTERN:
    
      while( block_top > 0 )  LeaveErrorBlock(TRUE);
      if( AltErrorFormat )
      {
        fprintf(fp, condcatgets(MsgCat, 28, 7, "%s internal error: %s%s"),
	  EchoAltFilePos(pos), val, (char *) STR_NEWLINE);
        /* for estrip's benefit: Error(28, 7, "%s internal error: %s%s") */
      }
      else
      {
        PrintFileBanner(file_num(*pos));
        fprintf(fp, condcatgets(MsgCat, 28, 4, "  %6s internal error: %s%s"),
	  EchoFileLine(pos), val, (char *) STR_NEWLINE);
        /* for estrip's benefit: Error(28, 4, "  %6s internal error: %s%s") */
      }
#if DEBUG_ON
      abort();
#else
      exit(1);
#endif
      break;


    case FATAL:
    case FATAL_WITH_USAGE:
    
      while( block_top > 0 )  LeaveErrorBlock(TRUE);
      if( AltErrorFormat )
      {
        fprintf(fp, condcatgets(MsgCat, 28, 8, "%s: fatal error: %s%s"),
	  EchoAltFilePos(pos), val, (char *) STR_NEWLINE);
      }
      else
      {
        PrintFileBanner(file_num(*pos));
        fprintf(fp, condcatgets(MsgCat, 28, 5, "  %6s: fatal error: %s%s"),
	  EchoFileLine(pos), val, (char *) STR_NEWLINE);
      }
      /* for estrip's benefit: Error(28, 5, "  %6s: fatal error: %s%s") */
      /* for estrip's benefit: Error(28, 8, "%s: fatal error: %s%s") */
      if( etype == FATAL_WITH_USAGE )
	PrintUsage(fp);
      exit(1);
      break;


    case WARN:
    
      if( block_top == 0 || print_block[block_top - 1] )
      {
	if( AltErrorFormat )
	{
	  fprintf(fp, "%s: %s%s", EchoAltFilePos(pos), val,
	    (char *) STR_NEWLINE);
	}
	else
	{
	  PrintFileBanner(file_num(*pos));
	  fprintf(fp, "  %6s: %s%s", EchoFileLine(pos), val,
	    (char *) STR_NEWLINE);
	}
      }
      else if( mess_top < MAX_ERRORS )
      {
	if( AltErrorFormat )
	{
	  sprintf(message[mess_top++], "%s: %s%s", EchoAltFilePos(pos), val,
	    (char *) STR_NEWLINE);
	}
	else
	{ message_fnum[mess_top] = file_num(*pos);
	  sprintf(message[mess_top++], "  %6s: %s%s",
	    EchoFileLine(pos), val, (char *) STR_NEWLINE);
	}
      }
      else Error(28, 6, "too many error messages", FATAL, pos);
      error_seen = TRUE;
      break;


    default:
    
      assert(FALSE, "Error: invalid error type");
      break;

  }
  va_end(ap);
  return 0;
} /* end Error */
