/*
** 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
*/
/*
** cscannerHelp.c - procedures for scanning C
**
** Most of this code was in cscanner.l, but moved here to separate it
** from the flex-generated code.
*/

# include "splintMacros.nf"
# include "basic.h"
# include "cscannerHelp.h"
# include "cscanner.h"
# include "cgrammar.h"
# include "osd.h"

static int lminput (void);
static int s_tokLength = 0;

static /*@owned@*/ cstring s_lastidprocessed = cstring_undefined;
static bool s_inSpecPart = FALSE;
static int s_whichSpecPart;
static char s_savechar = '\0';
static bool s_expectingMetaStateName = FALSE;
static bool s_expectingTypeName = TRUE;

struct skeyword
{
  /*@null@*/ /*@observer@*/ char *name;
  int token;
} ;

/*
** These tokens are followed by syntax that is parsed by the 
** grammar proper.
*/

static struct skeyword s_parsetable[] = {
  { "modifies", QMODIFIES } ,
  { "globals", QGLOBALS } ,
  { "alt", QALT } ,
  { "warn", QWARN } ,
  { "constant", QCONSTANT } ,
  { "function", QFUNCTION } ,
  { "iter", QITER } ,
  { "defines", QDEFINES } ,
  { "uses", QUSES } ,
  { "allocates", QALLOCATES } ,
  { "sets", QSETS } ,
  { "releases", QRELEASES } ,
  { "pre", QPRECLAUSE } ,
  { "post", QPOSTCLAUSE } ,
  { "requires", QPRECLAUSE } ,
  { "ensures", QPOSTCLAUSE } ,
  { NULL, BADTOK } 
} ;

/*
** These tokens are either stand-alone tokens, or followed by 
** token-specific text.
*/

static struct skeyword s_keytable[] = {
  { "anytype", QANYTYPE } ,
  { "integraltype", QINTEGRALTYPE } ,
  { "unsignedintegraltype", QUNSIGNEDINTEGRALTYPE } ,
  { "signedintegraltype", QSIGNEDINTEGRALTYPE } ,
  { "out", QOUT } ,
  { "in", QIN } ,
  { "only", QONLY } , 
  { "owned", QOWNED } ,
  { "dependent", QDEPENDENT } ,
  { "partial", QPARTIAL } ,
  { "special", QSPECIAL } ,
  { "truenull", QTRUENULL } ,
  { "falsenull", QFALSENULL } ,
  { "nullwhentrue", QTRUENULL } ,
  { "falsewhennull", QFALSENULL } ,
  { "keep", QKEEP } ,
  { "kept", QKEPT } ,
  { "notnull", QNOTNULL } ,
  { "abstract", QABSTRACT } ,
  { "numabstract", QNUMABSTRACT } ,
  { "concrete", QCONCRETE } ,
  { "mutable", QMUTABLE } ,
  { "immutable", QIMMUTABLE } ,
  { "unused", QUNUSED } ,
  { "external", QEXTERNAL } ,
  { "sef", QSEF } ,
  { "unique", QUNIQUE } ,
  { "returned", QRETURNED } ,
  { "exposed", QEXPOSED } ,
  { "refcounted", QREFCOUNTED } ,
  { "refs", QREFS } ,
  { "newref", QNEWREF } ,
  { "tempref", QTEMPREF } ,
  { "killref", QKILLREF } ,
  { "null", QNULL } ,
  { "relnull", QRELNULL } ,
  { "nullterminated", QNULLTERMINATED }, 
  { "isnull", QISNULL }, 
  { "MaxSet", QMAXSET},
  { "MaxRead", QMAXREAD},
  { "maxSet", QMAXSET},
  { "maxRead", QMAXREAD},
  { "reldef", QRELDEF } ,
  { "observer", QOBSERVER } ,
  { "exits", QEXITS } ,
  { "noreturn", QEXITS } ,
  { "mayexit", QMAYEXIT } ,
  { "maynotreturn", QMAYEXIT } ,
  { "trueexit", QTRUEEXIT } ,
  { "falseexit", QFALSEEXIT } ,
  { "noreturnwhentrue", QTRUEEXIT } ,
  { "noreturnwhenfalse", QFALSEEXIT } ,
  { "neverexit", QNEVEREXIT } ,
  { "alwaysreturns", QNEVEREXIT } ,
  { "temp", QTEMP } ,
  { "shared", QSHARED } ,
  { "ref", QREF } ,
  { "unchecked", QUNCHECKED } ,
  { "checked", QCHECKED } ,
  { "checkmod", QCHECKMOD } ,
  { "checkedstrict", QCHECKEDSTRICT } ,
  { "innercontinue", QINNERCONTINUE } ,
  { "innerbreak", QINNERBREAK } ,
  { "loopbreak", QLOOPBREAK } ,
  { "switchbreak", QSWITCHBREAK } ,
  { "safebreak", QSAFEBREAK } , 
  { "fallthrough", QFALLTHROUGH } ,
  { "l_fallthrou", QLINTFALLTHROUGH } , 
  { "l_fallth", QLINTFALLTHRU } ,
  { "notreached", QNOTREACHED } ,
  { "l_notreach", QLINTNOTREACHED } ,
  { "printflike", QPRINTFLIKE } ,
  { "l_printfli", QLINTPRINTFLIKE } ,
  { "scanflike", QSCANFLIKE } ,
  { "messagelike", QMESSAGELIKE } ,
  { "l_argsus", QARGSUSED } ,
  { NULL, BADTOK } 
} ;

static bool cscannerHelp_isSpecTokenRegularId (int p_tok) /*@*/;
static void cscannerHelp_advanceLine (void) /*@modifies g_currentloc, internalState@*/ ;

/*
** would be better if these weren't hard coded...
*/

static bool isArtificial (cstring s)
{
  return (cstring_equalLit (s, "modifies") 
	  || cstring_equalLit (s, "globals") 
	  || cstring_equalLit (s, "warn")
	  || cstring_equalLit (s, "alt"));
}

void cscannerHelp_swallowMacro (void)
{
  int i;
  bool skipnext = FALSE;

  while ((i = lminput ()) != EOF)
    {
      char c = (char) i;
      
      if (c == '\\')
	{
	  skipnext = TRUE;
	}
      else if (c == '\n')
	{
	  if (skipnext)
	    {
	      skipnext = FALSE;
	    }
	  else
	    {
	      reader_checkUngetc (i, c_in);
	      return;
	    }
	}
      else
	{
	  ;
	}
    }

  if (i != EOF)
    {
      reader_checkUngetc (i, c_in);
    }
}

static int commentMarkerToken (cstring s)
{
  int i = 0;
  
  while (s_parsetable[i].name != NULL) 
    {
      DPRINTF (("Try :%s:%s:", s, s_parsetable[i].name));

      if (cstring_equalLit (s, s_parsetable[i].name))
	{
	  return s_parsetable[i].token;
	}

      i++;
    }

  return BADTOK;
}

static int tokenMacroCode (cstring s)
{
  int i = 0;
  
  while (s_keytable[i].name != NULL) 
    {
      if (cstring_equalLit (s, s_keytable[i].name)) 
	{
	  if (s_keytable[i].token == QLINTFALLTHROUGH) 
	    {
	      voptgenerror
		(FLG_WARNLINTCOMMENTS,
		 cstring_makeLiteral
		 ("Traditional lint comment /*FALLTHROUGH*/ used. "
		  "Splint interprets this in the same way as most Unix lints, but it is "
		  "preferable to replace it with the /*@fallthrough@*/ "
		  "semantic comment"),
		 g_currentloc);
	      return QFALLTHROUGH;	      
	    }
	  else if (s_keytable[i].token == QLINTFALLTHRU)
	    {
	      voptgenerror 
		(FLG_WARNLINTCOMMENTS,
		 cstring_makeLiteral
		 ("Traditional lint comment /*FALLTHRU*/ used. "
		  "Splint interprets this in the same way as most Unix lints, but it is "
		  "preferable to replace it with the /*@fallthrough@*/ "
		  "semantic comment"),
		 g_currentloc);
	      return QFALLTHROUGH;
	    }
	  else if (s_keytable[i].token == QLINTNOTREACHED)
	    {
	      voptgenerror 
		(FLG_WARNLINTCOMMENTS,
		 cstring_makeLiteral
		 ("Traditional lint comment /*NOTREACHED*/ used. "
		  "Splint interprets this in the same way as most Unix lints, but it is "
		  "preferable to replace it with the /*@notreached@*/ "
		  "semantic comment."),
		 g_currentloc);
	      
	      return QNOTREACHED;
	    }
	  else if (s_keytable[i].token == QPRINTFLIKE)
	    {
	      setSpecialFunction (qual_createPrintfLike ());
	      return SKIPTOK;
	    }
	  else if (s_keytable[i].token == QLINTPRINTFLIKE)
	    {	      
	      voptgenerror 
		(FLG_WARNLINTCOMMENTS,
		 cstring_makeLiteral
		 ("Traditional lint comment /*PRINTFLIKE*/ used. "
		  "Splint interprets this in the same way as most Unix lints, but it is "
		  "preferable to replace it with either /*@printflike@*/, "
		  "/*@scanflike@*/ or /*@messagelike@*/."),
		 g_currentloc);
	      
	      setSpecialFunction (qual_createPrintfLike ());
	      return SKIPTOK;
	    }
	  else if (s_keytable[i].token == QSCANFLIKE)
	    {
	      setSpecialFunction (qual_createScanfLike ());
	      return SKIPTOK;
	    }
	  else if (s_keytable[i].token == QMESSAGELIKE)
	    {
	      setSpecialFunction (qual_createMessageLike ());
	      return SKIPTOK;
	    }
	  else if (s_keytable[i].token == QARGSUSED)
	    {
	      voptgenerror
		(FLG_WARNLINTCOMMENTS,
		 cstring_makeLiteral
		 ("Traditional lint comment /*ARGSUSED*/ used. "
		  "Splint interprets this in the same way as most Unix lints, but it is "
		  "preferable to use /*@unused@*/ annotations on "
		  "the unused parameters."),
		 g_currentloc);
	      
	      setArgsUsed ();
	      return SKIPTOK;
	    }
	  else
	    {
	      return s_keytable[i].token;
	    }
	}

      i++;
    }
  
  return BADTOK;
}

static int lminput (void)
{
  if (s_savechar == '\0')
    {
      incColumn ();
      return (cscanner_input ());
    }
  else
    {
      int save = (int) s_savechar;
      s_savechar = '\0';
      return save;
    }
}

static void lmsavechar (char c)
{
  if (s_savechar == '\0')
    {
      s_savechar = c;
    }
  else
    {
      llbuglit ("lmsavechar: override");
    }
}

int cscannerHelp_ninput (void)
{
  int c = lminput ();

  if (c != EOF && ((char)c == '\n'))
    {
      context_incLineno ();
    }

  return c;
}

static char macro_nextChar (void)
{
  static bool in_quote = FALSE, in_escape = FALSE, in_char = FALSE;
  int ic;
  char c;

  ic = lminput ();
  c = char_fromInt (ic);
  
  if (!in_quote && !in_char && (c == '\\' || c == BEFORE_COMMENT_MARKER[0]))
    {
      if (c == '\\')
	{
	  while ((c = char_fromInt (lminput ())) != '\0' && c != '\n')
	    {
	      ; /* skip to newline */
	    }
	  
	  context_incLineno ();
	  
	  if (c != '\0')
	    {
	      return macro_nextChar ();
	    }
	  else 
	    {
	      return c;
	    }
	}
      else /* if (c == '@') */
	{
	  llassert (FALSE); 

	  if (cscannerHelp_handleLlSpecial () != BADTOK)
	    {
	      llerrorlit (FLG_SYNTAX, "Macro cannot use special syntax");
	    }

	  return macro_nextChar ();
	}
    }
  else if (!in_escape && c == '\"')
    {
      in_quote = !in_quote;
    }
  else if (!in_escape && c == '\'')
    {
      in_char = !in_char;
    }
  else if ((in_quote || in_char) && c == '\\')
    {
      in_escape = !in_escape;
    }
  else if ((in_quote || in_char) && in_escape)
    {
      in_escape = FALSE;
    }
  else if (!in_quote && c == '/')
    {
      char c2;
      
      if ((c2 = char_fromInt (lminput ())) == '*')
	{
	  while (c2 != '\0')
	    {
	      while ((c2 = char_fromInt (lminput ())) != '\0'
		     && c2 != '\n' && c2 != '*')
		{
		  ;
		}
	      
	      if (c2 == '*')
		{
		  while ((c2 = char_fromInt (lminput ())) != '\0' 
			 && c2 == '*')
		    {
		      ;
		    }

		  if (c2 == '/')
		    {
		      goto outofcomment;
		    }
		}
	      else 
		{
		  llfatalerror (cstring_makeLiteral ("Macro: bad comment!"));
		}
	    }
	outofcomment:
	  return macro_nextChar ();
	}
      else
	{
          /*** putchar does not work!  why?  puts to stdio...??! ***/
          lmsavechar (c2);
	}
    }
  else
    {
      ;
    }

  return c;
}

/*
** keeps semantic comments
*/

static char macro_nextCharC (void)
{
  static bool in_quote = FALSE, in_escape = FALSE, in_char = FALSE;
  char c;

  c = char_fromInt (lminput ());

  if (!in_quote && !in_char && c == '\\')
    {
      while ((c = char_fromInt (lminput ())) != '\0' && c != '\n')
	{
	  ; /* skip to newline */
	}
      
      context_incLineno ();
      
      if (c != '\0')
	{
	  return macro_nextCharC ();
	}
      else
	{
	  return c;
	}
    }
  else if (!in_escape && c == '\"')
    {
      in_quote = !in_quote;
    }
  else if (!in_escape && c == '\'')
    {
      in_char = !in_char;
    }
  else if ((in_quote || in_char) && c == '\\')
    {
      in_escape = !in_escape;
    }
  else if ((in_quote || in_char) && in_escape)
    {
      in_escape = FALSE;
    }
  else if (!in_quote && c == '/')
    {
      char c2;
      
      if ((c2 = char_fromInt (lminput ())) == '*')
	{
	  while (c2 != '\0')
	    {
	      while ((c2 = char_fromInt (lminput ())) != '\0' 
		     && c2 != '\n' && c2 != '*')
		{
		  ;
		}
	      
	      if (c2 == '*')
		{
		  while ((c2 = char_fromInt (lminput ())) != '\0'
			 && c2 == '*')
		    {
		      ;
		    }

		  if (c2 == '/') 
		    {
		      goto outofcomment;
		    }
		}
	      else 
		{
		  llfatalerror (cstring_makeLiteral ("Macro: bad comment!"));
		}
	    }
	outofcomment:
	  return macro_nextCharC ();
	}
      else
	{
	  lmsavechar (c2);
	}
    }
  else /* normal character */
    {
      ;
    }

  return c;
}

/*
** skips whitespace (handles line continuations)
** returns first non-whitespace character
*/

static char skip_whitespace (void)
{
  char c;

  while ((c = macro_nextChar ()) == ' ' || c == '\t')
    {
      ;
    }

  return c;
}

void cscannerHelp_handleMacro (void)
{
  cstring mac = cstring_undefined;
  int macrocode;
  char c;

  while (currentColumn () > 2)
    {
      mac = cstring_appendChar (mac, ' ');
      cscannerHelp_setTokLength (-1);
    }

  c = macro_nextCharC ();

  if (c >= '0' && c <= '9')
    {
      int i;

      for (i = 0; i < (((int) (c - '0')) + 1); i++)
	{
	  mac = cstring_appendChar (mac, ' ');
	}
    }
  else
    {
      BADBRANCH;
    }

  while (((c = macro_nextCharC ()) != '\0') && (c != '\n'))
    {
      mac = cstring_appendChar (mac, c);
    }

  macrocode = tokenMacroCode (mac);

  if (macrocode == BADTOK && !isArtificial (mac))
    {
      context_addMacroCache (mac);
    }
  else
    {
      cstring_free (mac);
    }

  if (c == '\n')
    {
      context_incLineno ();
    }
}

bool cscannerHelp_handleSpecial (char *text)
{
  char *l; /* !!  = mstring_create (MAX_NAME_LENGTH); */
  int lineno = 0;
  char c;
  char *ol;
  cstring olc;

  l = mstring_copy (text + 1);
  
  while ((c = char_fromInt (lminput ())) != '\n' && c != '\0')
    {
      l = mstring_append(l, c);
    }

  ol = l;

  olc = cstring_fromChars (ol);
  
  if (cstring_equalPrefixLit (olc, "pragma"))
    {
      char *pname = mstring_create (size_fromInt (MAX_PRAGMA_LEN));
      char *opname = pname;
      char *ptr = ol + 6; /* pragma is six characters, plus space */
      int len = 0;
      
      
      /* skip whitespace */
      while (((c = *ptr) != '\0') && isspace (c))
	{
	  ptr++;
	}

      
      while (((c = *ptr) != '\0') && !isspace (c))
	{
	  len++;

	  if (len > MAX_PRAGMA_LEN)
	    {
	      break;
	    }

	  ptr++;
	  *pname++ = c;
	}

      *pname = '\0';
      
      if (len == PRAGMA_LEN_EXPAND 
	  && mstring_equal (opname, PRAGMA_EXPAND))
	{
	  cstring exname = cstring_undefined;
	  uentry ue;
	  
	  ptr++; 
	  while (((c = *ptr) != '\0') && !isspace (c))
	    {
	      exname = cstring_appendChar (exname, c);
	      ptr++;
	    }
	     
	  
	  ue = usymtab_lookupExposeGlob (exname);
	  
	  if (uentry_isExpandedMacro (ue))
	    {
	      if (fileloc_isPreproc (uentry_whereDefined (ue)))
		{
		  fileloc_setColumn (g_currentloc, 1);
		  uentry_setDefined (ue, g_currentloc);
		}
	    }

	  cstring_free (exname);
	}

      (void) cscannerHelp_handleNewLine (); /* evans 2003-10-27: pragment increments line */
    }
  else if (cstring_equalPrefixLit (olc, "ident"))
    {
      /* Some pre-processors will leave these in the code.  Ignore rest of line */
      (void) cscannerHelp_handleNewLine (); /* evans 2003-10-27: ident increments line */
    }

  /*
  ** Yuk...Win32 filenames can have spaces in them...we need to read
  ** to the matching end quote.
  */
  else if ((sscanf (ol, "line %d \"", &lineno) == 1)
	   || (sscanf (ol, " %d \"", &lineno) == 1))
    {
      char *tmp = ol;
      cstring fname;
      fileId fid;

      /*@access cstring@*/
      while (*tmp != '\"' && *tmp != '\0')
	{
	  tmp++;
	}

      llassert (*tmp == '\"');
      tmp++;
      fname = tmp;
      
      while (*tmp != '\"' && *tmp != '\0')
	{
	  tmp++;
	}

      llassert (*tmp == '\"');
      *tmp = '\0';

# if defined(OS2) || defined(WIN32)

      /*
      ** DOS-like path delimiters get delivered in pairs, something like 
      ** \"..\\\\file.h\", so we have to make it normal again. We do NOT
      ** remove the pre dirs yet as we usually specify tmp paths relative
      ** to the current directory, so tmp files would not get found in
      ** the hash table.  If this method fails we try it again later. 
      */

      {
	char *stmp = fname;
	
	/*
	** Skip past the drive marker.
	*/
	
	if (strchr (stmp, ':') != NULL)
	  {
	    stmp = strchr (stmp, ':') + 1;
	  }
	
	while ((stmp = strchr (stmp, CONNECTCHAR)) != NULL )
	  {
	    if (*(stmp+1) == CONNECTCHAR)
	      {
		memmove (stmp, stmp+1, strlen (stmp));
	      }
	    
	    stmp++;
	  }
	
	fid = fileTable_lookupBase (context_fileTable (), fname);
	if (!(fileId_isValid (fid)))
	  {
	    fname = osd_removePreDirs (fname);
	    fid = fileTable_lookupBase (context_fileTable (), fname);
	  }
      }
# else 
      fname = osd_removePreDirs (fname);
      fid = fileTable_lookupBase (context_fileTable (), fname);      
# endif
      
      if (!(fileId_isValid (fid)))
	{
          fileType ftype;

          if (context_inXHFile ())
          {
            ftype = FILE_XH; /* Files included by XH files are also XH files */
          }
          else if (fileLib_isHeader (fname))
          {
            ftype = FILE_HEADER;
          }
          else
          {
            ftype = FILE_NORMAL;
          }

          fid = fileTable_addFile (context_fileTable (), ftype, fname);
	}
      
      setFileLine (fid, lineno);
      /*@noaccess cstring@*/
    }
  else if ((sscanf (ol, "line %d", &lineno) == 1) 
	   || (sscanf (ol, " %d", &lineno) == 1))
    {
      setLine (lineno); /* next line is <cr> */
    }
  else
    {
      if (mstring_equal (ol, "")) {
	DPRINTF (("Empty pp command!"));
	/*
	** evs 2000-05-16: This is a horrible kludge, to get around a bug (well, difficulty) in the pre-processor.
	** We handle a plain # in the input file, by echoing it, and ignoring it in the post-pp-file.
	*/
	mstring_free (ol);
	(void) cscannerHelp_handleNewLine (); /* evans 2003-10-27: increments line */
	return FALSE;
      } else {
	voptgenerror
	  (FLG_UNRECOGDIRECTIVE,
	   message ("Unrecognized pre-processor directive: #%s", 
		    cstring_fromChars (ol)),
	   g_currentloc);
	(void) cscannerHelp_handleNewLine (); /* evans 2003-10-27: increments line */
      }
      
      sfree (ol);
      return FALSE; /* evans 2001-12-30: was: TRUE; */
    }

  sfree (ol);
  return FALSE;
}
  
int cscannerHelp_handleLlSpecial (void)
{ 
  bool hasnl = FALSE;
  int ic; 
  char c;
  char *s = mstring_createEmpty ();
  char *os; 
  int tok;
  int charsread = 0;
  fileloc loc;

  loc = fileloc_copy (g_currentloc);
  DPRINTF (("Handle special: %s", fileloc_unparse (loc)));

  while (((ic = cscannerHelp_ninput ()) != 0) && isalpha (ic))
    {
      c = (char) ic;
      s = mstring_append (s, c);
      charsread++;
    }

  DPRINTF (("Read: %s / %s", s, fileloc_unparse (g_currentloc)));
  os = s;

  if (charsread == 0 && ic == (int) AFTER_COMMENT_MARKER[0])
    {
      ic = cscannerHelp_ninput ();

      llassert (ic == (int) AFTER_COMMENT_MARKER[1]);
            
      if (*s == '\0')
	{
	  sfree (os);
	  fileloc_free (loc);
	  return QNOMODS; /* special token no modifications token */
	}
    }
  
  DPRINTF (("Coment marker: %s", os));
  tok = commentMarkerToken (cstring_fromChars (os));

  if (tok != BADTOK)
    {
      s_tokLength = charsread;
      sfree (os);
      fileloc_free (loc);

      s_inSpecPart = TRUE;
      s_whichSpecPart = tok;
      return tok;
    }

  DPRINTF (("Not a comment marker..."));
  /* Add rest of the comment */
  
  if (ic != 0 && ic != EOF)
    {
      c = (char) ic;
      
      s = mstring_append (s, c);
      charsread++;

      while (((ic = cscannerHelp_ninput ()) != 0) && (ic != EOF)
	     && (ic != (int) AFTER_COMMENT_MARKER[0]))
	{
	  c = (char) ic;

	  /* evans 2001-09-01 added to prevent assertion failures for uncloses syntactic comments */

	  if (c == '\n') {
	    hasnl = TRUE; /* This prevents tokLength from being set later. */
	    s_tokLength = 0; 

	    voptgenerror
	      (FLG_SYNTAX, 
	       message ("Likely parse error: syntactic comment token spans multiple lines: %s",
			cstring_fromChars (s)),
	       loc);
	  }

	  s = mstring_append (s, c);
	  charsread++;
	}
      /*@-branchstate@*/ 
    } /* spurious (?) warnings about s */
  /*@=branchstate@*/ 

  DPRINTF (("Read: %s / %s", s, fileloc_unparse (g_currentloc)));

  if (ic == (int) AFTER_COMMENT_MARKER[0]) 
    {
      int nc = cscannerHelp_ninput ();
      llassert ((char) nc ==  AFTER_COMMENT_MARKER[1]);
      charsread++;
    }

  os = s;

  while (*s == ' ' || *s == '\t' || *s == '\n') 
    {
      s++;
    }

  if (*s == '-' || *s == '+' || *s == '=') /* setting flags */
    {
      c = *s;

      while (c == '-' || c == '+' || c == '=')
	{
	  ynm set = ynm_fromCodeChar (c);
	  cstring thisflag;

	  s++;
	  
	  thisflag = cstring_fromChars (s);
	  
	  while ((c = *s) != '\0' && (c != '-') && (c != '=')
		 && (c != '+') && (c != ' ') && (c != '\t') && (c != '\n'))
	    {
	      s++;
	    }

	  *s = '\0';

	  if (!context_getFlag (FLG_NOCOMMENTS))
	    {
	      cstring flagname = thisflag;
	      flagcode fflag = flags_identifyFlag (flagname);
	      
	      if (flagcode_isSkip (fflag))
		{
		  ;
		}
	      else if (flagcode_isModeName (fflag))
		{
		  if (ynm_isMaybe (set))
		    {
		      llerror
			(FLG_BADFLAG, 
			 message 
			 ("Semantic comment attempts to restore flag %s.  "
			  "A mode flag cannot be restored.",
			  flagname));
		    }
		  else
		    {
		      context_setMode (flagname);
		    }
		}
	      else if (flagcode_isInvalid (fflag))
		{
		  voptgenerror
		    (FLG_UNRECOGFLAGCOMMENTS,
		     message ("Unrecognized option in semantic comment: %s", 
			      flagname),
		     loc);
		}
	      else if (flagcode_isGlobalFlag (fflag))
		{
		  voptgenerror
		    (FLG_BADFLAG, 
		     message 
		     ("Semantic comment attempts to set global flag %s.  "
		      "A global flag cannot be set locally.",
		      flagname),
		     loc);
		}
	      else
		{
		  context_fileSetFlag (fflag, set, loc);
		  
		  if (flagcode_hasArgument (fflag))
		    {
		      if (ynm_isMaybe (set))
			{
			  voptgenerror
			    (FLG_BADFLAG, 
			     message 
			     ("Semantic comment attempts to restore flag %s.  "
			      "A flag for setting a value cannot be restored.",
			      flagname),
			     loc);
			}
		      else
			{ /* cut-and-pastied from llmain...blecch */
			  cstring extra = cstring_undefined;
			  char *rest;
			  char *orest;
			  char rchar;
			  
			  *s = c;
			  rest = mstring_copy (s);
			  orest = rest;
			  *s = '\0';
			  
			  while ((rchar = *rest) != '\0'
				 && (isspace (rchar)))
			    {
			      rest++;
			      s++;
			    }
			  
			  while ((rchar = *rest) != '\0'
				 && !isspace (rchar))
			    {
			      extra = cstring_appendChar (extra, rchar);
			      rest++; 
			      s++;
			    }
			  s--; /* evans 2002-07-12: this was previously only in the else branch.
				  Leads to an invalid read on the true branch.
			       */

			  sfree (orest);
			  
			  if (cstring_isUndefined (extra))
			    {
			      llerror 
				(FLG_BADFLAG,
				 message
				 ("Flag %s (in semantic comment) must be followed by an argument",
				  flagcode_unparse (fflag)));

			      cstring_free (extra);
			    }
			  else
			    {
			      if (flagcode_hasNumber (fflag))
				{
				  flags_setValueFlag (fflag, extra);
				}
			      else if (flagcode_hasChar (fflag))
				{
				  flags_setValueFlag (fflag, extra);
				}
			      else if (flagcode_hasString (fflag))
				{
				  flags_setStringFlag (fflag, extra);
				}
			      else
				{
				  cstring_free (extra);
				  BADEXIT;
				}
			    }
			}
		    }
		}
	    }
	  else
	    {
	      ;
	    }

	  *s = c;
	  while ((c == ' ') || (c == '\t') || (c == '\n'))
	    {
	      c = *(++s);
	    }
	} 

      if (context_inHeader () && !isArtificial (cstring_fromChars (os)))
	{
	  DPRINTF (("Here adding comment: %s", os));
	  context_addComment (cstring_fromCharsNew (os), loc);
	}
      else
	{
	  ;
	}
    }
  else
    {
      char *t = s;
      int macrocode;
      char tchar = '\0';
      annotationInfo ainfo;

      while (*s != '\0' && *s != ' ' && *s != '\t' && *s != '\n') 
	{
	  s++;
	}

      if (*s != '\0') 
	{
	  tchar = *s;
	  *s = '\0';
	  s++;
	}
      
      t = cstring_toCharsSafe (cstring_downcase (cstring_fromChars (t)));
      macrocode = tokenMacroCode (cstring_fromChars (t));

      if (macrocode != BADTOK)
	{
	  s_tokLength = hasnl ? 0 : size_toInt (mstring_length (t));
	  
	  sfree (t);
	  sfree (os);
	  fileloc_free (loc);

	  if (macrocode == SKIPTOK)
	    {
	      return BADTOK;
	    }

	  return macrocode;
	}
      
      ainfo = context_lookupAnnotation (cstring_fromChars (os));
      
      if (annotationInfo_isDefined (ainfo)) {
	DPRINTF (("Found annotation: %s", annotationInfo_unparse (ainfo)));
	c_lval.annotation = ainfo;
	s_tokLength = 0;
	sfree (os);
	sfree (t);
	fileloc_free (loc);
	return CANNOTATION;
      } 

      if (context_inHeader ())
	{
	  if (tchar != '\0')
	    {
	      *(s-1) = tchar;
	    }
	  
	  if ((context_inMacro () || context_inGlobalContext ())
	      && macrocode != SKIPTOK
	      && !isArtificial (cstring_fromChars (os))) 
	    {
	      if (context_processingMacros ())
		{
		  /* evans 2002-02-24: don't add comments when procssing macros */
		}
	      else
		{
		  context_addComment (cstring_fromCharsNew (os), loc);
		}
	    }
	  else
	    {
	      ; 
	    }
	  
	  if (tchar != '\0')
	    {
	      *(s-1) = '\0';
	    }
	}

      if (mstring_equal (t, "ignore"))
	{
	  if (!context_getFlag (FLG_NOCOMMENTS))
	    {
	      context_enterSuppressRegion (loc);
	    }
	}
      else if ((*t == 'i' || *t == 't')
	       && (*(t + 1) == '\0'))
	{
	  if (!context_getFlag (FLG_NOCOMMENTS)
	      && (*t == 'i' || context_getFlag (FLG_TMPCOMMENTS)))
	    {
	      context_enterSuppressLine (-1, loc); /* infinite suppression */
	    }
	}
      else if (((*t == 'i') || (*t == 't'))
	       && ((*(t + 1) >= '0' && *(t + 1) <= '9')))
	{
	  bool tmpcomment = (*t == 't');
	  int val = -1; 
	  char *tt = t; /* don't mangle t, since it is free'd */
	  char lc = *(++tt);

	  if (lc >= '0' && lc <= '9')
	    {
	      val = (int)(lc - '0');
	      
	      lc = *(++tt);	  
	      while (lc >= '0' && lc <= '9')
		{
		  val *= 10;
		  val += (int) (lc - '0');
		  lc = *(++tt);
		}
	    }
	  
	  if (!context_getFlag (FLG_NOCOMMENTS)
	      && (!tmpcomment || context_getFlag (FLG_TMPCOMMENTS)))
	    {
	      DPRINTF (("Here: enter suppress: %s", fileloc_unparse (loc)));
	      context_enterSuppressLine (val, loc);
	    }
	}
      else if (mstring_equal (t, "end"))
	{
	  if (!context_getFlag (FLG_NOCOMMENTS))
	    {
	      context_exitSuppressRegion (loc);
	    }
	}
      else if (mstring_equal (t, "notfunction"))
	{
	 ; /* handled by pcpp */
	}
      else if (mstring_equal (t, "access"))
	{
	  cstring tname;
	  
	  while (TRUE)
	    {
	      while (((c = *s) != '\0') && (c == ' ' || c == '\t' || c == '\n'))
		{
		  s++;
		}
	      
	      if (c == '\0')
		{
                   break;
		}

	      tname = cstring_fromChars (s);
	      
	      while ((c = *s) != '\0' && c != ' ' 
		     && c != '\t' && c != '\n' && c != ',') 
		{
		  s++;
		}

	      *s = '\0';

	      DPRINTF (("Access %s", tname));

	      if (!context_getFlag (FLG_NOCOMMENTS) 
		  && !context_getFlag (FLG_NOACCESS))
		{
		  if (usymtab_existsType (tname))
		    {
		      typeId uid = usymtab_getTypeId (tname);
		      uentry ue = usymtab_getTypeEntry (uid);

		      if (uentry_isAbstractDatatype (ue))
			{
			  context_addFileAccessType (uid);
			  DPRINTF (("Adding access to: %s / %d", tname, uid));
			}
		      else
			{
			  voptgenerror
			    (FLG_COMMENTERROR,
			     message
			     ("Non-abstract type %s used in access comment",
			      tname),
			     loc);
			}
		    }
		  else
		    {
		      if (!(context_inSuppressRegion ()
			    || context_inSuppressZone (loc)))
			{
			  voptgenerror
			    (FLG_COMMENTERROR,
			     message
			     ("Unrecognized type %s used in access comment",
			      tname),
			     loc);
			}
		    }
		}
	      
	      if (c != '\0') 
		{
		  s++;
		}
	      
	      if (c != ',' && c != ' ')
		{
		  break;
		}
	    }
	}
      else if (mstring_equal (t, "noaccess"))
	{
	  cstring tname;
	  char lc;
	  
	  while (TRUE)
	    {
	      while (((lc = *s) != '\0') && (lc == ' ' || lc == '\t' || lc == '\n')) 
		{
		  s++;
		}
	      
	      if (lc == '\0')
		{
		 break;
		}

	      tname = cstring_fromChars (s);
	      
	      while ((lc = *s) != '\0' && lc != ' ' && lc != '\t' 
		     && lc != '\n' && lc != ',') 
		{
		  s++;
		}

	      *s = '\0';

	      if (!context_getFlag (FLG_NOCOMMENTS) 
		  && !context_getFlag (FLG_NOACCESS))
		{
		  if (usymtab_existsType (tname))
		    {
		      typeId tuid = usymtab_getTypeId (tname);
		      
		      if (context_couldHaveAccess (tuid))
			{
			  DPRINTF (("Removing access: %s", tname));
			  context_removeFileAccessType (tuid);
			}
		      else
			{
			  if (!(context_inSuppressRegion () 
				|| context_inSuppressZone (loc)))
			    {
			      uentry ue = usymtab_getTypeEntry (tuid);
			      
			      if (uentry_isAbstractDatatype (ue))
				{
				  voptgenerror
				    (FLG_COMMENTERROR,
				     message
				     ("Non-accessible abstract type %s used in noaccess comment",
				      tname),
				     loc);
				}
			      else
				{
				  voptgenerror
				    (FLG_COMMENTERROR,
				     message
				     ("Non-abstract type %s used in noaccess comment",
				      tname),
				     loc);
				}
			    }
			}
		    }
		  else
		    {
		      if (!(context_inSuppressRegion () 
			    || context_inSuppressZone (loc)))
			{
			  voptgenerror
			    (FLG_COMMENTERROR,
			     message
			     ("Unrecognized type %s used in noaccess comment",
			      tname),
			     loc);
			}
		    }
		}
	      
	      if (lc != '\0') 
		{
		  s++;
		}
	      
	      if (lc != ',' && lc != ' ')
		{
		  break;
		}
	    }
	}
      else
	{
	  voptgenerror (FLG_UNRECOGCOMMENTS, 
			message ("Semantic comment unrecognized: %s", 
				 cstring_fromChars (os)),
			loc);
	  /*@-branchstate@*/
	} /* spurious (?) warning about t */
      /*@=branchstate@*/
      
      sfree (t);
    }
  
  sfree (os);
  fileloc_free (loc);
  return BADTOK;
}

/*@only@*/ cstring cscannerHelp_makeIdentifier (char *s)
{
  char *c = mstring_create (strlen (s));
  cstring id = cstring_fromChars (c);

  while (isalnum (*s) || (*s == '_') || (*s == '$')) 
    {
      *c++ = *s++;
    }

  *c = '\0';
  return (id);
}

# ifdef DEADCODE
/*@observer@*/ /*@dependent@*/ uentry cscannerHelp_coerceId (cstring cn)
{
  if (!(usymtab_exists (cn)))
    {
      fileloc loc = fileloc_createExternal ();
      
      /*
      ** We need to put this in a global scope, otherwise the sRef will be deallocated.
      */
      
      uentry ce = uentry_makeUnrecognized (cn, loc);
      
      if (!context_inIterEnd ())
	{
	  voptgenerror 
	    (FLG_SYSTEMUNRECOG, 
	     message ("Unrecognized (possibly system) identifier: %q", 
		      uentry_getName (ce)), 
	     g_currentloc);
	}
      
      return ce;
    }
  
  return (usymtab_lookup (cn));
}

/*
** like, cscannerHelp_coerceId, but doesn't supercede for iters
*/

/*@observer@*/ uentry cscannerHelp_coerceIterId (cstring cn)
{
  if (!(usymtab_exists (cn)))
    {
      return uentry_undefined;
    }
  
  return (usymtab_lookup (cn));
}
# endif /* DEADCODE */

/*
** Need to keep this in case there is a declaration that isn't processed until
** the scope exits.  Would be good to rearrange the symbol table so this doesn't
** happen, and save all the cstring copying.
*/

/*@observer@*/ cstring cscannerHelp_observeLastIdentifier (void)
{
  cstring res = s_lastidprocessed;
  return res;
}

static void cscanner_setLastIdentifier (/*@keep@*/ cstring id) /*@modifies s_lastidprocessed@*/
{
  if (cstring_isDefined (s_lastidprocessed))
    {
      cstring_free (s_lastidprocessed); 
    }

  s_lastidprocessed = id;
}

static int
cscannerHelp_processIdentifier (/*@only@*/ cstring id)
  /*@modifies internalState@*/
{
  uentry le;

  if (context_getFlag (FLG_GRAMMAR))
    {
      lldiagmsg (message ("Process identifier: %s", id));
    }

  context_clearJustPopped ();
  cscanner_setLastIdentifier (id);

  DPRINTF (("Context: %s", context_unparse ()));

  if (context_inFunctionHeader ())
    {
      int tok;
      annotationInfo ainfo;

      DPRINTF (("in function decl: %s", id));

      tok = commentMarkerToken (id);
      if (tok != BADTOK)
	{
          s_inSpecPart = TRUE;
          s_whichSpecPart = tok;
	  return tok;
	}

      tok = tokenMacroCode (id);
      if (tok != BADTOK && !cscannerHelp_isSpecTokenRegularId(tok))
        {
          return tok;
        }

      if (s_expectingMetaStateName) 
        {
          metaStateInfo msinfo = context_lookupMetaStateInfo (id);
          if (metaStateInfo_isDefined (msinfo))
            {
              c_lval.msinfo = msinfo;
              return METASTATE_NAME;
            }
          else
            {
              DPRINTF (("Not meta state name: %s", cstring_toCharsSafe (id)));
            }
        }

      ainfo = context_lookupAnnotation (id);
      if (annotationInfo_isDefined (ainfo)) 
        {
          DPRINTF (("Found annotation: %s", annotationInfo_unparse (ainfo)));
          c_lval.annotation = ainfo;
          return CANNOTATION;
        }
      else
        {
          DPRINTF (("Not annotation: %s", id));
        }
    }

  DPRINTF (("Here!"));

  if (context_getFlag (FLG_GNUEXTENSIONS))
    {
      int tok = BADTOK;
      
      if (cstring_equalLit (id, "__stdcall")
	  || cstring_equalLit (id, "__cdecl")
	  || cstring_equalLit (id, "__extension__"))
	{
	  return BADTOK;
	}
      else if (cstring_equalLit (id, "__volatile__"))
	{
	  tok = QVOLATILE;
	}
      else if (cstring_equalLit (id, "__const__"))
	{
	  tok = QCONST;
	}
      else if (cstring_equalLit (id, "__alignof__")) 
	{
	  tok = CALIGNOF; /* alignof is parsed like sizeof */
	}
      else if (cstring_equalLit (id, "__typeof__")
               || cstring_equalLit (id, "typeof")) 
	{
	  tok = CTYPEOF;
	}
      else if (cstring_equalLit (id, "__FUNCTION__")
	       || cstring_equalLit (id, "__PRETTY_FUNCTION__")) 
	{
          return cscannerHelp_returnFunctionName (id);
	}
      else if (cstring_equalLit (id, "__attribute__")
	       || cstring_equalLit (id, "__asm__")
	       || cstring_equalLit (id, "_asm")
	       || cstring_equalLit (id, "__asm")
	       || cstring_equalLit (id, "__declspec"))
	{
	  int depth = 0;
	  bool useparens = FALSE;
	  bool usebraces = FALSE;
	  bool inquote = FALSE;
	  bool inescape = FALSE;
	  int ic;

	  while ((ic = cscanner_input ()) != EOF)
	    {
	      char cc = (char) ic;

	      if (inescape)
		{
		  inescape = FALSE;
		}
	      else if (cc == '\\')
		{
		  inescape = TRUE;
		}
	      else if (cc == '\"')
		{
		  inquote = !inquote;
		}
	      else if (!inquote)
		{
		  if (cc == '(')
		    {
		      if (!useparens)
			{
			  if (!usebraces)
			    {
			      useparens = TRUE;
			    }
			}

		      if (useparens)
			{
			  depth++;
			}
		    }
		  else if (cc == '{')
		    {
		      if (!usebraces)
			{
			  if (!useparens)
			    {
			      usebraces = TRUE;
			    }
			}

		      if (usebraces)
			{
			  depth++;
			}
		    }
		  else if (cc == ')' && useparens)
		    {
		      depth--;
		      if (depth == 0) break;
		    }
		  else if (cc == '}' && usebraces)
		    {
		      depth--;
		      if (depth == 0) break;
		    }
		  else if (cc == '}' 
			   && !usebraces && !useparens
			   && cstring_equalLit (id, "__asm"))
		    {
		      /*
		      ** We need this because some MS VC++ include files
		      ** have __asm mov ... }
		      ** Its a kludge, but otherwise would need to parse
		      ** the asm code!
		      */ 
		      return TRBRACE;
		    }
		  else
		    {
		      ;
		    }
		}
	      else
		{
		  ;
		}

	      if (cc == '\n')
		{
		  context_incLineno ();

		  if (cstring_equalLit (id, "__asm")
		      && !useparens && !usebraces)
		    {
		      break;
		    }
		}
	    }
	  
	  llassert ((useparens && ic == (int) ')')
		    || (usebraces && ic == (int) '}')
		    || (!useparens && !usebraces));

	  return BADTOK;
	}
      else if (cstring_equalLit (id, "__inline__")
	       || cstring_equalLit (id, "__inline")
	       || cstring_equalLit (id, "_inline"))
	{
	  tok = QINLINE;
	}
      else
	{
	  ;
	}
      
      if (tok != BADTOK)
	{
	  return (cscannerHelp_returnToken (tok));
	}
    }

  le = usymtab_lookupSafe (id);

  /*@-dependenttrans@*/
  
  if (uentry_isIter (le))
    {
      c_lval.entry = le;
      return (ITER_NAME);
    }
  else if (uentry_isEndIter (le))
    {
      c_lval.entry = le;
      return (ITER_ENDNAME);
    }
  else if (uentry_isUndefined (le))
    {
      c_lval.cname = cstring_copy (id);
      return (NEW_IDENTIFIER);
    }
  else if (!uentry_isDeclared (le) && !uentry_isCodeDefined (le))
    {
      if (uentry_isDatatype (le))
	{
	  c_lval.cname = cstring_copy (id);
	  return (NEW_IDENTIFIER);
	}
      else
	{
	  c_lval.entry = le;	  	  
	  return (IDENTIFIER); 
	}
    }
  else if (uentry_isDatatype (le))
    {
      if (!s_expectingTypeName)
	{
	  c_lval.cname = cstring_copy (id);

	  return (NEW_IDENTIFIER);
	}
      else
	{
	  c_lval.ctyp = uentry_getAbstractType (le);
	  
	  uentry_setUsed (le, g_currentloc);
	  return (TYPE_NAME);
	}
    }
  else
    {
      c_lval.entry = le;            
      return (IDENTIFIER); 
    }

  /*@=dependenttrans@*/
}

bool cscannerHelp_processHashIdentifier (/*@only@*/ cstring id)
{
  if (context_inMacro () || context_inIterDef () ||
      context_inIterEnd ())
    {
      uentry le;
      
      context_clearJustPopped ();

      le = usymtab_lookupSafe (id);
      cscanner_setLastIdentifier (id);

      if (uentry_isParam (le) || uentry_isRefParam (le))
	{
	  return TRUE;
	}
      else
	{
	  return FALSE;
	}
    }
  else
    {
      /*
      ** Will be handled by handleLlSpecial
      */

      cstring_free (id);
      return FALSE;
    }
}

/*
 * states & input classes needed for constant string processing automata;
 *
 * FIXME:
 * should be defined inside process_stringLiteral(), but Splint has problems
 * with dumping when enumeration data types are defined inside procedures.
 */

enum str_state {
    str_init = 0,
    str_inside,
    str_escape,
    str_white,
    str_ok,
    str_errnul, /* special treatement for null character error */
    str_errfmt
};

enum ch_input {
    ch_null = 0,
    ch_quote,
    ch_escape,
    ch_white,
    ch_other
};

static /*@only@*/ /*@notnull@*/ cstring
process_stringLiteral (const /*@notnull@*/ char* in, size_t inlen)
{
  /*
   * in expected to be of the form "..."[(( \t\n)*"...")*]
   * have to:
   *  - strip leading & trailing '\"' of each individual constant string;
   *  - handle possible null characters inside constant string;
   *  - if there are more then one constant strings, merge them [strip white
   *    space between them].
   */

  size_t outlen, i;
  int col;
  char* out;

  enum str_state state;
  /*@+enumint@*/
  static const enum str_state automata[str_ok+1][ch_other+1] = {
  /*                    null       quote      escape       white       other */
  /*   init */  { str_errfmt, str_inside, str_errfmt, str_errfmt, str_errfmt},
  /* inside */  { str_errnul,     str_ok, str_escape, str_inside, str_inside},
  /* escape */  { str_errnul, str_inside, str_inside, str_inside, str_inside},
  /*  white */  { str_errfmt, str_inside, str_errfmt,  str_white, str_errfmt},
  /*     ok */  { str_errfmt, str_inside, str_errfmt,  str_white, str_errfmt}
  };
  /*@=enumint@*/

  llassertfatal (in != NULL);

  out = mstring_create (inlen);
  outlen = i = 0;
  state = str_init;
  col = 0;
  while (i < inlen
      /* no reason to continue once in error state */
      && !(state == str_errnul || state == str_errfmt))
  {
    enum str_state newstate;
    enum ch_input chi;

    ++col;
    switch (in[i]) {
      case '\0': chi = ch_null; /*@switchbreak@*/ break;
      case '\"': chi = ch_quote; /*@switchbreak@*/ break;
      case '\\': chi = ch_escape; /*@switchbreak@*/ break;
      case '\n': col = 0; context_incLineno ();
      /*@fallthrough@*/
      case ' ' :
      case '\t': chi = ch_white; /*@switchbreak@*/ break;
      default  : chi = ch_other; /*@switchbreak@*/ break;
    }
    /*@+enumindex@*/
    newstate = automata [state][chi];
    /*@=enumindex@*/
    if ((newstate == str_inside || newstate == str_escape) &&
        (state == str_inside || state == str_escape))
    {
      out[outlen] = in[i];
      ++outlen;
    }

    state = newstate;
    ++i;
  }
  out[outlen] = '\0';
  addColumn (col);

  if (!(state == str_ok || state == str_errnul))
    {
      llbug (message ("String literal has unexpected format"));
    }
  else
    {
      if (state == str_errnul)
        {
          /* FIXME: should add a flag contolling this, and transform message
           * into warning ... */
          llgenmsg (message ("Null character(s) present in string literal"),
              g_currentloc);
        }

      if (context_getFlag (FLG_STRINGLITERALLEN) &&
          outlen > size_fromInt (context_getValue (FLG_STRINGLITERALLEN)))
        {
          voptgenerror (FLG_STRINGLITERALLEN,
                        message
                        ("String literal length (%d) exceeds maximum "
                         "length (%d): \"%s\"",
                         size_toInt (outlen),
                         context_getValue (FLG_STRINGLITERALLEN),
                         cstring_fromChars (out)),
                        g_currentloc);
        }
    }

  return cstring_fromChars (out);
}

/*@only@*/ exprNode cscannerHelp_processString (void)
{
  fileloc loc = fileloc_copy (g_currentloc);
  return exprNode_stringLiteral (process_stringLiteral (c_text, c_leng), loc);
}

/*
** process a wide character string L"...."
*/

/*@only@*/ exprNode cscannerHelp_processWideString (void)
{
  fileloc loc = fileloc_copy (g_currentloc);
  llassert (*c_text == 'L');
  return exprNode_wideStringLiteral (process_stringLiteral (c_text+1, c_leng-1), loc);
}

char cscannerHelp_processChar (void)
{
  char fchar;
  char next;

  llassert (*c_text != '\0');
  fchar = *(c_text + 1);
  if (fchar != '\\') return fchar;
  
  next = *(c_text + 2);
  
  switch (next)
    {
    case 'n': return '\n';
    case 't': return '\t';
    case '\"': return '\"';
    case '\'': return '\'';
    case '\\': return '\\';
    default: return '\0';
    }
}

double cscannerHelp_processFloat (void)
{
  double ret = atof (c_text);

    return (ret);
}

long cscannerHelp_processHex (void)
{
  int index = 2;
  long val = 0;

  llassert (c_text[0] == '0'
	    && (c_text[1] == 'X' || c_text[1] == 'x'));

  while (c_text[index] != '\0') {
    int tval;
    char c = c_text[index];

    if (c >= '0' && c <= '9') {
      tval = (int) c - (int) '0';
    } else if (c >= 'A' && c <= 'F') {
      tval = (int) c - (int) 'A' + 10;
    } else if (c >= 'a' && c <= 'f') {
      tval = (int) c - (int) 'a' + 10;
    } else if (c == 'U' || c == 'L' || c == 'u' || c == 'l') {
      index++;
      while (c_text[index] != '\0') {
	if (c == 'U' || c == 'L' || c == 'u' || c == 'l') {
	  ;
	} else {
	  voptgenerror
	    (FLG_SYNTAX, 
	     message ("Invalid character (%c) following specifier in hex constant: %s",
		      c, cstring_fromChars (c_text)),
	     g_currentloc);
	}
	index++;
      }

      break;
    } else {
      voptgenerror
	(FLG_SYNTAX, 
	 message ("Invalid character (%c) in hex constant: %s",
		  c, cstring_fromChars (c_text)),
	 g_currentloc);
      break;
    }

    val = (val << 4) + tval;
    index++;
  }

  DPRINTF (("Hex constant: %s = %ld", c_text, val));
  return val;
}

long cscannerHelp_processOctal (void)
{
  int index = 1;
  long val = 0;

  llassert (c_text[0] == '0' && c_text[1] != 'X' && c_text[1] != 'x');
    
  while (c_text[index] != '\0') {
    int tval;
    char c = c_text[index];
    
    if (c >= '0' && c <= '7') {
      tval = (int) c - (int) '0';
    } else if (c == 'U' || c == 'L' || c == 'u' || c == 'l') {
      index++;
      while (c_text[index] != '\0') {
	if (c == 'U' || c == 'L' || c == 'u' || c == 'l') {
	  ;
	} else {
	  voptgenerror
	    (FLG_SYNTAX, 
	     message ("Invalid character (%c) following specifier in octal constant: %s",
		      c, cstring_fromChars (c_text)),
	     g_currentloc);
	}
	index++;
      }

      break;
    } else {
      voptgenerror
	(FLG_SYNTAX, 
	 message ("Invalid character (%c) in octal constant: %s",
		  c, cstring_fromChars (c_text)),
	 g_currentloc);
      break;
    }

    val = (val * 8) + tval;
    index++;
  }

  DPRINTF (("Octal constant: %s = %ld", c_text, val));
  return val;
}

long cscannerHelp_processDec (void)
{
  return (atol (c_text));
}

/* Allow specificiation keywords to be used as identifiers in certain contexts. */
static bool cscannerHelp_isSpecTokenRegularId (int tok)
{
  if (!s_inSpecPart)
    {
      return TRUE;
    }

  if ( s_whichSpecPart == QMODIFIES
    || s_whichSpecPart == QDEFINES
    || s_whichSpecPart == QUSES 
    || s_whichSpecPart == QALLOCATES
    || s_whichSpecPart == QSETS
    || s_whichSpecPart == QRELEASES
    || s_whichSpecPart == QWARN)
    {
        return TRUE;
    }

  if (s_whichSpecPart == QGLOBALS)
    {
      if (!(tok == QKILLED || tok == QUNDEF || tok == QOUT || tok == QIN || tok == QPARTIAL))
      {
        return TRUE;
      }
    }

  if (s_whichSpecPart == QPRECLAUSE || s_whichSpecPart == QPOSTCLAUSE)
    {
      if (!(tok == QMAXSET || tok == QMAXREAD
            /* uncomment the additional tests when minSet and minRead are supported */
            /* || tok == QMINREAD || tok == QMINSET */

            || tok == QOBSERVER || tok == QEXPOSED
            || tok == QONLY || tok == QSHARED
            || tok == QOWNED || tok == QDEPENDENT
            || tok == QUNIQUE

            || tok == QISNULL || tok == QNULL
            || tok == QNOTNULL || tok == QRELNULL
            || tok == QNULLTERMINATED

            /* || tok == QYIELD */
            /* || tok == QTEMP */
            /* || tok == QKEEP */
            /* || tok == QKEPT */
            ))
        {
          return TRUE;
        }
    }

  /* Specification parts not handled (they require a more complex solution
   * then this simple hack): QCONSTANT, QFUNCTION, QITER & QALT. */

  return  FALSE;
}

int cscannerHelp_processSpec (int tok)
{
  size_t length = strlen (c_text);
  
  if (!cscannerHelp_isSpecTokenRegularId (tok))
    {
      cscannerHelp_setTokLengthT (length);
      return cscannerHelp_returnToken (tok);
    }

  context_saveLocation ();
  cscannerHelp_setTokLengthT (length);
  return (cscannerHelp_processIdentifier (cscannerHelp_makeIdentifier (c_text)));
}

void cscannerHelp_expectingMetaStateName (void)
{
  llassert (!s_expectingMetaStateName);
  llassert (context_inFunctionHeader ());
  s_expectingMetaStateName = TRUE;
}

void cscannerHelp_clearExpectingMetaStateName (void)
{
  llassert (s_expectingMetaStateName);
  s_expectingMetaStateName = FALSE;
}

bool cscannerHelp_processMacro (void)
{
  uentry e2;
  ctype ct;
  int noparams = 0;
  cstring fname = cstring_undefined;
  bool res = TRUE;
  bool isspecfcn = FALSE;
  bool isiter = FALSE;
  bool skipparam = FALSE;
  bool unknownm = FALSE;
  bool hasParams = FALSE;
  bool emptyMacro = FALSE;
  char c = skip_whitespace ();
  fileloc loc = fileloc_noColumn (g_currentloc);

  /* are both of these necessary?  what do they mean? */
  uentryList specparams = uentryList_undefined;
  uentryList pn = uentryList_undefined;

  context_resetMacroMissingParams ();

  if (c == '\0' || c == '\n')
    {
      llcontbug (cstring_makeLiteral ("Bad macro"));
      fileloc_free (loc);
      return FALSE;
    }
  
  fname = cstring_appendChar (fname, c);  

  while ((c = macro_nextChar ()) != '(' && c != '\0'
	 && c != ' ' && c != '\t' && c != '\n')
    {
      fname = cstring_appendChar (fname, c);
    }

  if (c == ' ' || c == '\t' || c == '\n')
    {
      char oldc = c;

      if (c != '\n')
	{
	  while (c == ' ' || c == '\t')
	    {
	      c = macro_nextChar ();
	    }
	  cscanner_unput ((int) c);
	}

      if (c == '\n')
	{
	  emptyMacro = TRUE;
	  cscanner_unput ((int) c);
	}

      c = oldc;
    }

  hasParams = (c == '(');
  
  if (usymtab_exists (fname))
    {
      e2 = usymtab_lookupExpose (fname);
      ct = uentry_getType (e2);

      if (uentry_isCodeDefined (e2) 
	  && fileloc_isUser (uentry_whereDefined (e2)))
	{
	  if (optgenerror 
	      (FLG_MACROREDEF,
	       message ("Macro %s already defined", fname),
	       loc))
	    {
	      uentry_showWhereDefined (e2);
	      uentry_clearDefined (e2);
	    }

	  if (uentry_isFunction (e2))
	    {
	      uentry_setType (e2, ctype_unknown);
	      ct = ctype_unknown;
	      unknownm = TRUE;
	      context_enterUnknownMacro (e2); 
	    }
	  else
	    {
	      context_enterConstantMacro (e2);
	    }
	}
      else if (uentry_isForward (e2) && uentry_isFunction (e2))
        {
          unknownm = TRUE;

          voptgenerror 
            (FLG_MACROFCNDECL,
             message
             ("Parameterized macro has no prototype or specification: %s ", 
              fname),
             loc);
          
          ct = ctype_unknown;
          uentry_setType (e2, ctype_unknown);
          uentry_setFunctionDefined (e2, loc); 
          uentry_setUsed (e2, fileloc_undefined);
          context_enterUnknownMacro (e2); 
        }
      else if (uentry_isIter (e2))
        {
          isiter = TRUE;
          specparams = uentry_getParams (e2);
          noparams = uentryList_size (specparams);
          uentry_setDefined (e2, loc);
          context_enterIterDef (e2); 
        }
      else if (uentry_isEndIter (e2))
        {
          uentry_setDefined (e2, loc);
          context_enterIterEnd (e2); /* don't care about it now */
          /* but should parse like an iter! */
        }
      else if (uentry_isEitherConstant (e2))
        {
          if (hasParams)
            {
              voptgenerror 
                (FLG_INCONDEFS, 
                 message ("Constant %s implemented as parameterized macro",
                          fname),
                 g_currentloc);
              
              uentry_showWhereSpecified (e2);
              uentry_setType (e2, ctype_unknown);
              uentry_makeConstantFunction (e2);
              uentry_setDefined (e2, g_currentloc);
              uentry_setFunctionDefined (e2, g_currentloc);
              context_enterUnknownMacro (e2); 
            }
          else
            {
              if (!uentry_isSpecified (e2))
                {
                  fileloc oloc = uentry_whereDeclared (e2);

                  if (fileloc_isLib (oloc))
                    {
                      ;
                    }
                  else if (fileloc_isUndefined (oloc)
                           || fileloc_isPreproc (oloc))
                    {
                      if (!emptyMacro)
                        {
                          voptgenerror
                            (FLG_MACROCONSTDECL,
                             message 
                             ("Macro constant %q not declared",
                              uentry_getName (e2)),
                             loc);			 
                        }
                    }
                  else if (!fileloc_withinLines (oloc, loc, 2))
                    { /* bogus!  will give errors if there is too much whitespace */
                      voptgenerror
                        (FLG_MACROCONSTDIST,
                         message 
                         ("Macro constant name %s matches name in "
                          "distant constant declaration.  This constant "
                          "is declared at %q", fname, 
                          fileloc_unparse (oloc)),
                         loc);
                    }
                  else
                    {
                      /* No warning */
                    }
                }

              context_enterConstantMacro (e2);	      
              cstring_free (fname);
              fileloc_free (loc);
              return res;
            }
        }
      else if (ctype_isFunction (ct))
        {
          isspecfcn = TRUE;
          specparams = ctype_argsFunction (ct);
          noparams = uentryList_size (specparams);
          
          uentry_setFunctionDefined (e2, loc); 
          context_enterMacro (e2);
        }
      else if (uentry_isVar (e2))
        {
          if (hasParams)
            {
              voptgenerror
                (FLG_INCONDEFS,
                 message ("Variable %s implemented as parameterized macro", 
                          fname),
                 loc);

              uentry_showWhereSpecified (e2);
              uentry_setType (e2, ctype_unknown);
              uentry_makeVarFunction (e2);
              uentry_setDefined (e2, g_currentloc);
              uentry_setFunctionDefined (e2, g_currentloc);
              context_enterUnknownMacro (e2); 
            }
          else
            {
              uentry ucons = uentry_makeConstant (fname,
                                                  ctype_unknown,
                                                  loc);
              if (uentry_isExpandedMacro (e2))
                {
                  ; /* okay */
                }
              else
                {
                  if (optgenerror 
                      (FLG_INCONDEFS,
                       message ("Variable %s implemented by a macro",
                                fname),
                       loc))
                    {
                      uentry_showWhereSpecified (e2);
                    }
                }

              uentry_setDefined (e2, loc);
              uentry_setUsed (ucons, loc);

              context_enterConstantMacro (ucons);
              uentry_markOwned (ucons);
              cstring_free (fname);
              return res;
            }
        }
      else if (uentry_isDatatype (e2))
        {
          vgenhinterror 
            (FLG_SYNTAX,
             message ("Type implemented as macro: %x", 
                      uentry_getName (e2)),
             message ("A type is implemented using a macro definition.  A "
                      "typedef should be used instead."),
             g_currentloc);

          cscannerHelp_swallowMacro ();
          /* Must exit scope (not sure why a new scope was entered?) */
          usymtab_quietExitScope (g_currentloc);
          uentry_setDefined (e2, g_currentloc);
          res = FALSE;
        }
      else
        {
          llcontbug 
            (message ("Unexpanded macro not function or constant: %q", 
                      uentry_unparse (e2)));
          uentry_setType (e2, ctype_unknown);
          
          if (hasParams)
            {
              uentry_makeVarFunction (e2);
              uentry_setDefined (e2, g_currentloc);
              uentry_setFunctionDefined (e2, g_currentloc);
              context_enterUnknownMacro (e2); 
            }
        }
    }
  else
    {
      uentry ce;

      /* evans 2001-09-09 - if it has params, assume a function */
      if (hasParams)
	{
	  voptgenerror 
	    (FLG_MACROMATCHNAME,
	     message ("Unexpanded macro %s does not match name of a declared "
		      "function. The name used in the control "
		      "comment on the previous line should match.",
		      fname),
	     loc);
	  
	  ce = uentry_makeFunction (fname, ctype_unknown, 
				    typeId_invalid,
				    globSet_undefined,
				    sRefSet_undefined,
				    warnClause_undefined,
				    fileloc_undefined);      
	  uentry_setUsed (ce, loc); /* perhaps bogus? */
	  e2 = usymtab_supEntryReturn (ce);
	  context_enterUnknownMacro (e2);	      
	}
      else
	{
	  voptgenerror 
	    (FLG_MACROMATCHNAME,
	     message ("Unexpanded macro %s does not match name of a constant "
		      "or iter declaration.  The name used in the control "
		      "comment on the previous line should match.  "
		      "(Assuming macro defines a constant.)", 
		      fname),
	     loc);
	  
	  ce = uentry_makeConstant (fname, ctype_unknown, fileloc_undefined);      
	  uentry_setUsed (ce, loc); /* perhaps bogus? */
	  e2 = usymtab_supEntryReturn (ce);
	  
	  context_enterConstantMacro (e2);	      
	  cstring_free (fname);
	  fileloc_free (loc);
	  return res;
	}
    }
  
  /* in macros, ( must follow immediatetly after name */
  
  if (hasParams)
    {
      int paramno = 0;
      
      c = skip_whitespace ();

      while (c != ')' && c != '\0')
	{
	  uentry  param;
	  bool    suppress = context_inSuppressRegion ();
	  cstring paramname = cstring_undefined;

	  /*
	  ** save the parameter location
	  */

	  decColumn ();
	  context_saveLocation ();
	  incColumn ();

	  while (c != ' ' && c != '\t' && c != ',' && c != '\0' && c != ')')
	    {
	      paramname = cstring_appendChar (paramname, c);
	      c = macro_nextChar ();
	    }
	  
	  if (c == ' ' || c == '\t') c = skip_whitespace ();

	  if (c == ',')
	    {
	      c = macro_nextChar ();
	      if (c == ' ' || c == '\t') c = skip_whitespace ();
	    }
	  
	  if (c == '\0')
	    {
	      llfatalerror (cstring_makeLiteral
			    ("Bad macro syntax: uentryList"));
	    }
	  
	  if ((isspecfcn || isiter) && (paramno < noparams)
	      && !uentry_isElipsisMarker (uentryList_getN 
					  (specparams, paramno)))
	    {
	      fileloc sloc = context_getSaveLocation ();
	      uentry decl = uentryList_getN (specparams, paramno);
	      sRef sr;
	      
	      param = uentry_nameCopy (paramname, decl);
	      	      	      
	      uentry_setParam (param);
	      sr = sRef_makeParam (paramno, uentry_getType (param), 
				   stateInfo_makeLoc (sloc, SA_DECLARED));

	      if (sRef_getNullState (sr) == NS_ABSNULL)
		{
		  ctype pt = ctype_realType (uentry_getType (param));

		  if (ctype_isUser (pt))
		    {
		      uentry te = usymtab_getTypeEntrySafe (ctype_typeId (pt));
		      
		      if (uentry_isValid (te))
			{
			  sRef_setStateFromUentry (sr, te);
			}
		    }
		  else
		    {
		      sRef_setNullState (sr, NS_UNKNOWN, sloc);
		    }
		}

	      uentry_setSref (param, sr);
	      uentry_setDeclaredForceOnly (param, sloc);

	      skipparam = isiter && uentry_isOut (uentryList_getN (specparams, paramno));
	    }
	  else
	    {
	      fileloc sloc = context_getSaveLocation ();

	      param = uentry_makeVariableSrefParam 
		(paramname, ctype_unknown, fileloc_copy (sloc), 
		 sRef_makeParam (paramno, ctype_unknown,
				 stateInfo_makeLoc (sloc, SA_DECLARED)));
	      DPRINTF (("Unknown param: %s", uentry_unparseFull (param)));
	      cstring_free (paramname);

	      sRef_setPosNull  (uentry_getSref (param), sloc);
	      uentry_setDeclaredForce (param, sloc);

	      skipparam = FALSE;
	      fileloc_free (sloc);
	    }

	  if (!skipparam)
	    {
	      llassert (!uentry_isElipsisMarker (param));

	      if (!suppress)
		{
		  sRef_makeUnsafe (uentry_getSref (param));
		}
	      
	      pn = uentryList_add (pn, uentry_copy (param));
	      usymtab_supEntry (param);
	    }
	  else
	    {
	      /* don't add param */
	      uentry_free (param);
	    }

	  if (c == ',') 
	    {
	      (void) macro_nextChar ();
	      c = skip_whitespace ();
	    }

	  paramno++;
	}
      
      if (c == ')')
	{
	  if (isspecfcn || isiter)
	    {
	      if (paramno != noparams && noparams >= 0)
		{
		  cscannerHelp_advanceLine ();

		  voptgenerror 
		    (FLG_INCONDEFS,
		     message ("Macro %s specified with %d args, defined with %d", 
			      fname, noparams, paramno),
		     g_currentloc);

		  uentry_showWhereSpecified (e2);
		  uentry_resetParams (e2, pn);
		}
	    }
	  else
	    {
	      uentry_resetParams (e2, pn);
	    }
	}
    }
  else
    {
      /*
      ** the form should be:
      **
      ** # define newname oldname
      ** where oldname refers to a function matching the specification
      ** of newname.
      */

      if (unknownm)
	{
	  sRef_setGlobalScope ();
	  usymtab_supGlobalEntry (uentry_makeVariableLoc (fname, ctype_unknown));
	  sRef_clearGlobalScope ();
	}
      else
	{
	  context_setMacroMissingParams ();
	}
    }
  
  
  /* context_setuentryList (pn); */
  usymtab_enterScope ();

  fileloc_free (loc);
  cstring_free (fname);

  return res;
}

void cscannerHelp_setTokLength (int len) 
{
  addColumn (len);
  s_tokLength = len;
  DPRINTF (("Set tok length: %d", len));
}

void cscannerHelp_setTokLengthT (size_t len)
{
  cscannerHelp_setTokLength (size_toInt (len));
}

static void cscannerHelp_advanceLine (void)
{
  s_tokLength = 0;
  beginLine ();
}

int cscannerHelp_returnToken (int t)
{
  if (s_tokLength > fileloc_column (g_currentloc)) {
    c_lval.tok = lltok_create (t, fileloc_copy (g_currentloc));
  } else {
    c_lval.tok = lltok_create (t, fileloc_decColumn (g_currentloc, s_tokLength)); 
  }

  s_tokLength = 0; 
  return (t); 
}

int cscannerHelp_returnTokenLength (int t, int length)
{
  cscannerHelp_setTokLength (length);
  return cscannerHelp_returnToken (t);
}

int cscannerHelp_returnString (cstring s)
{
  c_lval.expr = exprNode_stringLiteral (s, fileloc_decColumn (g_currentloc, s_tokLength)); 
  s_tokLength = 0;
  return (CCONSTANT); 
}

int cscannerHelp_returnInt (ctype ct, long val)
{
  ctype c = ct;

  if (ctype_equal (ct, ctype_int))
    {
      if (val == 0)
	{
	  c = context_typeofZero ();
	}
      else if (val == 1)
	{
	  c = context_typeofOne ();
	}
      else
	{
	  ;
	}
    }
  
  c_lval.expr = exprNode_numLiteral (c, cstring_fromChars (c_text), 
				     fileloc_decColumn (g_currentloc, s_tokLength),
				     val);   
  s_tokLength = 0; 
  return (CCONSTANT);
}

int cscannerHelp_returnFloat (ctype ct, double f)
{
  c_lval.expr = exprNode_floatLiteral (f, ct, cstring_fromChars (c_text), 
				       fileloc_decColumn (g_currentloc, s_tokLength));
  s_tokLength = 0; 
  return (CCONSTANT);
}

int cscannerHelp_returnChar (char c)
{
  c_lval.expr = exprNode_charLiteral (c, cstring_fromChars (c_text), 
				      fileloc_decColumn (g_currentloc, s_tokLength));
  s_tokLength = 0; 
  return (CCONSTANT);
}

int cscannerHelp_returnType (int tok, ctype ct) 
{
  c_lval.ctyp = ct; 
  s_tokLength = 0; 
  return tok;
}

int cscannerHelp_returnExpr (exprNode e)
{
  c_lval.expr = e; 
  s_tokLength = 0; 
  return (CCONSTANT); 
}

/* Should hold the name of the current function as string; instead it just
* saves the (literal) value of the token ... */
int cscannerHelp_returnFunctionName (cstring id)
{
  c_lval.expr = exprNode_makeConstantString (id,
          fileloc_decColumn (g_currentloc, s_tokLength));
  s_tokLength = 0;
  return CCONSTANT;
}

void cscannerHelp_setExpectingTypeName (void)
{
  s_expectingTypeName = TRUE;
}

void cscannerHelp_clearExpectingTypeName (void)
{
  s_expectingTypeName = FALSE;
}

# ifdef DEADCODE
bool cscannerHelp_isExpectingTypeName (void)
{
  return s_expectingTypeName;
}
# endif /* DEADCODE */

int cscannerHelp_processTextIdentifier (char *text)
{
  context_saveLocation (); 
  cscannerHelp_setTokLength (size_toInt (mstring_length (text))); 
  return cscannerHelp_processIdentifier (cscannerHelp_makeIdentifier (text)); 
}

static bool s_continueLine = FALSE;

int cscannerHelp_handleNewLine (void)
{
  context_incLineno ();

  if (s_tokLength != 0) { 
    s_tokLength = 0; 
    /* No error to report 
       voptgenerror
       (FLG_SYNTAX, 
       message ("Likely parse error: token spans multiple lines."),
       g_currentloc);
    */
  }
  
  if (s_continueLine)
    {
      s_continueLine = FALSE;
    }
  else 
    {
      if (context_inMacro ())
	{
	  /* Don't use return cscannerHelp_returnToken */
	  /* !!! evans 2002-03-13 */
	  c_lval.tok = lltok_create (TENDMACRO, fileloc_copy (g_currentloc)); 
	  return TENDMACRO;
	}  
    }

  return BADTOK;
}

void cscannerHelp_setContinueLine (void)
{
  s_continueLine = TRUE;
}

void cscannerHelp_exitSpecPart (void)
{
  llassert (s_inSpecPart);
  s_inSpecPart = FALSE;
  s_whichSpecPart = BADTOK;
}
