/*
** Splint - annotation-assisted static program checker
** Copyright (C) 1994-2003 University of Virginia,
**         Massachusetts Institute of Technology
**
** 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 2 of the License, 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.
** 
** The GNU General Public License is available from http://www.gnu.org/ or
** the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
** MA 02111-1307, USA.
**
** For information on splint: info@splint.org
** To report a bug: splint-bug@splint.org
** For more information: http://www.splint.org
*/
/*
** llerror.c
**
** error reporting procedures
*/

# include "splintMacros.nf"
# include <string.h>
# include <errno.h>
# include "basic.h"
# include "llmain.h"
# include "cpperror.h"
# include "Headers/version.h" /* Visual C++ finds a different version.h on some path! */

/* Don't allow possibly-recursive assertion failures. */
# undef llassert
# define llassert llassertprotect

static void printIndentMessage (FILE *p_stream, /*@only@*/ cstring p_sc, int p_indent)
   /*@modifies *p_stream@*/ ;

static bool s_scanOpen = FALSE;
static int s_lclerrors = 0;
static size_t s_lastfileloclen = 10;
static /*@only@*/ cstring s_lastmsg = cstring_undefined;
static int s_mcount = 0;
static /*@only@*/ cstring saveOneMessage = cstring_undefined;
static /*@only@*/ fileloc lastparseerror = fileloc_undefined;
static /*@only@*/ fileloc lastbug = fileloc_undefined;
static bool llgenerrorreal (flagcode p_code, 
			    char *p_srcFile, int p_srcLine, 
			    /*@only@*/ cstring p_s,
			    /*@temp@*/ cstring p_addtext,
			    fileloc p_fl, bool p_iserror, bool p_indent)
                 /*@modifies g_warningstream@*/ ;
static bool llgenerroraux (flagcode p_code, char *p_srcFile, int p_srcLine, 
			   /*@only@*/ cstring p_s,
			   /*@temp@*/ cstring p_addtext,
			   fileloc p_fl, bool p_iserror, bool p_indent)
                 /*@modifies g_warningstream@*/ ;

static void generateCSV (flagcode p_code, cstring p_s, cstring p_addtext, fileloc p_fl) 
     /*@modifies g_csvstream@*/ ;

static void printError (FILE *p_stream, /*@only@*/ cstring p_sc)
   /*@globals s_lastfileloclen @*/
   /*@modifies *p_stream@*/ ;
static void printMessage (FILE *p_stream, /*@only@*/ cstring p_s)
   /*@modifies *p_stream@*/ ;

static void llgenhint (/*@only@*/ cstring p_s) /*@modifies g_warningstream@*/ ;

static void showSourceLoc (char *srcFile, int srcLine)
     /*@modifies g_warningstream@*/
{
  if (context_getFlag (FLG_SHOWSOURCELOC)) {
    llgenhint (message ("%s:%d: Source code error generation point.",
			cstring_fromChars (srcFile), srcLine));
  }
  
}

static /*@null@*/ char *
maxcp (/*@null@*/ /*@returned@*/ char *a, /*@null@*/ /*@returned@*/ char *b)
{
  if (a > b) return a;
  else return b;
}

static void
printBugReport (void)
{
  fprintf (g_errorstream, "     *** Please report bug to %s ***\n",
	   SPLINT_MAINTAINER);
  llflush ();
  /* don't exit (EXIT_FAILURE); */
}

static bool s_needsPrepare = TRUE;

void prepareMessage ()
{
  DPRINTF (("Prepare message: %s", bool_unparse (context_loadingLibrary ())));
  showHerald ();

  if ((context_isPreprocessing () || context_loadingLibrary ())
      && s_needsPrepare
      && context_getFlag (FLG_SHOWSCAN))
    {
      llflush ();
      displayScanClose ();
      s_needsPrepare = FALSE;
    }

  llflush ();
}

void closeMessage (void)
{
  if (context_isPreprocessing ()
      && context_getFlag (FLG_SHOWSCAN))
    {
      llflush ();
      displayScanOpen (cstring_makeLiteral ("more preprocessing ."));
      llassertprotect (!s_needsPrepare);
      s_needsPrepare = TRUE;
    }
  else
    {
      llflush ();
    }
}

void
llmsg (/*@only@*/ cstring s)
{
  context_setNeednl ();
  prepareMessage ();
  printMessage (g_messagestream, s);
  closeMessage ();
}

void
lldiagmsg (/*@only@*/ cstring s)
{
  static bool inmsg = FALSE;

  if (inmsg)
    {
      fprintf (g_errorstream,
	       "Recursive message call detected: %s\n", 
	       cstring_toCharsSafe (s));
      llexit (LLFAILURE);
    }

  inmsg = TRUE;

  context_setNeednl ();
  prepareMessage ();
  printMessage (g_messagestream, s);
  closeMessage ();

  inmsg = FALSE;
}

void
llmsgplain (/*@only@*/ cstring s)
{
  context_setNeednl ();
  prepareMessage ();
  printMessage (g_messagestream, s);
  closeMessage ();
}

void llerror_flagWarning (cstring s)
{
  if (context_getFlag (FLG_WARNFLAGS))
    {
      llgenmsg (s, g_currentloc);
    }
  else
    {
      cstring_free (s);
    }
}

static void
llgenhint (/*@only@*/ cstring s) /*@modifies g_warningstream@*/
{
  int indent = context_getIndentSpaces () - 1;

  if (indent < 0) indent = 0;

  context_setNeednl ();
  printIndentMessage (g_warningstream, s, indent);
}

void
llhint (cstring s)
{
  if (context_getFlag (FLG_HINTS) &&
      !(context_inSuppressRegion () || context_inSuppressZone (g_currentloc)))
    {
      llgenhint (s);
    }
  else
    {
      cstring_free (s);
    }
}

static void
llshowhint (flagcode f)
{
  if (context_getFlag (FLG_HINTS))
    {
      if ((flagcode_numReported (f) == 0) || context_getFlag (FLG_FORCEHINTS))
	{
	  cstring desc = flagcodeHint (f);

	  if (cstring_isDefined (desc))
	    {
	      llgenhint (cstring_copy (desc));
	    }
	}
    }
}

static void
llsuppresshint2 (char c, flagcode f1, flagcode f2)
{

  if (context_getFlag (FLG_HINTS))
    {
      if ((flagcode_numReported (f1) == 0
	   || flagcode_numReported (f2) == 0)
	  || context_getFlag (FLG_FORCEHINTS))
	{
	  cstring desc = flagcodeHint (f1);
	  context_setNeednl ();
	  s_lastfileloclen = 8;

	  if (cstring_isUndefined (desc))
	    {
	      desc = flagcodeHint (f2);
	    }

	  if (flagcode_isNamePrefixFlag (f1))
	    {
	      f1 = FLG_NAMECHECKS;
	    }

	  if (flagcode_isNamePrefixFlag (f2))
	    {
	      f2 = FLG_NAMECHECKS;
	    }

	  if (f1 == f2)
	    {
	      if (cstring_isDefined (desc))
		{
		  llgenhint (message ("%s (Use %h%s to inhibit warning)", desc,
				      c,
				      flagcode_unparse (f1)));
		}
	      else
		{
		  llgenhint (message ("(Use %h%s to inhibit warning)", 
				      c, flagcode_unparse (f1)));
		}
	    }
	  else
	    {
	      if (cstring_isDefined (desc))
		{
		  llgenhint (message ("%s (Use either %h%s or %h%s to inhibit warning)", desc, 
				      c,
				      flagcode_unparse (f1),
				      c,
				      flagcode_unparse (f2)));
		}
	      else
		{
		  llgenhint (message ("(Use either %h%s or %h%s to inhibit warning)", c,
				      flagcode_unparse (f1),
				      c, flagcode_unparse (f2)));
		}
	    }
	}
    }
}

static void
llsuppresshint (char c, flagcode f)
{
  if (context_getFlag (FLG_HINTS))
    {
      if ((flagcode_numReported (f) == 0) || context_getFlag (FLG_FORCEHINTS))
	{
	  cstring desc = flagcodeHint (f);
	  context_setNeednl ();
	  s_lastfileloclen = 8;

	  if (flagcode_isNamePrefixFlag (f))
	    {
	      f = FLG_NAMECHECKS;
	    }

	  if (cstring_isDefined (desc))
	    {
	      llgenhint (message ("%s (Use %h%s to inhibit warning)", desc, c,
				  flagcode_unparse (f)));
	    }
	  else
	    {
	      llgenhint (message ("(Use %h%s to inhibit warning)", c,
				  flagcode_unparse (f)));
	    }
	}
    }
}

static void
llnosuppresshint (flagcode f)
{
  if (context_getFlag (FLG_FORCEHINTS))
    {
      cstring desc = flagcodeHint (f);
      context_setNeednl ();
      s_lastfileloclen = 8;

      if (cstring_isDefined (desc))
	{
	  printError (g_warningstream, message ("    %s", desc));
	}
    }
}

/*@constant int MAXSEARCH; @*/
# define MAXSEARCH 20

/*@constant int MINLINE; @*/
# define MINLINE 35

typedef /*@null@*/ /*@dependent@*/ char *nd_charp;

/*
** mstring_split
**
** Divides a string into lines of up to maxline characters.
**
** Initial string: *sp
**
** Output split: *sp / *tp
**                     possibly null
*/

static void
mstring_split (/*@returned@*/ char **sp,
	       /*@out@*/ nd_charp *tp,
	       int maxline, /*@in@*/ int *indentchars)
{
  char *nl;
  char *t;
  char *s = *sp;
  char *osp = *sp;

  *tp = NULL;

  DPRINTF (("Split: %s / %d", *sp, maxline));

  if (maxline < MINLINELEN)
    {
      maxline = MINLINELEN;
    }

  if (*indentchars > 0)
    {
      s = *sp = mstring_concatFree1 (mstring_spaces (*indentchars), s);
      osp = s;
    }

  /*
  ** splitting:
  **
  **    if there is a newline in first maxline characters, split there
  **    if line len is <= maxline, return no split
  **    if there is a ':' or ';' or ',' followed by ' ' in first maxline characters,
  **       split there unless the ' ' is followed by a '}', then
  **       split after '}'
  **       of the ';' is inside quotation marks
  **    if there is a space or tab in last maxsearch characters, split there
  **    else, split at maxline
  **
  **    special code: slash [1-9] after a newline means indent the rest <n> chars
  **
  */

  nl = strchr (s, '\n');

  if ((nl != NULL) && ((nl - s) < maxline))
    {
      *nl = '\0';
      t = nl + 1;

      if (*t == '\0')
	{
	  llassertprotect (*tp == NULL || (*tp > osp));
	  return;
	}

      if (*t >= '\1' && *t <= '\7')
	{
	  *indentchars += (int) (*t - '\1') + 1;
	  t++;
	}
      
      *tp = t;
      return;
    }
  else if (size_toInt (strlen (s)) < maxline)
    {
      llassertprotect (*tp == NULL);
      return;
    }
  else
    {
      int i = 0;
      char savechar;
      char *lcolon, *lsemi, *lcomma;
      char *splitat;
      
      splitat = NULL;

      t = s + maxline - 1;
      savechar = *t;

      *t = '\0';
      lcolon = strrchr (s, ':');
      lsemi = strrchr (s, ';');
      lcomma = strrchr (s, ',');

      *t = savechar;

      splitat = maxcp (lcolon, lsemi);

      if (splitat != NULL && ((int)(splitat - s) > MINLINE)
	  && *(splitat) != '\0'
	  && *(splitat + 1) == ' ' 
	  && (*(splitat + 2) != '}'
	      && *(splitat + 2) != ',' 
	      && (*(splitat + 2) != '\0'))) 
	{
	  *(splitat + 1) = '\0';
	  t = splitat + 2;
	  *tp = t;
	  llassertprotect (*tp == NULL || (*tp > osp));
	  return;
	}

      if (lcomma != NULL && ((lcomma - s) > maxline - 5))
	{
	  splitat = lcomma;
	  
	  if (splitat != NULL && ((int)(splitat - s) > MINLINE)
	      && *(splitat) != '\0'
	      && *(splitat + 1) == ' ' 
	      && (*(splitat + 2) != '}'
		  && (*(splitat + 2) != '\0'))) 
	    {
	      *(splitat + 1) = '\0';
	      t = splitat + 2;
	      *tp = t;
	      llassertprotect (*tp == NULL || (*tp > osp));
	      return;
	    }
	}
      
      /*
      ** Search for any breaking point (at least 4 letters past s)
      */

      while (*t != ' ' && *t != '\t' && i < MAXSEARCH && t > (s + 4))
	{
	  t--;
	  i++;
	}

      if (*t != ' ' && *t != '\t')
	{
	  llassertprotect (maxline > 0);
	  t = mstring_copy (s + maxline);
	  *(s + maxline) = '\0';

	  if (*t == '\0')
	    {
	      sfree (t);
	      llassertprotect (*tp == NULL || (*tp > osp));
	      return;
	    }

	  mstring_markFree (t);
	  *tp = t;
	  /* Won't be true since t is a copy: llassertprotect (*tp == NULL || (*tp > osp)); */
	  return;
	}
      else
	{

	  *t = '\0';
	  t++;
	  
	  if (*t == '\0') return;

	  /*
	  ** returns unqualified as only
	  */

	  *tp = t;

	  llassert (*sp != *tp);
	  return;
	}
    }

  BADBRANCH;
}

static
void limitmessage (/*@only@*/ cstring s, fileloc loc)
{
  if (s_mcount > context_getLimit () + 1)
    {
      cstring_free (s);
    }
  else
    {
      cstring flstring = fileloc_unparse (loc);

      s_lastfileloclen = cstring_length (flstring);
      cstring_free (saveOneMessage);
      saveOneMessage = message ("%q: %q", flstring, s);
    }
}

static int parseerrorcount = 0;

void cleanupMessages ()
{
  parseerrorcount = 0;

  if (context_unlimitedMessages ())
    {
     ;
    }
  else
    {
      int unprinted = s_mcount - context_getLimit ();

      if (unprinted > 0)
	{
	  if (unprinted == 1 && cstring_isDefined (saveOneMessage))
	    {
	      prepareMessage ();
	      printError (g_warningstream, saveOneMessage);
	      closeMessage ();
	      saveOneMessage = cstring_undefined;
	    }
	  else
	    {
	      if (cstring_isDefined (saveOneMessage))
		{
		  /* cstring_free (saveOneMessage); */
		  saveOneMessage = cstring_undefined;
		}

	      fprintf (g_warningstream, "%s: (%d more similar errors unprinted)\n",
		       cstring_toCharsSafe (fileloc_filename (g_currentloc)),
		       s_mcount - context_getLimit ());
	    }
	}
    }

  s_mcount = 0;
}

void
llgenmsg (/*@only@*/ cstring s, fileloc fl)
{
  cstring flstring = fileloc_unparse (fl);
  s_lastfileloclen = cstring_length (flstring);

  prepareMessage ();
  (void) printError (g_warningstream, message ("%q: %q", flstring, s));
  closeMessage ();
}

void
llgenindentmsg (/*@only@*/ cstring s, fileloc fl)
{
  cstring flstring = fileloc_unparse (fl);
  int indentspaces = context_getLocIndentSpaces ();
  prepareMessage ();
  
  (void) printIndentMessage (g_warningstream, message ("%q: %q", flstring, s),
			     indentspaces);

  closeMessage ();
}

void
llgenindentmsgnoloc (/*@only@*/ cstring s)
{
  prepareMessage ();
  (void) printIndentMessage (g_warningstream, s, context_getIndentSpaces ());
  closeMessage ();
}

static bool
llgentypeerroraux (char *srcFile, int srcLine,
		   flagcode ocode, ctype t1, exprNode e1, ctype t2, exprNode e2,
		   /*@only@*/ cstring s, fileloc fl)
{
  cstring hint = cstring_undefined;
  flagcode code = ocode;
  flagcode hcode = INVALID_FLAG;
  ctype ut1 = t1;
  ctype ut2 = t2;

  DPRINTF (("Type error [%s]: %s / %s : %s / %s",
	    flagcode_unparse (ocode),
	    exprNode_unparse (e1), exprNode_unparse (e2),
	    ctype_unparse (t1), ctype_unparse (t2)));
  
  DPRINTF (("Bool: %s / %s",
	    bool_unparse (ctype_isBool (t1)),
	    bool_unparse (ctype_isBool (t2))));

  /* 
  ** Set the flag using the underlying types that didn't match.
  */

  while (ctype_isPointer (ut1) && ctype_isPointer (ut2)) {
    ut1 = ctype_baseArrayPtr (ut1);
    ut2 = ctype_baseArrayPtr (ut2);
  }

  if (ctype_isRealNumAbstract (ut1) && exprNode_isNumLiteral (e2)) 
    {
      hcode = FLG_NUMABSTRACTLIT;
    }
  else if ((ctype_isFloat (ut1) && ctype_isDouble (ut2))
	   || (ctype_isFloat (ut1) && ctype_isDouble (ut2)))
    {
      hcode = FLG_FLOATDOUBLE;
    }
  else if ((exprNode_isCharLiteral (e1) && ctype_isInt (ut2))
	   || (exprNode_isCharLiteral (e2) && ctype_isInt (ut1)))
    {
      hcode = FLG_CHARINTLITERAL;
    }
  else if ((exprNode_isNumLiteral (e1) && ctype_isReal (ut2))
	   || (exprNode_isNumLiteral (e2) && ctype_isReal (ut1)))
    {
      hcode = FLG_NUMLITERAL;
    }
  else if ((ctype_isManifestBool (ut1) && ctype_isInt (ut2))
	   || (ctype_isInt (ut1) && ctype_isManifestBool (ut2)))
    /* evs 2000-07-24: was ctype_isDirectBool */
    {
      hcode = FLG_BOOLINT;
    }
  else if (((ctype_isChar (ut1) && !ctype_isInt (ut1)) && ctype_isInt (ut2))
	   || ((ctype_isInt (ut1) && (ctype_isChar (ut2) && !ctype_isInt (ut2)))))
    {
      hcode = FLG_CHARINT;
    }
  else if ((ctype_isInt (ut1) && ctype_isInt (ut2))
	   || (ctype_isChar (ut1) && ctype_isChar (ut2))
	   || (ctype_isDouble (ut1) && ctype_isDouble (ut2)))
    {
      if (!bool_equal (ctype_isSigned (ut1), ctype_isSigned (ut2)))
	{
	  if (ctype_isArbitraryIntegral (ctype_realType (ut1))
	      && !ctype_isArbitraryIntegral (ctype_realType (ut2)))
	    {
	      hcode = FLG_MATCHANYINTEGRAL;
	    }
	  else if (ctype_isArbitraryIntegral (ctype_realType (ut2))
		   && !ctype_isArbitraryIntegral (ctype_realType (ut1)))
	    {
	      hcode = FLG_MATCHANYINTEGRAL;
	    }
	  else
	    /*drl 4-270-2003 even if ignoresigns is set there may be another
	      problem that is causing splint to complain about a type error.
	      Don't tell the user that they can add +ignoresigns if it's
	      already on*/
	    {
	      DPRINTF(("TEST INGORESIGNS"));
	      if (context_getFlag(FLG_IGNORESIGNS) )
		{
		  DPRINTF(("INGORESIGNS SET"));
		  hcode = FLG_IGNOREQUALS;
		}
	    
	      else
		{
		  DPRINTF(("INGORESIGNS NOT SET"));
		  hcode = FLG_IGNORESIGNS;
		}
	    }
	}
      else
	{
	  hcode = FLG_IGNOREQUALS;
	}
    }
  else if (ctype_isArbitraryIntegral (ctype_realType (ut1)))
    {
      if (ctype_isArbitraryIntegral (ctype_realType (ut2)))
	{
	  hcode = FLG_MATCHANYINTEGRAL;
	}
      else if (ctype_match (ut2, ctype_ulint))
	{
	  hcode = FLG_LONGUNSIGNEDINTEGRAL;
	}
      else if (ctype_match (ut2, ctype_lint))
	{
	  hcode = FLG_LONGINTEGRAL;
	}
      else if (ctype_isInt (ut2))
	{
	  hcode = FLG_MATCHANYINTEGRAL;
	}
      else
	{
	  ;
	}
    }
  else if (ctype_isArbitraryIntegral (ctype_realType (ut2)))
    {
      ctype tr = ctype_realType (ut1);

      if (ctype_isArbitraryIntegral (tr))
	{
	  hcode = FLG_MATCHANYINTEGRAL;
	}
      else if (ctype_match (ut1, ctype_ulint))
	{
	  if (ctype_isUnsignedIntegral (tr))
	    {
	      hcode = FLG_LONGUNSIGNEDUNSIGNEDINTEGRAL;
	    }
	  else if (ctype_isSignedIntegral (tr))
	    {
	      ;
	    }
	  else
	    {
	      hcode = FLG_LONGUNSIGNEDINTEGRAL;
	    }
	}
      else if (ctype_match (ut1, ctype_lint))
	{
	  if (ctype_isSignedIntegral (tr))
	    {
	      hcode = FLG_LONGSIGNEDINTEGRAL;
	    }
	  else if (ctype_isSignedIntegral (tr))
	    {
	      ;
	    }
	  else
	    {
	      hcode = FLG_LONGINTEGRAL;
	    }
	}
      else if (ctype_isInt (ut1))
	{
	  hcode = FLG_MATCHANYINTEGRAL;
	}
      else
	{
	  ;
	}
    }
  else
    {
      ;
    }

  if (hcode == INVALID_FLAG)
    {
      DPRINTF (("[%s] %s - %s / %s",
		ctype_unparse (ut1),
		bool_unparse (ctype_isEnum (ut1)),
		bool_unparse (ctype_isEnum (ctype_realType (ut1))),
		bool_unparse (ctype_isInt (ut2))));

      if (ctype_isAbstract (ut1) && !ctype_isAbstract (ut2))
	{
	  uentry ue1 = usymtab_getTypeEntry (ctype_typeId (ut1));
	  ctype ct = uentry_getType (ue1);
	  
	  if (ctype_match (ct, ut2))
	    {
	      code = FLG_ABSTRACT;
	      hint = message ("Underlying types match, but %s is an "
			      "abstract type that is not accessible here.",
			      ctype_unparse (t1));
	    }
	}
      else if (ctype_isAbstract (ut2) && !ctype_isAbstract (ut1))
	{
	  uentry ue = usymtab_getTypeEntry (ctype_typeId (ut2));
	  ctype ct = uentry_getType (ue);
	  
	  if (ctype_match (ct, ut1))
	    {
	      if (ctype_isNumAbstract (ut2)) 
		{
		  if (exprNode_isNumLiteral (e1))
		    {
		      code = FLG_NUMABSTRACTLIT;
		      hint = message ("Underlying types match, but %s is a "
				      "numabstract type that is not accessible here. "
				      "(Use +numabstractlit to allow numeric literals "
				      "to be used as numabstract type values.)",
				      ctype_unparse (t2));
		    }
		  else
		    {
		      code = FLG_NUMABSTRACT;
		      hint = message ("Underlying types match, but %s is a "
				      "numabstract type that is not accessible here.",
				      ctype_unparse (t2));
		    }
		}
	      else
		{
		  code = FLG_ABSTRACT;
		  hint = message ("Underlying types match, but %s is an "
				  "abstract type that is not accessible here.",
				  ctype_unparse (t2));
		}
	    }
	}
      else
	{
	  ; /* Not an abstract mismatch. */
	}
				       

      if (hcode == INVALID_FLAG)
	{
	  if ((ctype_isEnum (ut1) && ctype_isInt (ut2))
	      || (ctype_isEnum (ut2) && ctype_isInt (ut1))) 
	    {
	      hcode = FLG_ENUMINT;
	    }
	  else if ((ctype_isEnum (ut1) && ctype_isInt (ut2))
		   || (ctype_isEnum (ut2) && ctype_isInt (ut1))) 
	    {
	      hcode = FLG_ENUMINT;
	    }
	  else if ((ctype_isSignedChar (ut1) && ctype_isUnsignedChar (ut2))
		   || (ctype_isUnsignedChar (ut1) && ctype_isSignedChar (ut2)))
	    {
	      hcode = FLG_CHARUNSIGNEDCHAR;
	    }
	  else if (ctype_isNumeric (ut1) && ctype_isNumeric (ut2)) 
	    {
	      hcode = FLG_RELAXTYPES;
	      DPRINTF (("Setting relax types!"));
	    }
	  else
	    {
	      DPRINTF (("No special type rule: %s / %s", ctype_unparse (ut1),
			ctype_unparse (ut2)));
	    }
	}
    }

  if (cstring_isDefined (hint))
    {
      if (!context_suppressFlagMsg (ocode, fl))
	{
	  return xllgenhinterror (srcFile, srcLine, code, s, hint, fl);
	}
      else
	{
	  cstring_free (s);
	  cstring_free (hint);
	  return FALSE;
	}
    }
  else
    {
      if (hcode != INVALID_FLAG && hcode != ocode)
	{
	  code = hcode;
	}
      
      if (llgenerroraux (ocode, srcFile, srcLine, s,
			 flagcodeHint (code), fl, TRUE, FALSE))
	{
	  if (code != ocode) 
	    {
	      if (context_flagOn (code, fl))
		{
		  /* The flag is alreay set, something buggy in the flag code */
		  llcontbug (message ("No hint available, flag %s is already set.",
				      flagcode_unparse (code)));
		}
	      else
		{
		  llshowhint (code);
		}
	  } 
	  else
	    {
	      llsuppresshint ('-', code);
	    }

	  flagcode_recordError (code);
	  return TRUE;
	}

      return FALSE;
    }
}

bool
xllgentypeerror (char *srcFile, int srcLine,
		 ctype t1, exprNode e1, ctype t2, exprNode e2,
		 /*@only@*/ cstring s, fileloc fl)
{
  return llgentypeerroraux (srcFile, srcLine, FLG_TYPE, t1, e1, t2, e2, s, fl);
}

bool
xllgenformattypeerror (char *srcFile, int srcLine,
		       ctype t1, exprNode e1, ctype t2, exprNode e2,
		       /*@only@*/ cstring s, fileloc fl)
{
  if (!context_suppressFlagMsg (FLG_FORMATTYPE, fl))
    {
      if (ctype_isInt (t1)
	  && ctype_isNumAbstract (t2))
	{
	  if (!context_suppressFlagMsg (FLG_NUMABSTRACTPRINT, fl))
	    {
	      return llgentypeerroraux (srcFile, srcLine, FLG_NUMABSTRACTPRINT, t1, e1, t2, e2, s, fl);
	    }
	  else
	    {
	      return FALSE;
	    }
	}
      else
	{
	  return llgentypeerroraux (srcFile, srcLine, FLG_FORMATTYPE, t1, e1, t2, e2, s, fl);
	}
    }
  else
    {
      cstring_free (s);
      return FALSE;
    }
}

bool
xllgenerror (char *srcFile, int srcLine, flagcode o, /*@only@*/ cstring s, fileloc fl)
{
  if (llgenerroraux (o, srcFile, srcLine, s, flagcodeHint (o), fl, TRUE, FALSE))
    {
      llnosuppresshint (o);
      flagcode_recordError (o);
      closeMessage ();
      return TRUE;
    }
  else
    {
      flagcode_recordSuppressed (o);
      return FALSE;
    }
}

bool
xllgenhinterror (char *srcFile, int srcLine,
		 flagcode o, /*@only@*/ cstring s, /*@only@*/ cstring hint,
		 fileloc fl)
{
  if (!context_suppressFlagMsg (o, fl))
    {
      if (llgenerroraux (o, srcFile, srcLine, s, hint, fl, TRUE, FALSE))
	{
	  flagcode_recordError (o);

	  if (context_getFlag (FLG_HINTS))
	    {
	      llgenhint (hint);
	    }
	  else
	    {
	      cstring_free (hint);
	    }

	  closeMessage ();
	  return TRUE;
	}

      cstring_free (hint);
    }
  else
    {
      cstring_free (hint);
      cstring_free (s);
    }

  flagcode_recordSuppressed (o);
  return FALSE;
}

static bool
llrealerror (flagcode code, char *srcFile, int srcLine, /*@only@*/ cstring s, /*@temp@*/ cstring addtext, fileloc fl)
{
  return (llgenerrorreal (code, srcFile, srcLine, s, addtext, fl, TRUE, FALSE));
}

static bool
llgenerroraux (flagcode code,
	       char *srcFile, int srcLine,
	       /*@only@*/ cstring s, 
	       cstring addtext,
	       fileloc fl, bool iserror, bool indent)
{
  if (context_inSuppressZone (fl))
    {
      cstring_free (s);
      return FALSE;
    }
  
  if (llgenerrorreal (code, srcFile, srcLine, s, addtext, fl, iserror, indent)) {
    return TRUE;
  } else {
    return FALSE;
  }
}

bool
xllforceerror (char *srcFile, int srcLine, 
	       flagcode code, /*@only@*/ cstring s, fileloc fl)
{
  flagcode_recordError (code);

  if (llgenerrorreal (code, srcFile, srcLine, s, cstring_undefined, fl, TRUE, FALSE)) {
    closeMessage ();
    return TRUE;
  } else {
    return FALSE;
  }
}

static void generateCSV (flagcode code, cstring s, cstring addtext, fileloc fl)
{

  if (g_csvstream != NULL) {
    /* Warning, Flag Code, Flag Name, Priority, File, Line, Column, Warning Text, Additional Text */
    fprintf (g_csvstream, "%d,%d,%s,%d,%s,%d,%d,\"%s\"",
	     context_numErrors (),
	     (int) code, /* flag code */
	     cstring_toCharsSafe (flagcode_unparse (code)), /* flag name */
	     flagcode_priority (code), /* priority */
	     cstring_toCharsSafe (fileloc_outputFilename (fl)),
	     fileloc_lineno (fl),
	     fileloc_column (fl),
	     cstring_toCharsSafe (s));

    if (cstring_isDefined (addtext)) {
      fprintf (g_csvstream, ",\"%s\"\n", cstring_toCharsSafe (addtext));
    } else {
      fprintf (g_csvstream, "\n");
    }
  }
}

static bool
llgenerrorreal (flagcode code, char *srcFile, int srcLine, 
		/*@only@*/ cstring s, 
		cstring addtext,
		fileloc fl, bool iserror, bool indent)
{
  cstring flstring;

  /* duplicate message (rescanning a header file */

  if (!messageLog_add (context_messageLog (), fl, s))
    {
      DPRINTF (("Duplicate message suppressed! %s / %s",
		fileloc_unparse (fl), s));
      cstring_free (s);
      return FALSE;
    }

  /*
  ** If herald has not been displayed, display it before the first message.
  */

  showHerald ();

  if (iserror) context_hasError ();

  if (context_unlimitedMessages ())
    {
      ;
    }
  else
    {
      /*
      ** suppress excessive messages:
      **    check up to ':'
      **
      */

      char *sc = cstring_toCharsSafe (s);
      char *tmpmsg = strchr (sc, ':');

      if (tmpmsg == NULL)
	{
	  tmpmsg = sc;
	}
      else
	{
	  char *savechar = tmpmsg;
	  *tmpmsg = '\0';
	  tmpmsg = sc;
	  *savechar = ':';
	}

      if (cstring_equal (s_lastmsg, cstring_fromChars (tmpmsg)))
	{
	  s_mcount++;
	  if (s_mcount == (context_getLimit () + 1))
	    {
	      limitmessage (s, fl);
	      return FALSE;
	    }

	  if (s_mcount > (context_getLimit ()))
	    {
	      cstring_free (s);
	      return FALSE;
	    }
	}
      else
	{
	  cleanupMessages ();
	  s_mcount = 0;
	  cstring_free (s_lastmsg);
	  s_lastmsg = cstring_fromCharsNew (tmpmsg);
	}
    }

  DPRINTF (("Here..."));

  if (context_hasAliasAnnote ())
    {
      char *sc = cstring_toCharsSafe (s);
      char *fcolon = strchr (sc, ':');
      cstring a = context_getAliasAnnote ();


      if (fcolon == NULL)
	{
	  s = message ("%q (%q)", s, a);
	}
      else
	{
	  cstring afterColon;

	  *fcolon = '\0';
	  afterColon = cstring_fromCharsNew (fcolon + 1);

	  s = message ("%q (%q):%q", s, a, afterColon);
	}
          }

  if (context_hasMessageAnnote ())
    {
      char *fcolon = strchr (cstring_toCharsSafe (s), ':');


      if (fcolon == NULL)
	{
	  /*@-dependenttrans@*/ /* s becomes dependent for fcolon */
	  s = message ("%q (%q)", s, context_getMessageAnnote ());
	  /*@=dependenttrans@*/
	}
      else
	{
	  cstring afterColon;

	  *fcolon = '\0';
	  afterColon = cstring_fromCharsNew (fcolon + 1);

	  /*@-dependenttrans@*/ /* s becomes dependent for fcolon */
	  s = message ("%q (%q):%q", s,
		       context_getMessageAnnote (), afterColon);
	  /*@=dependenttrans@*/
	}
     }

  context_setNeednl ();
  prepareMessage ();

  if (context_showFunction ())
    {
      cstring fname = fileloc_unparseFilename (g_currentloc);

      if (context_inIterDef ())
	{
	  fprintf (g_warningstream, "%s: (in iter %s)\n",
		   cstring_toCharsSafe (fname),
		   cstring_toCharsSafe (context_inFunctionName ()));
	}
      else if (context_inIterEnd ())
	{
	  fprintf (g_warningstream, "%s: (in iter finalizer %s)\n",
		   cstring_toCharsSafe (fname),
		   cstring_toCharsSafe (context_inFunctionName ()));
	}
      else if (context_inMacro ())
	{
	  fprintf (g_warningstream, "%s: (in macro %s)\n", cstring_toCharsSafe (fname),
		   cstring_toCharsSafe (context_inFunctionName ()));
	}
      else
	{
	  fprintf (g_warningstream, "%s: (in function %s)\n",
		   cstring_toCharsSafe (fname),
		   cstring_toCharsSafe (context_inFunctionName ()));
	}

      cstring_free (fname);
      context_setShownFunction ();
    }

  flstring = fileloc_unparse (fl);
  s_lastfileloclen = cstring_length (flstring);

  generateCSV (code, s, addtext, fl);

  if (indent)
    {
      printError (g_warningstream, message ("   %q: %q", flstring, s));
    }
  else
    {
      printError (g_warningstream, message ("%q: %q", flstring, s));
    }

  showSourceLoc (srcFile, srcLine);
	     
  return TRUE;
}

/*
** printMessage
**
** message contains no '\n'
**    message fits in one line: print it
**    message fits in two lines with 3-space indent after fileloc: print it
**    split line with 5-space indent from left margin: print it
**
*/

static
void printMessage (FILE *stream, /*@only@*/ cstring s)
{
  printIndentMessage (stream, s, 0);
}

static
void printIndentMessage (FILE *stream, /*@only@*/ cstring sc, int indent)
{
  static bool inbody = FALSE;
  int maxlen = context_getLineLen ();
  char *s = cstring_toCharsSafe (sc);
  char *olds = NULL;

  llassertprotect (!inbody);
  inbody = TRUE;

  do
    {
      char *t = NULL;
      char *st = s;

      llassertprotect (st != olds);
      olds = st;
      mstring_split (&st, &t, maxlen, &indent);
      fprintf (stream, "%s\n", st);
      llassertprotect (t != s);
      s = t;
    } while (s != NULL) ;

  cstring_free (sc);
  inbody = FALSE;
}

static
void printError (FILE *stream, /*@only@*/ cstring sc)
{
  int maxlen = context_getLineLen ();
  size_t nspaces = s_lastfileloclen + 5;
  int nextlen = maxlen - size_toInt (nspaces);
  size_t len = cstring_length (sc);
  int indent = 0;
  char *s = cstring_toCharsSafe (sc);
  char *os = s;
  char *t = NULL;

  DPRINTF (("Print error: [%s]", sc));

  if (size_toInt (len) < (maxlen + nextlen) && (strchr (s, '\n') == NULL))
    {
      mstring_split (&s, &t, maxlen, &indent);

      fprintf (stream, "%s\n", s);

      if (t != NULL)
	{
	  len = mstring_length (t);

	  if (size_toInt (len) < (maxlen - 3) && (strchr (t, '\n') == NULL)
	      && size_toInt (len) > (nextlen - 1))
	    {
	      fprintf (stream, "    %s\n", t);
	    }
	  else
	    {
	      char *spaces = (char *) dmalloc ((nspaces + 1) * sizeof (*spaces));
	      int i;

	      for (i = 0; i < size_toInt (nspaces); i++)
		{
		  spaces[i] = ' ';
		}

	      spaces[nspaces] = '\0';

	      while (t != NULL)
		{
		  char *st = t;
		  mstring_split (&st, &t, nextlen, &indent);
		  fprintf (stream, "%s%s\n", spaces, st);
		}

	      sfree (spaces);
	    }
	}
    }
  else
    {
      DPRINTF (("Here 1: [%s]", sc));

      if (size_toInt (len) < (maxlen + maxlen - 1) && (strchr (s, '\n') != NULL))
	{
	  nspaces = ((maxlen + maxlen - 1) - len) / 2;

	  if (nspaces < 1) nspaces = 1;

	  nextlen = size_toInt (maxlen - nspaces);

	  mstring_split (&s, &t, maxlen, &indent);

	  fprintf (stream, "%s\n", s);

	  if (t != NULL)
	    {
	      char *spaces = (char *) dmalloc ((nspaces + 1) * sizeof (*spaces));
	      int i;

	      for (i = 0; i < size_toInt (nspaces); i++)
		{
		  spaces[i] = ' ';
		}

	      spaces[nspaces] = '\0';

	      while (t != NULL)
		{
		  char *st = t;

		  mstring_split (&st, &t, nextlen, &indent);
		  fprintf (stream, "%s%s\n", spaces, st);
		}

	      sfree (spaces);
	    }
	}
      else
	{
	  nspaces = 4;
	  nextlen = size_toInt (maxlen - nspaces);

	  DPRINTF (("Here 2: [%s]", s));
	  mstring_split (&s, &t, maxlen, &indent);
	  DPRINTF (("Here 3: [%s] [%s]", s, t));

	  fprintf (stream, "%s\n", s);

	  if (t != NULL)
	    {
  	      char *spaces = (char *) dmalloc ((nspaces + 1) * sizeof (*spaces));
	      size_t i;

	      for (i = 0; i < nspaces; i++)
		{
		  spaces[i] = ' ';
		}

	      spaces[nspaces] = '\0';

	      while (t != NULL)
		{
		  char *st = t;
		  DPRINTF (("Loop: [%s]", t));
		  mstring_split (&st, &t, nextlen, &indent);
		  DPRINTF (("Split: [%s] [%s]", st, t));
		  fprintf (stream, "%s%s\n", spaces, st);
		  DPRINTF (("Next..."));
		}

	      sfree (spaces);
	    }
	}
    }

  DPRINTF (("Done"));
  sfree (os);
}

void
xllfatalbug (char *srcFile, int srcLine, /*@only@*/ cstring s)
{
  prepareMessage ();
  printError (g_errorstream, message ("%q: *** Fatal bug: %q",
				      fileloc_unparse (g_currentloc), s));
  showSourceLoc (srcFile, srcLine);
  printCodePoint ();
  printBugReport ();
  llexit (LLFAILURE);
}

void
lclfatalbug (char *msg)
{
  prepareMessage ();
  printError (g_errorstream,
	      message ("*** Fatal Bug: %s", cstring_fromChars (msg)));
  printCodePoint ();
  printBugReport ();
  llexit (LLFAILURE);
}

void
checkParseError (void)
{
  if (fileloc_withinLines (lastparseerror, g_currentloc, 10))
    {
      llfatalerror (message ("%q: Cannot recover from parse error.",
			     fileloc_unparse (g_currentloc)));
    }
}

void llbugaux (cstring file, int line, /*@only@*/ cstring s)
{
  /*@unchecked@*/
  static int numbugs = 0;
  static bool inbug = FALSE;

  if (inbug)
    {
      cstring temps = fileloc_unparseRaw (file, line);

      fprintf (g_errorstream,
	       "%s: Recursive bug detected: %s\n",
	       cstring_toCharsSafe (temps),
	       cstring_toCharsSafe (s));
      cstring_free (temps);

      llexit (LLFAILURE);
    }

  inbug = TRUE;

  prepareMessage ();

  if (fileloc_withinLines (lastparseerror, g_currentloc, 7))
    {
      llfatalerror (message ("%q: Cannot recover from parse error.",
			     fileloc_unparse (g_currentloc)));
    }

  (void) fflush (g_warningstream);

  printError (g_errorstream,
	      message ("%q: *** Internal Bug at %q: %q [errno: %d]",
		       fileloc_unparse (g_currentloc),
		       fileloc_unparseRaw (file, line),
		       s, errno));
  
  /* printCodePoint (); no longer useful */

  llflush ();
  
  /*
  ** This is confusing, and hardly ever useful.

  if (errno != 0)
    {
      perror ("Possible system error diagnostic: ");
    }

  **
  */

  printBugReport ();
  llflush ();

  numbugs++;

  if (numbugs > context_getBugsLimit () && fileloc_withinLines (lastbug, g_currentloc, 2))
    {
      llfatalerror
	(message ("%q: Cannot recover from last bug. "
		  "(If you really want Splint to try to continue, use -bugslimit <n>.)",
		  fileloc_unparse (g_currentloc)));
    }
  
  fprintf (g_errorstream, "       (attempting to continue, results may be incorrect)\n");
  fileloc_free (lastbug);
  lastbug = fileloc_copy (g_currentloc);
  closeMessage ();
  inbug = FALSE;
}

void
lclbug (/*@only@*/ cstring s)
{
  prepareMessage ();
  printError (g_errorstream, message ("*** Internal Bug: %q", s));
  printCodePoint ();
  printBugReport ();
  fprintf (g_errorstream, "       (attempting to continue, results may be incorrect)\n");
  closeMessage ();
}

void
xllfatalerror (char *srcFile, int srcLine, cstring s)
{
  prepareMessage ();
  printError (g_errorstream, s);
  printError (g_errorstream, cstring_makeLiteral ("*** Cannot continue."));
  showSourceLoc (srcFile, srcLine);
  llexit (LLFAILURE);
}

void
xllfatalerrorLoc (char *srcFile, int srcLine, /*@only@*/ cstring s)
{
  prepareMessage ();
  (void) fflush (g_warningstream);
  printError (g_errorstream, message ("%q: %q", fileloc_unparse (g_currentloc), s));
  printError (g_errorstream, cstring_makeLiteral ("*** Cannot continue."));
  showSourceLoc (srcFile, srcLine);
  (void) fflush (g_warningstream);
  llexit (LLFAILURE);
}

bool
lclHadError (void)
{
  return (s_lclerrors > 0);
}

bool
lclHadNewError (void)
{
  static int lastcall = 0;

  if (s_lclerrors > lastcall)
    {
      lastcall = s_lclerrors;
      return TRUE;
    }
  else
    {
      return FALSE;
    }
}

int
lclNumberErrors (void)
{
  return (s_lclerrors);
}

void
xlclerror (char *srcFile, int srcLine, ltoken t, /*@only@*/ cstring msg)
{
  s_lclerrors++;

  if (ltoken_getCode (t) != NOTTOKEN)
    {
      cstring loc = ltoken_unparseLoc (t);
      s_lastfileloclen = cstring_length (loc);

      printError (g_warningstream, message ("%q: %q", loc, msg));
      showSourceLoc (srcFile, srcLine);
    }
  else
    {
      printError (g_warningstream, msg);
      showSourceLoc (srcFile, srcLine);
    }
}

void
lclplainerror (/*@only@*/ cstring msg)
{
  s_lclerrors++;
  printError (g_warningstream, msg);
}

void
lclfatalerror (ltoken t, /*@only@*/ cstring msg)
{
  if (ltoken_getCode (t) != NOTTOKEN)
    {
      cstring loc = ltoken_unparseLoc (t);
      s_lastfileloclen = cstring_length (loc);
      printError (g_errorstream, message ("%q: %q", loc, msg));
    }
  else
    {
      printError (g_errorstream, msg);
    }

  printError (g_errorstream, cstring_makeLiteral ("*** Cannot continue"));
  llexit (LLFAILURE);
}

void
lclplainfatalerror (/*@only@*/ cstring msg)
{
  (void) fflush (g_warningstream);
  printError (g_errorstream, message ("*** Cannot continue: %q", msg));
  llexit (LLFAILURE);
}

void
lclRedeclarationError (ltoken id)
{
  cstring s = ltoken_getRawString (id);


  if (usymtab_existsEither (s))
    {
      uentry le = usymtab_lookupEither (s);

      lclerror (id, message ("Respecification of %s", s));
      llgenindentmsg (message ("Previous specification of %q",
			       uentry_getName (le)),
		uentry_whereSpecified (le));
    }
  else
    {
      lclerror (id, message ("Identifier redeclared: %s", s));
    }
}

void genppllerror (flagcode code, /*@only@*/ cstring s)
{
  if (context_inSuppressZone (g_currentloc))
    {
      cstring_free (s);
    }
  else
    {
      if (context_getFlag (code))
	{
	  if (s_scanOpen)
	    {
	      displayScanClose ();
	    }

	  llerror (code, s);

	  if (code != FLG_PREPROC)
	    {
	      llsuppresshint ('-', code);
	    }

	  if (!context_isInCommandLine ())
	    {
	      displayScanOpen (cstring_makeLiteral ("< more preprocessing ."));
	    }
	}
      else
	{
	  cstring_free (s);
	}
    }
}

void genppllerrorhint (flagcode code, /*@only@*/ cstring s,
		       /*@only@*/ cstring hint)
{
  if (context_inSuppressZone (g_currentloc))
    {
      cstring_free (s);
      cstring_free (hint);
    }
  else
    {
      if (context_getFlag (code))
	{
	  generateCSV (code, s, hint, g_currentloc);
	  prepareMessage ();
	  context_clearPreprocessing ();
	  llerror (code, s);
	  llgenhint (hint);
	  context_setPreprocessing ();
	  closeMessage ();
	}
      else
	{
	  cstring_free (s);
	  cstring_free (hint);
	}
    }
}

void ppllerror (/*@only@*/ cstring s)
{
  genppllerror (FLG_PREPROC, s);
}

void pplldiagmsg (cstring s)
{
  if (!context_isInCommandLine ())
    {
      if (s_scanOpen) 
	{
	  displayScanClose ();
	}

      lldiagmsg (s);
      displayScanOpen (cstring_makeLiteral ("< more preprocessing ."));
    }
  else
    {
      lldiagmsg (s);
    }
}

void loadllmsg (cstring s)
{
  displayScanClose ();
  lldiagmsg (s);
  displayScanOpen (cstring_makeLiteral ("< ."));
}

static void llreportparseerror (/*@only@*/ cstring s)
{
  if (fileloc_withinLines (lastparseerror, g_currentloc, 5))
    {
      cstring_free (s);
    }
  else
    {
      llerror (FLG_SYNTAX, s);

      fileloc_free (lastparseerror);
      lastparseerror = fileloc_copy (g_currentloc);
    }
}

bool xcppoptgenerror (char *srcFile, int srcLine,
		      flagcode o,
		      /*@only@*/ cstring s,
		      cppReader *pfile)
{
  bool res = FALSE;
  fileloc loc = cppReader_getLoc (pfile);

  if (context_flagOn (o, loc))
    {
      if (xlloptgenerror (srcFile, srcLine, o, s, loc))
	{
	  cppReader_printContainingFiles (pfile);
	  res = TRUE;
	}
    }
  else
    {
      cstring_free (s);
    }

  fileloc_free (loc);

  return res;
}

bool xlloptgenerror (char *srcFile, int srcLine, 
		     flagcode o, /*@only@*/ cstring s, fileloc loc)
{
  DPRINTF (("xllopt: %s", s));

  if (llrealerror (o, srcFile, srcLine, s, flagcodeHint (o), loc))
    {
      DPRINTF (("Here we are!"));
      llsuppresshint ('-', o);
      closeMessage ();
      flagcode_recordError (o);
      return TRUE;
    }
  else
    {
      DPRINTF (("Suppressed!"));
      flagcode_recordSuppressed (o);
      return FALSE;
    }
}

bool xoptgenerror2 (char *srcFile, int srcLine,
		    flagcode f1, flagcode f2, /*@only@*/ cstring s, fileloc loc)
{
  if (context_suppressFlagMsg (f1, loc))
    {
      flagcode_recordSuppressed (f1);
      cstring_free (s);
    }
  else
    {
      if (context_suppressFlagMsg (f2, loc))
	{
	  flagcode_recordSuppressed (f2);
	  cstring_free (s);
	}
      else
	{
	  if (llrealerror (f1, srcFile, srcLine, s, flagcodeHint (f1), loc))
	    {
	      llsuppresshint2 ('-', f1, f2);
	      flagcode_recordError (f2);
	      closeMessage ();
	      return TRUE;
	    }
	  else
	    {
	      flagcode_recordSuppressed (f2);
	    }
	}
    }

  return FALSE;
}

bool xoptgenerror2n (char *srcFile, int srcLine,
		     flagcode f1, flagcode f2, /*@only@*/ cstring s, fileloc loc)
{

  if (context_suppressFlagMsg (f1, loc))
    {
      flagcode_recordSuppressed (f1);
      cstring_free (s);
    }
  else
    {
      if (context_suppressNotFlagMsg (f2, loc))
	{
	  flagcode_recordSuppressed (f2);
	  cstring_free (s);
	}
      else
	{
	  if (llrealerror (f1, srcFile, srcLine, s, flagcodeHint (f2), loc))
	    {
	      llsuppresshint ('+', f2);
	      flagcode_recordError (f2);
	      closeMessage ();
	      return TRUE;
	    }

	  flagcode_recordSuppressed (f2);
	}
    }
  return FALSE;
}

bool xllnoptgenerror (char *srcFile, int srcLine,
		      flagcode o, /*@only@*/ cstring s, fileloc loc)
{
  if (llrealerror (o, srcFile, srcLine, s, flagcodeHint (o), loc))
    {
      llsuppresshint ('+', o);
      flagcode_recordError (o);
      closeMessage ();
      return TRUE;
    }

  flagcode_recordSuppressed (o);
  return FALSE;
}

void xllparseerror (char *srcFile, int srcLine, cstring s)
{
  if (context_getFlag (FLG_TRYTORECOVER))
    {
      parseerrorcount++;

      if (parseerrorcount > GIVEUPPARSE)
	{
	  if (cstring_isDefined (s))
	    {
	      xllfatalerror (srcFile, srcLine,
			     message ("%q: Parse Error: %q.  "
				      "Too many errors, giving up.",
				      fileloc_unparse (g_currentloc), s));
	    }
	  else
	    {
	      xllfatalerror (srcFile, srcLine,
			     message ("%q: Parse Error.  Too many errors, giving up.",
				      fileloc_unparse (g_currentloc)));
	    }
	}
      else
	{
	  if (cstring_isDefined (s))
	    {
	      llreportparseerror (message ("Parse Error: %q. Attempting to continue.",
					   s));
	      showSourceLoc (srcFile, srcLine);
	    }
	  else
	    {
	      llreportparseerror (message ("Parse Error. Attempting to continue."));
	      showSourceLoc (srcFile, srcLine);
	    }
	}
    }
  else
    {
      cstring msg;

      if (cstring_isDefined (s))
	{
	  msg = message ("Parse Error: %q.", s);
	}
      else
	{
	  msg = message ("Parse Error.");
	}

      xllfatalerror
	(srcFile, srcLine,
	 message ("%q: %s (For help on parse errors, "
		  "see splint -help parseerrors.)",
		  fileloc_unparse (g_currentloc), msg));
    }
}

bool xfsgenerror (char *srcFile, int srcLine,
		  flagSpec fs, /*@only@*/ cstring s, fileloc fl) 
{
  if (flagSpec_isOn (fs, fl))
    {
      flagcode firston = flagSpec_getFirstOn (fs, fl);

      if (llgenerroraux (firston, srcFile, srcLine, s, 
			 flagcodeHint (firston),
			 fl, TRUE, FALSE))
	{
	  llsuppresshint ('-', firston);
	  flagcode_recordError (firston);
	  return TRUE;
	}
      else
	{
	  flagcode_recordSuppressed (firston);
	  return FALSE;
	}
    }
  else
    {
      flagcode_recordSuppressed (flagSpec_getDominant (fs));
      cstring_free (s);
      return FALSE;
    }
}

bool doCheck (bool x, cstring pred, cstring file, int line)
{
  if (!x) {
    llbug (message ("%q: Check Failed: %s",
		    fileloc_unparseRaw (file, line),
		    pred));
  }

  return x;
}

/*@observer@*/ cstring lldecodeerror (/*@unused@*/ int errnum)
{
  char *result;

#ifndef VMS
#ifndef HAVE_STRERROR
  result = NULL;
#else
  result = strerror (errnum);
#endif
#else	/* VMS */
  /* VAXCRTL's strerror() takes an optional second argument, which only
     matters when the first argument is EVMSERR.  However, it's simplest
     just to pass it unconditionally.  `vaxc$errno' is declared in
     <errno.h>, and maintained by the library in parallel with `errno'.
     We assume that caller's `errnum' either matches the last setting of
     `errno' by the library or else does not have the value `EVMSERR'.  */

  result = strerror (errnum, vaxc$errno);
#endif

  if (result == NULL)
    {
      result = cstring_toCharsSafe (message ("undocumented I/O error: %d", errnum));
    }

  return cstring_fromChars (result);
}

void llquietbugaux (cstring s, /*@unused@*/ cstring file, /*@unused@*/ int line)
{
# if 0
# ifdef HOMEVERSION
  llflush ();
  printError (g_errorstream, message ("%q: *** Internal Bug at %q: %q [errno: %d]",
				      fileloc_unparse (g_currentloc),
				      fileloc_unparseRaw (file, line),
				      s, errno));
  printCodePoint ();
  llflush ();
# endif
# else
  cstring_free (s);
# endif
}

void llflush (void)
{
  (void) fflush (g_warningstream);
  (void) fflush (g_messagestream);
}

void displayScan (cstring msg)
{
  if (s_scanOpen)
    {
      displayScanClose ();
    }

  llassert (!s_scanOpen);

  if (context_getFlag (FLG_SHOWSCAN))
    {
      showHerald ();
      fprintf (g_messagestream, "< %s >\n", cstring_toCharsSafe (msg));
      (void) fflush (g_messagestream);
    }

  cstring_free (msg);
}

void displayScanOpen (cstring msg)
{
  if (s_scanOpen)
    {
      displayScanClose ();
    }

  llassert (!s_scanOpen);
  s_scanOpen = TRUE;

  if (context_getFlag (FLG_SHOWSCAN))
    {
      fprintf (g_messagestream, "< %s", cstring_toCharsSafe (msg));
      (void) fflush (g_messagestream);
    }

  cstring_free (msg);
}

void displayScanContinue (/*@temp@*/ cstring msg)
{
  if (context_getFlag (FLG_SHOWSCAN))
    {
      if (s_scanOpen) 
	{
	  fprintf (g_messagestream, "%s", cstring_toCharsSafe (msg));
	  (void) fflush (g_messagestream);
	}
      else
	{
	  /*
	  ** Don't call bug recursively
	  */

	  fprintf (stderr, "*** Bug: scan continue scan not open\n");
	}
    }
}

void displayScanClose (void)
{
  if (s_scanOpen)
    {
      if (context_getFlag (FLG_SHOWSCAN))
	{
	  fprintf (g_messagestream, " >\n");
	  (void) fflush (g_messagestream);
	}
    }
  else
    {
      /*
      ** Don't call bug recursively
      */
      
      fprintf (stderr, "*** Bug: scan close scan not open\n");
    }
  
  s_scanOpen = FALSE;
}


