text: /*
  ** 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
  */
  /*
  ** cprim.c
  */
  
  # include "splintMacros.nf"
  # include "basic.h"
  
  static bool cprim_isReal (cprim c)
  {
    return (cprim_isAnyReal (c));
  }
  
  static bool cprim_isNumeric (cprim c)
  {
    return (cprim_isReal (c) || cprim_isInt (c));
  }
  
  cprim
  cprim_fromInt (int i)
  {
    if (i < CTX_UNKNOWN || i > CTX_LAST)
      {
        llcontbug (message ("cprim_fromInt: out of range: %d", i));
        return CTX_UNKNOWN;
      }
    return (cprim) i;
  }
  
  
  /*
  ** not symmetric:  c1 := c2 or c2 is passed as c1
  **    (if RELAXQUALS, c1 must be "bigger" than c2)
  */
  
  static bool cprim_closeEnoughAux (cprim p_c1, cprim p_c2, bool p_deep);
  
  bool
  cprim_closeEnoughDeep (cprim c1, cprim c2) 
  {
    /*
    ** If * c2 is passed as * c1
    ** Comparison is slightly different since it is safe to pass int as long,
    ** but not to pass int * as long *!
    **
    ** For deep comparisons, +relaxquals does not permit the long/int break.
    */
  
    return cprim_closeEnoughAux (c1, c2, TRUE);
  }
  
  bool
  cprim_closeEnough (cprim c1, cprim c2)
  {
    return cprim_closeEnoughAux (c1, c2, FALSE);
  }
  
  static bool
  cprim_closeEnoughAux (cprim c1, cprim c2, bool deep)
  {
    if (c1 == c2) return TRUE;
    
    DPRINTF (("cprim close: %s / %s", cprim_unparse (c1), cprim_unparse (c2)));
  
    if (c1 == CTX_ANYINTEGRAL)
      {
        if (context_getFlag (FLG_MATCHANYINTEGRAL)
  	  || context_getFlag (FLG_IGNOREQUALS))
  	{
  	  return (cprim_isAnyInt (c2)
  		  || (cprim_isAnyChar (c2) && context_msgCharInt ()));
  	}
        else if (context_getFlag (FLG_LONGINTEGRAL))
  	{
  	  return (cprim_closeEnough (CTX_LINT, c2));
  	}
        else if (context_getFlag (FLG_LONGUNSIGNEDINTEGRAL))
  	{
  	  return (cprim_closeEnough (CTX_ULINT, c2));
  	}
        else
  	{
  	  return FALSE;
  	}
      }
  
    if (c1 == CTX_UNSIGNEDINTEGRAL)
      {
        /* We allow signed ints to match any integral if matchanyintegral is set */
        if (context_getFlag (FLG_MATCHANYINTEGRAL)) {
  	return (cprim_isAnyInt (c2)
  		|| (cprim_isAnyChar (c2) && context_msgCharInt ()));
        }
        
        if (context_getFlag (FLG_IGNOREQUALS))
  	{
  	  if (context_getFlag (FLG_IGNORESIGNS)) 
  	    {
  	      return (cprim_isAnyUnsignedInt (c2)
  		      || (cprim_isUnsignedChar (c2) && context_msgCharInt ()));
  	    }
  	  else
  	    {
  	      return (cprim_isAnyInt (c2)
  		      || (cprim_isAnyChar (c2) && context_msgCharInt ()));
  	    }
  	}
        else if (context_getFlag (FLG_LONGUNSIGNEDUNSIGNEDINTEGRAL))
  	{
  	  return (cprim_closeEnough (CTX_ULINT, c2));
  	}
        else
  	{
  	  return FALSE;
  	}
      }
  
    if (c1 == CTX_SIGNEDINTEGRAL)
      {
        /* We allow signed ints to match any integral if matchanyintegral is set */
        if (context_getFlag (FLG_MATCHANYINTEGRAL)) {
  	return (cprim_isAnyInt (c2)
  		|| (cprim_isAnyChar (c2) && context_msgCharInt ()));
        }
  
        if (context_getFlag (FLG_IGNOREQUALS))
  	{
  	  return (cprim_isAnyInt (c2)
  		  || (cprim_isAnyChar (c2) && context_msgCharInt ()));
  	}
        else if (context_getFlag (FLG_LONGSIGNEDINTEGRAL))
  	{
  	  return (cprim_closeEnough (CTX_LINT, c2));
  	}
        else
  	{
  	  return FALSE;
  	}
      }
  
    if (c2 == CTX_ANYINTEGRAL)
      {
        if (context_getFlag (FLG_MATCHANYINTEGRAL))
  	{
  	  return (cprim_isAnyInt (c1)
  		  || (cprim_isAnyChar (c1) && context_msgCharInt ()));
  	}
        else if (context_getFlag (FLG_LONGINTEGRAL))
  	{
  	  return (cprim_closeEnough (c1, CTX_LINT));
  	}
        else if (context_getFlag (FLG_LONGUNSIGNEDINTEGRAL))
  	{
  	  return (cprim_closeEnough (c1, CTX_ULINT));
  	}
        else
  	{
  	  return FALSE;
  	}
      }
  
    if (c2 == CTX_UNSIGNEDINTEGRAL)
      {
        if (context_getFlag (FLG_MATCHANYINTEGRAL))
  	{
  	  return (cprim_isAnyInt (c1)
  		  || (cprim_isAnyChar (c1) && context_msgCharInt ()));
  	}
        else if (context_getFlag (FLG_LONGUNSIGNEDUNSIGNEDINTEGRAL))
  	{
  	  return (cprim_closeEnough (c1, CTX_ULINT));
  	}
        else
  	{
  	  return FALSE;
  	}
      }
  
    if (c2 == CTX_SIGNEDINTEGRAL)
      {
        if (context_getFlag (FLG_MATCHANYINTEGRAL))
  	{
  	  return (cprim_isAnyInt (c2)
  		  || (cprim_isAnyChar (c2) && context_msgCharInt ()));
  	}
        else if (context_getFlag (FLG_LONGSIGNEDINTEGRAL))
  	{
  	  return (cprim_closeEnough (c1, CTX_LINT));
  	}
        else
  	{
  	  return FALSE;
  	}
      }
  
  
    DPRINTF (("cprim close: %s / %s", cprim_unparse (c1), cprim_unparse (c2)));
  
    if (context_getFlag (FLG_RELAXTYPES))
      {
        if (cprim_isNumeric (c1) && cprim_isNumeric (c2)) return TRUE;
      }
  
    if (context_getFlag (FLG_IGNOREQUALS))
      {
        switch (c1)
  	{
  	case CTX_CHAR:
  	case CTX_UCHAR:
  	  if (cprim_isAnyChar (c2) 
  	      || (cprim_isAnyInt (c2) && (context_msgCharInt ()))) {
  	    return TRUE;
  	  } 
  	  break;
  	case CTX_DOUBLE:
  	case CTX_FLOAT:
  	case CTX_LDOUBLE:
  	  if (c2 == CTX_DOUBLE || c2 == CTX_FLOAT || c2 == CTX_LDOUBLE) {
  	    return TRUE;
  	  }
  	  break;
  	case CTX_INT:
  	case CTX_LINT:
  	case CTX_LLINT:
  	case CTX_ULLINT:
  	case CTX_SINT:
  	case CTX_UINT:
  	case CTX_ULINT:
  	case CTX_USINT:
  	  if (cprim_isAnyInt (c2) 
  	      || (cprim_isAnyChar (c2) && context_msgCharInt ())) {
  	    return TRUE;
  	  }
  	  /*@fallthrough@*/ 
  	default:
  	  ;
  	}
      }
  
    if (context_getFlag (FLG_IGNORESIGNS))
      {
        if (c1 == CTX_UCHAR)  
  	{
  	  c1 = CTX_CHAR;
  	}
        else if (c1 == CTX_UINT)  
  	{
  	  c1 = CTX_INT;
  	}
        else if (c1 == CTX_ULINT) 
  	{
  	  c1 = CTX_LINT;
  	}
        /* 2001-06-10: This fix provided by Jim Zelenka: */
        else if (c1 == CTX_ULLINT) 
  	{
  	  c1 = CTX_LLINT;
  	}
        /* End fix */
        else if (c1 == CTX_USINT)  
  	{
  	  c1 = CTX_SINT;
  	}
        else
  	{
  	  ;
  	}
        
        if (c2 == CTX_UCHAR)  
  	{
  	  c2 = CTX_CHAR;
  	}
        else if (c2 == CTX_UINT)   
  	{
  	  c2 = CTX_INT;
  	}
        else if (c2 == CTX_ULINT) 
  	{
  	  c2 = CTX_LINT;
  	}
        /* 2001-06-10: This fix provided by Jim Zelenka: */
        else if (c2 == CTX_ULLINT)
  	{
  	  c2 = CTX_LLINT;
  	}
        /* End fix */
        else if (c2 == CTX_USINT)  
  	{
  	  c2 = CTX_SINT;
  	}
        else
  	{
  	  ;
  	}
      }
  
    if (c1 == c2) return TRUE;
    
    if (context_getFlag (FLG_FLOATDOUBLE))
      {
        if (c1 == CTX_FLOAT && c2 == CTX_DOUBLE) 
  	{
  	  return TRUE;
  	}
        if (c2 == CTX_FLOAT && c1 == CTX_DOUBLE)
  	{
  	  return TRUE;
  	}
      }
    
    DPRINTF (("cprim close: %s / %s", cprim_unparse (c1), cprim_unparse (c2)));
    
    if (!deep && context_getFlag (FLG_RELAXQUALS))
      {
        switch (c1)
  	{
  	case CTX_DOUBLE:
  	  return (c2 == CTX_FLOAT);
  	case CTX_LDOUBLE:
  	  return (c2 == CTX_DOUBLE || c2 == CTX_FLOAT);
  	case CTX_SINT:
  	  return ((c2 == CTX_CHAR && context_msgCharInt ()) 
  		  || (c2 == CTX_INT && context_msgShortInt ())
  		  || (c2 == CTX_LINT && context_msgShortInt () && context_msgLongInt ()));
  
  	case CTX_INT:
  	  return ((c2 == CTX_SINT
  		   || (cprim_isAnyChar (c2) && context_msgCharInt ())
  		   || (c2 == CTX_LINT && context_msgLongInt ())));
  
  	case CTX_LLINT:
  	  return (c2 == CTX_SINT
  		  || c2 == CTX_INT 
  		      || c2 == CTX_LINT
  		  || (cprim_isAnyChar (c2) && context_msgCharInt ()));
  	case CTX_ULLINT:
  	  return (c2 == CTX_USINT
  		  || c2 == CTX_UINT 
  		  || c2 == CTX_ULINT
  		  /* 2001-06-10: This fix provided by Jim Zelenka: */
  		  || (cprim_isAnyChar (c2) && context_msgCharInt ()));
  	case CTX_LINT:
  	  return (c2 == CTX_SINT
  		  || c2 == CTX_INT 
  		  || (cprim_isAnyChar (c2) && context_msgCharInt ()));
  	case CTX_UINT:
  	  return (c2 == CTX_USINT 
  		  || (c2 == CTX_UCHAR && context_msgCharInt ()));
  	case CTX_USINT:
  	  return (c2 == CTX_UCHAR && context_msgCharInt ());
  	case CTX_ULINT:
  	  /* 2001-06-10: This fix provided by Jim Zelenka: */
  	  return (c2 == CTX_UINT || c2 == CTX_USINT
  		  || (c2 == CTX_UCHAR && context_msgCharInt()));
  	case CTX_UCHAR:
  	  return (c2 == CTX_UINT && context_msgCharInt ());
  	case CTX_CHAR:
  	  return ((c2 == CTX_INT || c2 == CTX_SINT)
  		  && context_msgCharInt ());
  	default:
  	  return FALSE;
  	}
      }
    else
      {
        switch (c1)
  	{
  	case CTX_DOUBLE:
  	case CTX_LDOUBLE:
  	  return FALSE;
  	case CTX_SINT:
  	  if (c2 == CTX_INT && context_msgShortInt ()) {
  	    return TRUE;
  	  }
  	  /*@fallthrough@*/
  	case CTX_INT:
  	  if (c2 == CTX_INT && context_msgLongInt ()) {
  	    return TRUE;
  	  }
  	  
  	  if (c2 == CTX_SINT && context_msgShortInt ()) {
  	    return TRUE;
  	  }
  	  /*@fallthrough@*/
  	case CTX_LINT:
  	  if (c2 == CTX_INT && context_msgLongInt ()) {
  	    return TRUE;
  	  }
  	  /*@fallthrough@*/
  	case CTX_LLINT:
  	  return (c2 == CTX_CHAR && context_msgCharInt ());
  	case CTX_UINT:
  	case CTX_USINT:
  	case CTX_ULINT:
  	case CTX_ULLINT:
  	  return (c2 == CTX_UCHAR && context_msgCharInt ());
  	case CTX_UCHAR:
  	  return (c2 == CTX_UINT && context_msgCharInt ());
  	case CTX_CHAR:
  	  return ((c2 == CTX_INT || c2 == CTX_SINT)
  		  && context_msgCharInt ());
  	default:
  	  return FALSE;
  	}
      }
  }
  
  /*@only@*/ cstring
  cprim_unparse (cprim c)
  {
    switch (c)
      {
      case CTX_UNKNOWN:
        return cstring_makeLiteral ("-");
      case CTX_VOID:
        return cstring_makeLiteral ("void");
      case CTX_CHAR:
        return cstring_makeLiteral ("char");
      case CTX_UCHAR:
        return cstring_makeLiteral ("unsigned char");
     case CTX_DOUBLE:
        return cstring_makeLiteral ("double");
      case CTX_LDOUBLE:
        return cstring_makeLiteral ("long double");
      case CTX_FLOAT:
        return cstring_makeLiteral ("float");
      case CTX_INT:
        return cstring_makeLiteral ("int");
      case CTX_LINT:
        return cstring_makeLiteral ("long int");
      case CTX_LLINT:
        return cstring_makeLiteral ("long long");
      case CTX_ULLINT:
        return cstring_makeLiteral ("unsigned long long");
      case CTX_SINT:
        return cstring_makeLiteral ("short int");
      case CTX_UINT:
        return cstring_makeLiteral ("unsigned int");
      case CTX_ULINT:
        return cstring_makeLiteral ("unsigned long int");
      case CTX_USINT:
        return cstring_makeLiteral ("unsigned short int");
      case CTX_UNSIGNEDINTEGRAL:
        return cstring_makeLiteral ("arbitrary unsigned integral type");
      case CTX_SIGNEDINTEGRAL:
        return cstring_makeLiteral ("arbitrary signed integral type");
      case CTX_ANYINTEGRAL:
        return cstring_makeLiteral ("arbitrary integral type");
      default:
        return cstring_makeLiteral ("unknown prim");
      }
  }
  
  bool cprim_isInt (cprim c) 
  {
    return (cprim_isAnyInt (c)
  	  || (cprim_isAnyChar (c) && context_msgCharInt ()));
  }
      
  int cprim_getExpectedBits (cprim c)
  {
    /* Any basis to these numbers?  Just guesses for now..., check ISO spec */
    switch (c)
      {
      case CTX_UNKNOWN:
        return 0;
      case CTX_VOID:
        return 0;
      case CTX_CHAR:
        return 8;
      case CTX_UCHAR:
        return 8;
     case CTX_DOUBLE:
        return 64;
      case CTX_LDOUBLE:
        return 128;
      case CTX_FLOAT:
        return 32;
      case CTX_INT:
        return 32;
      case CTX_LINT:
        return 64;
      case CTX_LLINT:
        return 128;
      case CTX_ULLINT:
        return 128;
      case CTX_SINT:
        return 8;
      case CTX_UINT:
        return 32;
      case CTX_ULINT:
        return 64;
      case CTX_USINT:
        return 8;
      case CTX_UNSIGNEDINTEGRAL:
        return 64;
      case CTX_SIGNEDINTEGRAL:
        return 64;
      case CTX_ANYINTEGRAL:
        return 64;
      default:
        return 0;
      }
  }
  