/*;-*-C-*-; 
** Splint - annotation-assisted static program checker
** Copyright (C) 1994-2002 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: splint@cs.virginia.edu
** To report a bug: splint-bug@cs.virginia.edu
** For more information: http://www.splint.org
*/
/*
** cscanner.l
**
** Flex lexer for C.
** Based on a C lexer by Nate Osgood
**    from hacrat@catfish.lcs.mit.edu Mon Jun 14 13:06:32 1993
*/
/*
** Modified by Herbert 08/19/97:
** - added #include for IBM's OS/2 compiler.
** - fixed weird bug with lookup of tmp files (OS/2 and MSDOS only).
*/
/*
** Modified by Mike Smith 
** Corrected missing 'line' in scanf() calls in handleSpecial().
** Without this, I get an error when Splint hits a '#line' directive
** in the pre-pre-processed source files. For safety, I have made these
** conditional on OS2 and MSDOS because I don't understand why noone else
** has seen this problem.
**
** Modified by Mike Smith, 4th June 1997
** Finally resolved the #line problem.  The scanf() calls have been fixed to
** allow the following #line forms:-
**
**        #line 123 "filename"
**        #line 123
**        # 123 "filename"
**        # 123
**
** The last two are generated by the GNU pre-processor, apparently
*/

Digit			[0-9]
Letter			[a-zA-Z_$]
H			[a-fA-F0-9]
E			[Ee][+-]?{Digit}+
U                       (u|U)
L                       (l|L)
FS			(f|F|l|L)
IS			(u|U|l|L)*
ULSuffix                ({U}{L}|{L}{U})

%{
# include "splintMacros.nf"
# if defined(OS2) && defined(__IBMC__)
   /* needed for isatty()... */
# include <io.h>
# else

/*
** Win32 doesn't have unistd.h
*/

# ifndef WIN32
# include <unistd.h>
# endif

# endif

# include "basic.h"

# include "cgrammar.h"
# include "cgrammar_tokens.h"

# include "fileIdList.h"
# include "portab.h"

static bool lastWasString = FALSE;
static char savechar = '\0';

/*@notfunction@*/
# define yyinput() (incColumn (), getc (yyin))

static /*@owned@*/ cstring lastidprocessed = cstring_undefined;
static int lminput (void);
static int tokLength = 0;
static bool inSpecPart = FALSE;
static bool continueLine = FALSE;

static int ninput (void);
static char processChar (void);
static double processFloat (void);
static /*@only@*/ exprNode processString (void) ;
static /*@only@*/ exprNode processWideString (void) ;
static long processDec (void);
static long processHex (void);
static long processOctal (void);
static int processIdentifier (/*@only@*/ cstring)
   /*@globals undef lastidprocessed@*/ ;
static bool processHashIdentifier (/*@only@*/ cstring)
   /*@globals undef lastidprocessed@*/ ;

static int processSpec (int);
static bool handleSpecial (char *);
static int handleLlSpecial (void);
static void handleMacro (void);
static bool processMacro (void);
static /*@only@*/ cstring makeIdentifier (char *);

/* yes, this is exported! */
bool g_expectingTypeName = TRUE; /* beginning of file can be type name! */

static bool expectingMetaStateName = FALSE;

static int returnInt (ctype, long);
static int returnFloat (ctype, double);
static int returnChar (char);
static void setTokLength (int) /*@modifies g_currentloc@*/ ;
static void setTokLengthT (size_t) /*@modifies g_currentloc@*/ ;

static void advanceLine (void)
{
  tokLength = 0;
  beginLine ();
}
    
/*@-allmacros@*/
# define RETURN_INT(c,i) \
  do { lastWasString = FALSE; \
       return (returnInt (c, i)); } while (FALSE)

# define RETURN_FLOAT(c,f) \
  do { lastWasString = FALSE; \
       return (returnFloat (c, f)); \
       } while (FALSE)

# define RETURN_CHAR(c) \
  do { lastWasString = FALSE; \
       return (returnChar (c)); \
     } while (FALSE)

# define RETURN_TOK(t) \
  do { yylval.tok = lltok_create (t, fileloc_decColumn (g_currentloc, tokLength)); \
       tokLength = 0; \
       lastWasString = FALSE; \
       return (t); } while (FALSE)

# define RETURN_TYPE(t, ct) \
  do { yylval.ctyp = ct; tokLength = 0; return (t); } while (FALSE)

/* don't fileloc_decColumn (g_currentloc, tokLength));  
   the string could have \n's in it!
*/

# define RETURN_STRING(c) \
 do { yylval.expr = exprNode_stringLiteral (c, fileloc_decColumn (g_currentloc, tokLength)); \
      tokLength = 0; \
      lastWasString = TRUE; \
      return (CCONSTANT); } while (FALSE)

# define RETURN_EXPR(e) \
 do { yylval.expr = e; \
      tokLength = 0; \
      lastWasString = TRUE; \
      return (CCONSTANT); } while (FALSE)

/*@=allmacros@*/

static void setTokLength (int len) 
{
  addColumn (len);
  tokLength = len;
  DPRINTF (("Set tok length: %d", len));
}

static void setTokLengthT (size_t len)
{
  setTokLength (size_toInt (len));
}

# include "flex.head"

/*@-unrecog@*/ /*@i5343@*/

%}

%%

"/*"		{ llfatalbug (cstring_makeLiteral ("Comment in pre-processor output")); }

"#"{Letter}({Letter}|{Digit})*  { 
                 context_saveLocation (); 
                 setTokLength (longUnsigned_toInt (mstring_length (yytext))); 

                 if (processHashIdentifier (makeIdentifier (yytext + 1)))
                   {
		     if (lastWasString)
		       {
			 /* was nothing! */ /*@i32@*/
			 RETURN_STRING (cstring_makeLiteral ("\"\""));
		       }
		     else
		       {
			 RETURN_STRING (cstring_makeLiteral ("\"\""));
		       }
		   }
                 else
                   { 
		     if (handleSpecial (yytext)) 
                       { 
			 setTokLength (1); 
			 RETURN_TOK (0); 
		       }
		   }
                } 
"#"             { if (handleSpecial (yytext)) 
                    { 
                       setTokLength (1); RETURN_TOK (0); 
                     }
                }
"..."           { setTokLength (3); RETURN_TOK (CTOK_ELIPSIS); }
"break"		{ setTokLength (5); RETURN_TOK (BREAK); }
"case"		{ setTokLength (4); RETURN_TOK (CASE); }
"continue"	{ setTokLength (8); RETURN_TOK (CONTINUE); }
"default"	{ setTokLength (7); RETURN_TOK (DEFAULT); }
"do"		{ setTokLength (2); RETURN_TOK (DO); }
"else"		{ setTokLength (4); RETURN_TOK (CELSE); }
"for"		{ setTokLength (3); RETURN_TOK (CFOR); }
"goto"		{ setTokLength (4); RETURN_TOK (GOTO); }
"if"		{ setTokLength (2); RETURN_TOK (CIF); }
"return"	{ setTokLength (6); RETURN_TOK (RETURN); }
"sizeof"	{ setTokLength (6); RETURN_TOK (CSIZEOF); }
"offsetof"	{ setTokLength (8); RETURN_TOK (COFFSETOF); }
"switch"	{ setTokLength (6); RETURN_TOK (SWITCH); }
"while"		{ setTokLength (5); RETURN_TOK (WHILE); }
"va_arg"        { setTokLength (6); RETURN_TOK (VA_ARG); }   
"va_dcl"        { setTokLength (6); RETURN_TOK (VA_DCL); }   
"inline"        { 
                  /* gcc extension...this might not be appropriate */
                  setTokLength (6); RETURN_TOK (QINLINE); }

"struct"        { setTokLength (6); RETURN_TOK (CSTRUCT); }  
"typedef"       { setTokLength (7); RETURN_TOK (CTYPEDEF); }

"union"         { setTokLength (5); RETURN_TOK (CUNION); }
"enum"          { setTokLength (4); RETURN_TOK (CENUM); }

"void"		{ setTokLength (4); RETURN_TYPE (CVOID, ctype_void); }
"int"		{ setTokLength (3); RETURN_TYPE (CINT, ctype_int); }
"double"	{ setTokLength (6); RETURN_TYPE (CDOUBLE, ctype_double); }
"char"		{ setTokLength (4); RETURN_TYPE (CGCHAR, ctype_char); }
"float"		{ setTokLength (5); RETURN_TYPE (CGFLOAT, ctype_float); }

"long"		{ setTokLength (4); RETURN_TOK (QLONG); }
"short"		{ setTokLength (5); RETURN_TOK (QSHORT); }
"unsigned"	{ setTokLength (8); RETURN_TOK (QUNSIGNED); }
"signed"	{ setTokLength (6); RETURN_TOK (QSIGNED); }

"volatile"	{ setTokLength (8); RETURN_TOK (QVOLATILE); }
"const"		{ setTokLength (5); RETURN_TOK (QCONST); }

                        /* some systems expect this! [gack!] */ 
"__const"	{ setTokLength (7); RETURN_TOK (QCONST); }

"extern"	{ setTokLength (6); RETURN_TOK (QEXTERN); }
"auto"		{ setTokLength (4); RETURN_TOK (QAUTO); }
"register"	{ setTokLength (8); RETURN_TOK (QREGISTER); }
"static"	{ setTokLength (6); RETURN_TOK (QSTATIC); }

\"(\\.|[^\\"])*\"([ \t\n]*\"(\\.|[^\\"])*\")* { RETURN_EXPR (processString ()); }
L\"(\\.|[^\\"])*\"([ \t\n]*\"(\\.|[^\\"])*\")* { RETURN_EXPR (processWideString ()); }
"out"                   { return (processSpec (QOUT)); }
"in"                    { return (processSpec (QIN)); }
"partial"               { return (processSpec (QPARTIAL)); }
"special"               { return (processSpec (QSPECIAL)); }
"anytype"               { return (processSpec (QANYTYPE)); }
"integraltype"          { return (processSpec (QINTEGRALTYPE)); }
"unsignedintegraltype"  { return (processSpec (QUNSIGNEDINTEGRALTYPE)); }
"signedintegraltype"    { return (processSpec (QSIGNEDINTEGRALTYPE)); }
"keep"                  { return (processSpec (QKEEP)); }
"null"                  { return (processSpec (QNULL)); } 
"notnull"               { return (processSpec (QNOTNULL)); } 
"isnull"                { return (processSpec (QISNULL)); } 
"truenull"              { return (processSpec (QTRUENULL)); } 
"falsenull"             { return (processSpec (QFALSENULL)); } 
"nullwhentrue"          { return (processSpec (QTRUENULL)); } 
"nullwhenfalse"             { return (processSpec (QFALSENULL)); } 
"relnull"               { return (processSpec (QRELNULL)); }
"reldef"                { return (processSpec (QRELDEF)); }
"exposed"               { return (processSpec (QEXPOSED)); }
"newref"                { return (processSpec (QNEWREF)); }
"tempref"               { return (processSpec (QTEMPREF)); }
"killref"               { return (processSpec (QKILLREF)); }
"refcounted"            { return (processSpec (QREFCOUNTED)); }
"checked"               { return (processSpec (QCHECKED)); }
"checkmod"              { return (processSpec (QCHECKMOD)); }
"checkedstrict"         { return (processSpec (QCHECKEDSTRICT)); }
"unchecked"             { return (processSpec (QUNCHECKED)); }
"only"                  { return (processSpec (QONLY)); }
"owned"                 { return (processSpec (QOWNED)); }
"observer"              { return (processSpec (QOBSERVER)); }
"dependent"             { return (processSpec (QDEPENDENT)); }
"unused"                { return (processSpec (QUNUSED)); }
"external"              { return (processSpec (QEXTERNAL)); }
"sef"                   { return (processSpec (QSEF)); }
"shared"                { return (processSpec (QSHARED)); }
"yield"                 { return (processSpec (QYIELD)); }
"undef"                 { return (processSpec (QUNDEF)); }
"killed"                { return (processSpec (QKILLED)); }
"nullterminated"        { return (processSpec (QNULLTERMINATED));}
"MaxSet"                { return (processSpec (QMAXSET));}
"MaxRead"               { return (processSpec (QMAXREAD));}
"maxSet"                { return (processSpec (QMAXSET));}
"maxRead"               { return (processSpec (QMAXREAD));}

{Letter}({Letter}|{Digit})* { int tok; 
			      context_saveLocation (); 
			      setTokLength (longUnsigned_toInt (mstring_length (yytext))); 
			      tok = processIdentifier (makeIdentifier (yytext)); 
			      if (tok != BADTOK)
				{
				  return (tok);
				}
			    }
0[xX]{H}+	        { setTokLengthT (mstring_length (yytext)); 
                          RETURN_INT (ctype_int, processHex ());  /* evs 2000-05-17 was ctype_uint */
			}
0[xX]{H}+{L}	        { setTokLengthT (mstring_length (yytext)); 
			  RETURN_INT (ctype_lint, processHex ()); }
0[xX]{H}+{L}{L}	        { setTokLengthT (mstring_length (yytext)); 
			  RETURN_INT (ctype_llint, processHex ()); }
0[xX]{H}+{U}	        { setTokLengthT (mstring_length (yytext)); 
                          RETURN_INT (ctype_uint, processHex ()); }
0[xX]{H}+{ULSuffix}     { setTokLengthT (mstring_length (yytext)); 
			  RETURN_INT (ctype_ulint, processHex ()); }
0[xX]{H}+{U}{L}{L}      { setTokLengthT (mstring_length (yytext)); 
			  RETURN_INT (ctype_ullint, processHex ()); }
0[xX]{H}+{L}{L}{U}      { setTokLengthT (mstring_length (yytext)); 
			  RETURN_INT (ctype_ullint, processHex ()); }
0{Digit}+	        { setTokLengthT (mstring_length (yytext)); 
			  RETURN_INT (ctype_int, processOctal ()); } 
0{Digit}+{U}	        { setTokLengthT (mstring_length (yytext)); 
		 	  RETURN_INT (ctype_uint, processOctal ()); } 
0{Digit}+{L}	        { setTokLengthT (mstring_length (yytext)); 
 		          RETURN_INT (ctype_lint, processOctal ()); } 
0{Digit}+{L}{L}	        { setTokLengthT (mstring_length (yytext)); 
 		          RETURN_INT (ctype_llint, processOctal ()); } 
0{Digit}+{ULSuffix}     { setTokLengthT (mstring_length (yytext)); 
	   	          RETURN_INT (ctype_ulint, processOctal ()); } 
0{Digit}+{U}{L}{L}      { setTokLengthT (mstring_length (yytext)); 
	   	          RETURN_INT (ctype_ullint, processOctal ()); } 
0{Digit}+{L}{L}{U}      { setTokLengthT (mstring_length (yytext)); 
	   	          RETURN_INT (ctype_ullint, processOctal ()); } 
{Digit}+	       { setTokLengthT (mstring_length (yytext)); 
		         RETURN_INT (ctype_int, processDec ()); } 
{Digit}+{U}	       { setTokLengthT (mstring_length (yytext)); 
		         RETURN_INT (ctype_uint, processDec ()); } 
{Digit}+{L}	       { setTokLengthT (mstring_length (yytext)); 
	      	         RETURN_INT (ctype_lint, processDec ()); } 
{Digit}+{L}{L}	       { setTokLengthT (mstring_length (yytext)); 
	      	         RETURN_INT (ctype_llint, processDec ()); } 
{Digit}+{ULSuffix}     { setTokLengthT (mstring_length (yytext)); 
	    	         RETURN_INT (ctype_ulint, processDec ()); } 
{Digit}+{U}{L}{L}      { setTokLengthT (mstring_length (yytext)); 
	    	         RETURN_INT (ctype_ullint, processDec ()); } 
{Digit}+{L}{L}{U}      { setTokLengthT (mstring_length (yytext)); 
	    	         RETURN_INT (ctype_ullint, processDec ()); } 
'(\\.|[^\\'])+'	       { setTokLengthT (mstring_length (yytext)); 
                         RETURN_CHAR (processChar ()); }
L'(\\.|[^\\'])+'       { setTokLengthT (mstring_length (yytext)); 
                         RETURN_CHAR (processChar ()); }
{Digit}+{E}[fF]        { setTokLengthT (mstring_length (yytext)); 
			 RETURN_FLOAT (ctype_float, processFloat ()); }
{Digit}+{E}[lL]        { setTokLengthT (mstring_length (yytext)); 
			 RETURN_FLOAT (ctype_ldouble, processFloat ()); }
{Digit}+{E}            { setTokLengthT (mstring_length (yytext)); 
			 RETURN_FLOAT (ctype_double, processFloat ()); }

{Digit}*"."{Digit}+({E})?[fF] { setTokLengthT (mstring_length (yytext)); 
				RETURN_FLOAT (ctype_float, processFloat ()); }
{Digit}*"."{Digit}+({E})?[lL] { setTokLengthT (mstring_length (yytext)); 
				RETURN_FLOAT (ctype_ldouble, processFloat ()); }
{Digit}*"."{Digit}+({E})?     { setTokLengthT (mstring_length (yytext)); 
				RETURN_FLOAT (ctype_double, processFloat ()); }

{Digit}+"."{Digit}*({E})?[fF]   { setTokLengthT (mstring_length (yytext)); 
				  RETURN_FLOAT (ctype_float, processFloat ()); }
{Digit}+"."{Digit}*({E})?[lL]   { setTokLengthT (mstring_length (yytext)); 
				  RETURN_FLOAT (ctype_ldouble, processFloat ()); }
{Digit}+"."{Digit}*({E})?       { setTokLengthT (mstring_length (yytext)); 
				  RETURN_FLOAT (ctype_double, processFloat ()); }

">>="		{ setTokLength (3); RETURN_TOK (RIGHT_ASSIGN); }
"<<="		{ setTokLength (3); RETURN_TOK (LEFT_ASSIGN); }
"+="		{ setTokLength (2); RETURN_TOK (ADD_ASSIGN); }
"-="		{ setTokLength (2); RETURN_TOK (SUB_ASSIGN); }
"*="		{ setTokLength (2); RETURN_TOK (MUL_ASSIGN); }
"/="		{ setTokLength (2); RETURN_TOK (DIV_ASSIGN); }
"%="		{ setTokLength (2); RETURN_TOK (MOD_ASSIGN); }
"&="		{ setTokLength (2); RETURN_TOK (AND_ASSIGN); }
"^="		{ setTokLength (2); RETURN_TOK (XOR_ASSIGN); }
"|="		{ setTokLength (2); RETURN_TOK (OR_ASSIGN); }
">>"		{ setTokLength (2); RETURN_TOK (RIGHT_OP); }
"<<"		{ setTokLength (2); RETURN_TOK (LEFT_OP); }
"++"		{ setTokLength (2); RETURN_TOK (INC_OP); }
"--"		{ setTokLength (2); RETURN_TOK (DEC_OP); }
"->"		{ setTokLength (2); RETURN_TOK (ARROW_OP); }
"&&"		{ setTokLength (2); RETURN_TOK (AND_OP); }
"||"		{ setTokLength (2); RETURN_TOK (OR_OP); }
"<="		{ setTokLength (2); RETURN_TOK (LE_OP); }
">="		{ setTokLength (2); RETURN_TOK (GE_OP); }
"=="		{ setTokLength (2); RETURN_TOK (EQ_OP); }
"!="		{ setTokLength (2); RETURN_TOK (NE_OP); }
";"		{ setTokLength (1); RETURN_TOK (TSEMI); }
"{"		{ setTokLength (1); RETURN_TOK (TLBRACE); }
"}"		{ setTokLength (1); RETURN_TOK (TRBRACE); }
","		{ setTokLength (1); RETURN_TOK (TCOMMA); }
":"		{ setTokLength (1); RETURN_TOK (TCOLON); }
"="		{ setTokLength (1); RETURN_TOK (TASSIGN); }
"("		{ setTokLength (1); RETURN_TOK (TLPAREN); }
")"		{ setTokLength (1); RETURN_TOK (TRPAREN); }
"["		{ setTokLength (1); RETURN_TOK (TLSQBR); }
"]"		{ setTokLength (1); RETURN_TOK (TRSQBR); }
"."		{ setTokLength (1); RETURN_TOK (TDOT); }
"&"		{ setTokLength (1); RETURN_TOK (TAMPERSAND); }
"!"		{ setTokLength (1); RETURN_TOK (TEXCL); }


"~"		{ setTokLength (1); RETURN_TOK (TTILDE); }
"-"		{ setTokLength (1); RETURN_TOK (TMINUS); }
"+"		{ setTokLength (1); RETURN_TOK (TPLUS); }
"*"		{ setTokLength (1); RETURN_TOK (TMULT); }
"/"		{ setTokLength (1); RETURN_TOK (TDIV); }
"%"		{ setTokLength (1); RETURN_TOK (TPERCENT); }
"<"		{ setTokLength (1); RETURN_TOK (TLT); }
">"		{ setTokLength (1); RETURN_TOK (TGT); }
"^"		{ setTokLength (1); RETURN_TOK (TCIRC); }
"|"		{ setTokLength (1); RETURN_TOK (TBAR); }
"?"		{ setTokLength (1); RETURN_TOK (TQUEST); }


"/\\"		{ setTokLength (1); RETURN_TOK (TCAND); }


[ \t\v\f]	{ incColumn (); }
\n              { context_incLineno ();
                  if (tokLength != 0) { 
		    tokLength = 0; 
		    /* No error to report 
		    voptgenerror
		      (FLG_SYNTAX, 
		       message ("Likely parse error: token spans multiple lines."),
		       g_currentloc);
		    */
		  }
		  
		  if (continueLine)
		    {
		      continueLine = FALSE;
		    }
                 else 
		   {
		     if (context_inMacro ())
		       {
			 /* Don't use RETURN_TOK */
			 yylval.tok = lltok_create (TENDMACRO, g_currentloc);
			 lastWasString = FALSE;
			 return (TENDMACRO);
		       }  
		   }
		}
"@@MR@@"        { setTokLength (6); 
		  
		  if (processMacro ()) {
		    if (context_inIterDef ()) 
		      { 
			RETURN_TOK (LLMACROITER); 
		      }
		    if (context_inIterEnd ())
		      {
			RETURN_TOK (LLMACROEND); 
		      }
		    if (context_inMacro ())
		      {
			RETURN_TOK (LLMACRO); 
		      }
		  }
		}
"@QLMR"         { if (context_inHeader () || context_inFunction ())
		    { 
		      handleMacro ();
		    }
                  else
		    {
		      int nspchar = ninput ();
		      int nspaces;

		      /* 
		      ** This is a hack to get the column number correct.
		      */

		      llassert (nspchar >= '0' && nspchar <= '9');
		      
		      nspaces = nspchar - '0';

		      setTokLength (5 + nspaces); 
		      
		      if (processMacro ()) 
			{
			  DPRINTF (("Here we are: %s", context_unparse ()));
			  
			  if (context_inIterDef ()) 
			    {
			      RETURN_TOK (LLMACROITER); 
			    }
			  if (context_inIterEnd ())
			    {
			      RETURN_TOK (LLMACROEND); 
			    }
			  if (context_inMacro ())
			    { 
			      RETURN_TOK (LLMACRO); 
			    }
			}
		    }
		}
"@.CT"          { setTokLength (4); lldiagmsg (ctype_unparseTable ()); }
"@.FA"          { setTokLength (4); lldiagmsg (message ("Access types: %q", typeIdSet_unparse (context_fileAccessTypes ()))); }
"@.F"           { setTokLength (3); 
		  lldiagmsg (message ("%q: *** marker ***", fileloc_unparse (g_currentloc)));
		}
"@.L"           { setTokLength (3); usymtab_printLocal (); }
"@.A"           { setTokLength (3); lldiagmsg (usymtab_unparseAliases ()); }
"@.C"           { setTokLength (3); lldiagmsg (context_unparse ()); }
"@.W"           { setTokLength (3); lldiagmsg (context_unparseClauses ()); }
"@.G"           { setTokLength (3); usymtab_printGuards (); }
"@.S"           { setTokLength (3); usymtab_printOut (); }
"@.X"           { setTokLength (3); usymtab_printAll (); }
"@.Z"           { setTokLength (3); usymtab_printComplete (); }
"@.T"           { setTokLength (3); usymtab_printTypes (); }
"@.K"           { setTokLength (3); lldiagmsg (usymtab_unparseStack ()); }
"@.M"           { setTokLength (3); 
		  lldiagmsg (message ("Can modify: %q", 
				  sRefSet_unparse (context_modList ()))); 
		}
"%{"            { /* BEFORE_COMMENT_MARKER */
                  int tok; 
		  incColumn (); incColumn ();
		  tok = handleLlSpecial (); 

		  if (tok != BADTOK)
		    {
		      if (tok == CANNOTATION) {
			return (tok);
		      } else {
			/* Beware - this bashes yylval! */
			RETURN_TOK (tok); 
		      }
		    }
		}
"%}"            { /* AFTER_COMMENT_MARKER */ 
                  setTokLength (2);
		  inSpecPart = FALSE;
		  RETURN_TOK (QENDMACRO); }
"\\"            { incColumn (); continueLine = TRUE; }
.		{ incColumn (); 
                  if ((int) *yytext == 13 ) {
		     ;
                  } else {
		    voptgenerror
		      (FLG_SYNTAX, 
		       message ("Invalid character (ascii: %d), skipping character",
				(int)(*yytext)),
		       g_currentloc);
		  }
		}
%%

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

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

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 } ,
  { "setBufferSize", QSETBUFFERSIZE},
  { "setStringLength", QSETSTRINGLENGTH},
  { "testinRange", QTESTINRANGE},
  { "requires", QPRECLAUSE } ,
  { "ensures", QPOSTCLAUSE } ,
  { "invariant", QINVARIANT} ,
  { NULL, BADTOK } 
} ;

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

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 } ,
  { "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 }, 
  { "setBufferSize", QSETBUFFERSIZE },
  { "testInRange", QTESTINRANGE},
  { "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 } 
} ;

/*
** 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 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, yyin);
	      return;
	    }
	}
    }

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

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;
	    }
	  
	  return s_keytable[i].token;
	}
      
      i++;
    }
  
  return BADTOK;
}

static int lminput ()
{
  if (savechar == '\0')
    {
      incColumn ();
      return (input ());
    }
  else
    {
      int save = (int) savechar;
      savechar = '\0';
      return save;
    }
}

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

static int returnFloat (ctype ct, double f)
{
  yylval.expr = exprNode_floatLiteral (f, ct, cstring_fromChars (yytext), 
				       fileloc_decColumn (g_currentloc, tokLength));
  tokLength = 0; 
  return (CCONSTANT);
}

static int returnInt (ctype ct, long i)
{
  ctype c = ct;

  if (ctype_equal (ct, ctype_int))
    {
      if (i == 0)
	{
	  c = context_typeofZero ();
	}
      else if (i == 1)
	{
	  c = context_typeofOne ();
	}
    }
  
  yylval.expr = exprNode_numLiteral (c, cstring_fromChars (yytext), 
				     fileloc_decColumn (g_currentloc, tokLength), i);   
  tokLength = 0; 
  return (CCONSTANT);
}

static int returnChar (char c)
{
  yylval.expr = exprNode_charLiteral (c, cstring_fromChars (yytext), 
				      fileloc_decColumn (g_currentloc, tokLength));
  tokLength = 0; 
  return (CCONSTANT);
}

static int ninput ()  
{
  int c = lminput ();

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

  return c;
}

static char macro_nextChar ()
{
  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); /*@i23@*/
	  if (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);
	}
    }
  return c;
}

/*
** keeps semantic comments
*/

static char macro_nextCharC ()
{
  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);
	}
    }
  return c;
}

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

static char skip_whitespace ()
{
  char c;

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

  return c;
}

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

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

  c = macro_nextCharC ();

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

      for (i = 0; i < ((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 ();
    }
}

static bool 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 isenditer = 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 ();
	    }
	  unput (c);
	}

      if (c == '\n')
	{
	  emptyMacro = TRUE;
	  unput (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))
		{
		  isenditer = TRUE;
		  uentry_setDefined (e2, loc);
		  context_enterIterEnd (e2); /* don't care about it now */
		  /* but should parse like an iter! */
		}
	      else if (uentry_isConstant (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_SYNTAX,
				 message 
				 ("Macro constant name %s does not match name in "
				  "previous constant declaration.  This constant "
				  "is declared at %q", fname, 
				  fileloc_unparse (oloc)),
				 loc);
			    }
			}

		      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);

		      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));

	      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)));
	      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)
		{
		  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;
}

static bool handleSpecial (char *yyt)
{
  char *l = mstring_create (MAX_NAME_LENGTH);
  int lineno = 0;
  char c;
  char *ol;
  cstring olc;
  
  strcpy (l, yyt + 1);

  /* Need to safe original l for deallocating. */
  ol = l;

  l += strlen (yyt) - 1;
  
  while ((c = char_fromInt (lminput ())) != '\n' && c != '\0')
    {
      *l++ = c;
    }

  *l = '\0';
  olc = cstring_fromChars (ol);
  
  if (cstring_equalPrefixLit (olc, "pragma"))
    {
      char *pname = mstring_create (longUnsigned_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);
	}
    }
  else if (cstring_equalPrefixLit (olc, "ident"))
    {
      /* Some pre-processors will leave these in the code.  Ignore rest of 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(MSDOS) || 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 = removePreDirs (fname);
	    fid = fileTable_lookupBase (context_fileTable (), fname);
	  }
      }
# else  /* !defined(OS2) && !defined(MSDOS) */
      fname = removePreDirs (fname);
      fid = fileTable_lookupBase (context_fileTable (), fname);      
# endif /* !defined(OS2) && !defined(MSDOS) */
      
      if (!(fileId_isValid (fid)))
	{
	  if (context_inXHFile ())
	    {
	      fid = fileTable_addXHFile (context_fileTable (), fname);
	    }
	  else if (isHeaderFile (fname))
	    {
	      fid = fileTable_addHeaderFile (context_fileTable (), fname);
	    }
	  else
	    {
	      fid = fileTable_addFile (context_fileTable (), 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);
	return FALSE;
      } else {
	voptgenerror
	  (FLG_UNRECOGDIRECTIVE,
	   message ("Unrecognized pre-processor directive: #%s", 
		    cstring_fromChars (ol)),
	   g_currentloc);
      }
      
      sfree (ol);
      return FALSE; /* evans 2001-12-30: was: TRUE; */
    }

  sfree (ol);
  return FALSE;
}
  
static int handleLlSpecial ()
{ 
  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 = 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 = ninput ();

      llassert (ic == 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)
    {
      tokLength = charsread;
      sfree (os);
      inSpecPart = TRUE;
      fileloc_free (loc);
      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 = ninput ()) != 0) && (ic != EOF)
	     && (ic != 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. */
	    tokLength = 0; 

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

	  s = mstring_append (s, c);
	  charsread++;
	}
    }

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

  if (ic == AFTER_COMMENT_MARKER[0]) 
    {
      int nc = 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_isInvalid (fflag))
		{
		  if (isMode (flagname))
		    {
		      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
		    {
		      voptgenerror
			(FLG_UNRECOGFLAGCOMMENTS,
			 message ("Unrecognized option in semantic comment: %s", 
				  flagname),
			 g_currentloc);
		    }
		}
	      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),
		     g_currentloc);
		}
	      else
		{
		  context_fileSetFlag (fflag, set);
		  
		  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),
			     g_currentloc);
			}
		      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++;
			    }
			  
			  sfree (orest);
			  
			  if (cstring_isUndefined (extra))
			    {
			      llerror 
				(FLG_BADFLAG,
				 message
				 ("Flag %s (in semantic comment) must be followed by an argument",
				  flagcode_unparse (fflag)));
			    }
			  else
			    {
			      s--;
			      
			      if (flagcode_hasNumber (fflag))
				{
				  setValueFlag (fflag, extra);
				}
			      else if (flagcode_hasChar (fflag))
				{
				  setValueFlag (fflag, extra);
				}
			      else if (flagcode_hasString (fflag))
				{
				  setStringFlag (fflag, extra);
				}
			      else
				{
				  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));
	}
      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)
	{
	  tokLength = hasnl ? 0 : 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)));
	/*@i324@*/ yylval.annotation = ainfo;
	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))) 
	    {
	      DPRINTF (("Add comment: %s", os));
	      context_addComment (cstring_fromCharsNew (os));
	    }
	  else
	    {
	      ; 
	    }
	  
	  if (tchar != '\0')
	    {
	      *(s-1) = '\0';
	    }
	}

      if (mstring_equal (t, "ignore"))
	{
	  if (!context_getFlag (FLG_NOCOMMENTS))
	    {
	      context_enterSuppressRegion ();
	    }
	}
      else if ((*t == 'i' || *t == 't')
	       && (*(t + 1) == '\0'))
	{
	  if (!context_getFlag (FLG_NOCOMMENTS)
	      && (*t == 'i' || context_getFlag (FLG_TMPCOMMENTS)))
	    {
	      context_enterSuppressLine (-1); /* 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 += lc - '0';
		  lc = *(++tt);
		}
	    }

	  
	  if (!context_getFlag (FLG_NOCOMMENTS)
	      && (!tmpcomment || context_getFlag (FLG_TMPCOMMENTS)))
	    {
	      context_enterSuppressLine (val);
	    }
	}
      else if (mstring_equal (t, "end"))
	{
	  if (!context_getFlag (FLG_NOCOMMENTS))
	    {
	      context_exitSuppressRegion ();
	    }
	}
      else if (mstring_equal (t, "notfunction"))
	{
	 ; /* handled by pcpp */
	}
      else if (mstring_equal (t, "access"))
	{
	  cstring tname;
	  
	  while (TRUE)
	    {
	      while ((c = *s) && (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),
			     g_currentloc);
			}
		    }
		  else
		    {
		      if (!(context_inSuppressRegion ()
			    || context_inSuppressZone (g_currentloc)))
			{
			  voptgenerror
			    (FLG_COMMENTERROR,
			     message
			     ("Unrecognized type %s used in access comment",
			      tname),
			     g_currentloc);
			}
		    }
		}
	      
	      if (c != '\0') 
		{
		  s++;
		}
	      
	      if (c != ',' && c != ' ')
		{
		  break;
		}
	    }
	}
      else if (mstring_equal (t, "noaccess"))
	{
	  cstring tname;
	  char lc;
	  
	  while (TRUE)
	    {
	      while ((lc = *s) && (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 (g_currentloc)))
			    {
			      uentry ue = usymtab_getTypeEntry (tuid);
			      
			      if (uentry_isAbstractDatatype (ue))
				{
				  voptgenerror
				    (FLG_COMMENTERROR,
				     message
				     ("Non-accessible abstract type %s used in noaccess comment",
				      tname),
				     g_currentloc);
				}
			      else
				{
				  voptgenerror
				    (FLG_COMMENTERROR,
				     message
				     ("Non-abstract type %s used in noaccess comment",
				      tname),
				     g_currentloc);
				}
			    }
			}
		    }
		  else
		    {
		      if (!(context_inSuppressRegion () 
			    || context_inSuppressZone (g_currentloc)))
			{
			  voptgenerror
			    (FLG_COMMENTERROR,
			     message
			     ("Unrecognized type %s used in noaccess comment",
			      tname),
			     g_currentloc);
			}
		    }
		}
	      
	      if (lc != '\0') 
		{
		  s++;
		}
	      
	      if (lc != ',' && lc != ' ')
		{
		  break;
		}
	    }
	}
      else
	{
	  voptgenerror (FLG_UNRECOGCOMMENTS, 
			message ("Semantic comment unrecognized: %s", 
				 cstring_fromChars (os)), loc);
	}

      sfree (t);
    }
  
  sfree (os);
  fileloc_free (loc);
  return BADTOK;
}

static /*@only@*/ cstring makeIdentifier (char *s)
{
  char *c = mstring_create (size_toInt (strlen (s)) + 1);
  cstring id = cstring_fromChars (c);

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

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

/*@observer@*/ /*@dependent@*/ uentry 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, coerceId, but doesn't supercede for iters
*/

/*@observer@*/ uentry coerceIterId (cstring cn)
{
  if (!(usymtab_exists (cn)))
    {
      return uentry_undefined;
    }
  
  return (usymtab_lookup (cn));
}

/*@observer@*/ cstring LastIdentifier ()
{
  return (lastidprocessed);
}

static int processIdentifier (cstring id)
{
  uentry le;

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

  context_clearJustPopped ();
  lastidprocessed = id; 

  if (context_inFunctionHeader ())
    {
      int tok = commentMarkerToken (id);
      DPRINTF (("in function decl..."));

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

	  if (tok != BADTOK)
	    {
	      return tok;
	    }
	  else 
	    {
	      annotationInfo ainfo;

	      if (expectingMetaStateName) 
		{
		  metaStateInfo msinfo = context_lookupMetaStateInfo (id);

		  if (metaStateInfo_isDefined (msinfo))
		    {
		      yylval.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)));
		  /*@i324@*/ yylval.annotation = ainfo;
		  return CANNOTATION;
		}
	      else
		{
		  DPRINTF (("Not annotation: %s", id));
		}
	    }
	}
    }

  /* Consider handling: Defined by C99 as static const char __func__[] */

  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, "__signed"))
	{
	  tok = QSIGNED;
	}
      else if (cstring_equalLit (id, "__unsigned"))
	{
	  tok = QUNSIGNED;
	}
      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, "__FUNCTION__")
	       || cstring_equalLit (id, "__PRETTY_FUNCTION__")) 
	{
	  /* These tokens hold the name of the current function as strings */
	  /* evans 2001-12-30: changed from exprNode_stringLiteral; bug reported by Jim Zelenka. */
	  yylval.expr = exprNode_makeConstantString (id, fileloc_copy (g_currentloc));
	  tokLength = 0;
	  lastWasString = TRUE;
	  tok = CCONSTANT;
	  return tok;
	}
      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 = input ()) != EOF)
	    {
	      		      
	      if (inescape)
		{
		  inescape = FALSE;
		}
	      else if (ic == '\\')
		{
		  inescape = TRUE;
		}
	      else if (ic == '\"')
		{
		  inquote = !inquote;
		}
	      else if (!inquote)
		{
		  if (ic == '(')
		    {
		      if (!useparens)
			{
			  if (!usebraces)
			    {
			      useparens = TRUE;
			    }
			}

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

		      if (usebraces)
			{
			  depth++;
			}
		    }
		  else if (ic == ')' && useparens)
		    {
		      depth--;
		      if (depth == 0) break;
		    }
		  else if (ic == '}' && usebraces)
		    {
		      depth--;
		      if (depth == 0) break;
		    }
		  else if (ic == '}' 
			   && !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;
		    }
		}

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

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

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

  le = usymtab_lookupSafe (id);

  /*@-dependenttrans@*/
  
  if (uentry_isIter (le))
    {
      /*@i32@*/ yylval.entry = le;
      return (ITER_NAME);
    }
  else if (uentry_isEndIter (le))
    {
      /*@i32@*/ yylval.entry = le;
      return (ITER_ENDNAME);
    }
  else if (uentry_isUndefined (le))
    {
      yylval.cname = id;

      /* avoid parse errors for certain system built ins */

      if (g_expectingTypeName && (cstring_firstChar (id) == '_')
	  && (cstring_secondChar (id) == '_'))
	{
	  return (TYPE_NAME_OR_ID);
	}

      return (NEW_IDENTIFIER);
    }
  else if (!uentry_isDeclared (le) && !uentry_isCodeDefined (le))
    {
      if (uentry_isDatatype (le))
	{
	  yylval.cname = id;
	  return (NEW_IDENTIFIER);
	}
      else
	{
	  /*@i32@*/ yylval.entry = le;	  	  
	  return (IDENTIFIER); 
	}
    }
  else if (uentry_isDatatype (le))
    {
      if (!g_expectingTypeName)
	{
	  yylval.cname = id;

	  return (NEW_IDENTIFIER);
	}
      else
	{
	  yylval.ctyp = uentry_getAbstractType (le);
	  
	  uentry_setUsed (le, g_currentloc);
	  return (TYPE_NAME);
	}
    }
  else
    {
      /*@i32@*/ yylval.entry = le;            
      return (IDENTIFIER); 
    }

  /*@=dependenttrans@*/
}

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

      lastidprocessed = id; 
      le = usymtab_lookupSafe (id);

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

      cstring_free (id);
      return FALSE;
    }
}


static /*@only@*/ exprNode processString ()
{
  exprNode res;
  fileloc loc;
  char *nl = strchr (yytext, '\n');
  cstring ns = cstring_fromCharsNew (yytext);

  if (nl == NULL)
    {
      loc = fileloc_copy (g_currentloc);
      addColumn (cstring_length (ns));
    }
  else
    {
      char *lastnl = nl;

      loc = fileloc_copy (g_currentloc);

      context_incLineno ();
      
      while ((nl = strchr ((nl + 1), '\n')) != NULL)
	{
	  context_incLineno ();
	  lastnl = nl;
	}
    }

    
  res = exprNode_stringLiteral (ns, loc);
  return (res);
}

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

static /*@only@*/ exprNode processWideString ()
{
  exprNode res;
  fileloc loc;
  char *nl = strchr (yytext, '\n');
  cstring ns;

  llassert (*yytext == 'L');
  yytext++;

  ns = cstring_fromCharsNew (yytext);
  
  if (nl == NULL)
    {
      loc = fileloc_copy (g_currentloc);
      addColumn (cstring_length (ns));
    }
  else
    {
      char *lastnl = nl;

      loc = fileloc_copy (g_currentloc);

      context_incLineno ();
      
      while ((nl = strchr ((nl + 1), '\n')) != NULL)
	{
	  context_incLineno ();
	  lastnl = nl;
	}
    }

  res = exprNode_wideStringLiteral (ns, loc);
  return (res);
}

static 
char processChar ()
{
  char fchar;
  char next;

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

static
double processFloat ()
{
  double ret = atof (yytext);

    return (ret);
}

static
long processHex ()
{
  int index = 2;
  long val = 0;

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

  while (yytext[index] != '\0') {
    int tval;
    char c = yytext[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 (yytext[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 (yytext)),
	     g_currentloc);
	}
	index++;
      }

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

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

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

static
long processOctal ()
{
  int index = 1;
  long val = 0;

  llassert (yytext[0] == '0' && yytext[1] != 'X' && yytext[1] != 'x');
    
  while (yytext[index] != '\0') {
    int tval;
    char c = yytext[index];
    
    if (c >= '0' && c <= '7') {
      tval = (int) c - (int) '0';
    } else if (c == 'U' || c == 'L' || c == 'u' || c == 'l') {
      index++;
      while (yytext[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 (yytext)),
	     g_currentloc);
	}
	index++;
      }

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

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

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

static
long processDec ()
{
  return (atol (yytext));
}

static int
processSpec (int tok)
{
  size_t length = strlen (yytext);
  
  if (inSpecPart)
    {
      setTokLengthT (length);
      RETURN_TOK (tok);
    }
  else
    {
      
      context_saveLocation ();
      setTokLengthT (length);
      return (processIdentifier (makeIdentifier (yytext)));
    }
}

void cscanner_expectingMetaStateName ()
{
  llassert (!expectingMetaStateName);
  llassert (context_inFunctionHeader ());
  expectingMetaStateName = TRUE;
}

void cscanner_clearExpectingMetaStateName ()
{
  llassert (expectingMetaStateName);
  expectingMetaStateName = FALSE;
}
