/* $Id: cparse.c,v 1.13 2009/02/25 18:09:13 kiesling Exp $ */

/*
  This file is part of Ctalk.
  Copyright © 2005-2009 Robert Kiesling, ctalk@ctalklang.org.
  Permission is granted to copy this software provided that this copyright
  notice is included in all source code modules.

  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.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software Foundation, 
  Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <errno.h>
#include "object.h"
#include "message.h"
#include "ctalk.h"
#include "lex.h"
#include "typeof.h"
#include "cvar.h"
#include "parser.h"

extern int error_line, error_column;  /* Declared in parser.c.             */
extern int warn_extension_opt;        /* Declared in main.c.               */
extern int verbose_opt;
extern I_PASS interpreter_pass;
int main_declaration = FALSE;         /* Used by the parser to determine
					 when to output the initialization
					 code. */
Boolean constant_expr = False;        /* True if syntax requires a 
					 constant expression.              */
Boolean typedef_check = False;        /* True if parsing a typedef.        */
MESSAGE *c_messages[N_MESSAGES + 1];  /* C language message stack and      */
int c_message_ptr = N_MESSAGES;       /* stack pointer.  Also used by      */
                                      /* method_param () in primitives.c,  */
                                      /* and in control.c.                 */
int preamble = TRUE;                  /* True until the first function 
					 declaration in the module is
					 reached. */
extern EXCEPTION parse_exception;     /* Declared in pexcept.c.            */

extern int format_method_output;      /* Declared in methodbuf.c.          */

DECLARATION_CONTEXT declaration_context = decl_null_context;

extern METHOD *new_methods[MAXARGS+1]; /* Declared in lib/rtnwmthd.c.      */
extern int new_method_ptr;

extern int ctrlblk_pred,               /* Declared in control.c.           */
  ctrlblk_blk,
  ctrlblk_else_blk;

extern OBJECT *rcvr_class_obj;

int get_c_message_ptr (void) {
  return c_message_ptr;
}

MESSAGE_STACK c_message_stack (void) {
  return c_messages;
}

MESSAGE *c_message_at (int idx) {
  return c_messages[idx];
}

int c_message_push (MESSAGE *m) {
  if (c_message_ptr == 0) {
    warning (m, "c_message_push: stack overflow.");
    return ERROR;
  }
  c_messages[c_message_ptr--] = m;
  return c_message_ptr;
}

/*
 *   Return true if a block closure is a function or method closure.
 *   The function checks for a scope change from the frame before
 *   or the frame after the closing brace.
 *
 *   If there are no statements between the end of a function and the 
 *   declaration of the next function, the scope can change from LOCAL_VAR 
 *   to PARAM_VAR.
 */
int fn_closure (MESSAGE_STACK messages, int msg_ptr, int frame) {

  int last_frame;

  /* Make sure that there are frames. */
  if ((interpreter_pass != parsing_pass) &&
      (interpreter_pass != library_pass) && 
      (interpreter_pass != method_pass))
    _error ("fn_closure called without frames.");

  if (messages[msg_ptr] -> tokentype != CLOSEBLOCK)
    _error ("Not a close block character in fn_closure ().\n");

  if ((last_frame = get_frame_pointer ()) >= MAXFRAMES)
    return FALSE;

  if (frame == last_frame) {  
                              /* If the closure is the last frame. */
                              /* We should also have checked for a */
                              /* syntax error.                     */
    if (frame_at (frame + 1) -> scope == LOCAL_VAR)
      return TRUE;
  } else {                    
    if (((frame_at (frame + 1) -> scope == LOCAL_VAR) && 
	 (frame_at (frame)     -> scope != LOCAL_VAR)) ||
	((frame_at (frame)     -> scope == LOCAL_VAR) &&
	 (frame_at (frame - 1) -> scope != LOCAL_VAR)))
      return TRUE;
  }

  return FALSE;
}

/*
 *   Determines whether a declaration is a function prototype.
 */

/* 
 *  Also used by is_c_fn_declaration_msg.
 *  TO DO - The function should also parse the arguments.
 */

int return_is_unsigned;         /* Return value is unsigned or a pointer. */
int return_is_ptr;
int return_is_ptr_ptr;
int decl_is_ptr;
int decl_is_ptr_ptr;
int decl_is_prototype;

/* char declarators[10][MAXLABEL]; */ /* Stack of declarator labels. */
FN_DECLARATOR declarators[10];
int n_declarators;

static int add_fn_declarator (char *s) {
  if (is_ctalk_keyword (s)) return ERROR;
  declarators[n_declarators].attr = is_c_data_type_attr (s);
  strcpy (declarators[n_declarators].name, s);
  n_declarators++;
  return SUCCESS;
}

int is_c_function_prototype_declaration (MESSAGE_STACK messages, int msg_ptr) {

  int i,
    lookahead,
    stack_end,
    return_val;
  Boolean param_start,
    param_end;
  MESSAGE *m;
  /*
   *  If the scope isn't global, then return False.
   *  If parsing a library between methods, then the
   *  scope is also global.
   */
  if ((interpreter_pass == parsing_pass) ||
      (interpreter_pass == library_pass) ||
      (interpreter_pass == method_pass))
    if ((frame_at (current_parser () -> frame)) -> scope != GLOBAL_VAR)
      return FALSE;

  stack_end = get_stack_top (messages);

  param_start = param_end = False;

  for (i = msg_ptr, n_declarators = 0, 
	 return_is_ptr = return_is_ptr_ptr = return_val = FALSE; 
       i > stack_end; i--) {

    m = messages[i];

    if ((m -> tokentype == WHITESPACE) || (m -> tokentype == NEWLINE))
      continue;

    switch (m -> tokentype)
      {
      case LABEL:
      case CTYPE:
	if (param_start != True)
	  if (add_fn_declarator (m -> name) == ERROR) return FALSE;
	break;
      case OPENPAREN:
	param_start = True;
	break;
      case CLOSEPAREN:
	param_end = True;
	break;
      case ARRAYOPEN:
      case ARRAYCLOSE:
	return_val = FALSE;
	goto done;
	break;
      case SEMICOLON:
	if (param_end == True)
	  return_val = TRUE;
	goto done;
	break;
      case ASTERISK:
	if (param_start == False) {
	  for (lookahead = i-1; lookahead > stack_end; lookahead--) {
	    if ((messages[lookahead]->tokentype == NEWLINE) ||
		(messages[lookahead]->tokentype == WHITESPACE))
	      continue;
	    else
	      break;
	  }
	  if (messages[lookahead] -> tokentype == ASTERISK) {
	    return_is_ptr_ptr = TRUE;
	    i = lookahead;
	  } else {
	    return_is_ptr = TRUE;
	  }
	}
	break;
      case OPENBLOCK:
	if (param_end == True)
	  return_val = FALSE;
	goto done;
	break;
      case EQ:  /* Break off if we see an initializer. */
	return_val = FALSE;
	goto done;
	break;
      default:
	break;
      }
  }

 done:

  return return_val;
}

/* 
 *  Called with a message pointer in the function declaration, before
 *  the opening brace.  Returns the parser message stack index of the
 *  closing brace.
 */
int find_function_close (MESSAGE_STACK messages, int msg_ptr) {

  int ptr;
  int last_msg_ptr;
  int block_level = 0;
  int in_func = FALSE;
  
  last_msg_ptr = get_stack_top (messages);

  for (ptr = msg_ptr; ptr > last_msg_ptr; ptr--) {
    if (messages[ptr] -> tokentype == OPENBLOCK) {
      ++block_level;
      if (!in_func)
	in_func = TRUE;
    }
    if (messages[ptr] -> tokentype == CLOSEBLOCK) {
      --block_level;

      if (in_func && !block_level)
	return ptr;
    }

    /* Prototype declaration. */
    if ((messages[ptr] -> tokentype == SEMICOLON) && !block_level)
      return ptr;
  }
  return ERROR;
}

/*
 *    A variable declaration ends at the first semicolon outside  
 *    curly braces.  Also takes into account preprocessor branches.
 *    Returns the stack index of the semicolon that ends the 
 *    declaration.
 *
 *    While parsing a C function argument, if the index reaches
 *    the end of the stack, then back up the pointer by one and
 *    return that.
 */

int find_declaration_end (MESSAGE_STACK messages, int msg_ptr) {

  int i;
  int blocklevel,
    subscriptlevel,
    n_parens;
  Boolean have_function;
  int end;
  VAL macro_val;

  end = get_stack_top (messages);

  memset ((void *)&macro_val, 0, sizeof (VAL));
  macro_val.type = INTEGER_T;

  for (i = msg_ptr, subscriptlevel = 0, blocklevel = 0, n_parens = 0, 
	 have_function = False; 
       i > end; i--) {

    if (!messages[i] || !IS_MESSAGE (messages[i])) {
      ++i;
      goto done;
    }

    switch (messages[i] -> tokentype)
      {
      case OPENPAREN:
	++n_parens;
	break;
      case CLOSEPAREN:
	if (--n_parens == 0) {
	  int m_next_idx;
	  if ((m_next_idx = nextlangmsg (messages, i)) != ERROR)
	    if (M_TOK(messages[m_next_idx]) == OPENBLOCK)
	      have_function = True;
	}
	break;
      case OPENBLOCK:
	++blocklevel;
	break;
      case CLOSEBLOCK:
	--blocklevel;
	if (blocklevel == 0 && have_function)
	  goto done;
	break;
      case SEMICOLON:
	if (!blocklevel)
	  goto done;
	break;
      default:
	break;
      }
  }
  
  if ((i == end) &&
      (interpreter_pass == c_fn_pass)) {
    while (messages[i] == NULL)
      ++i;
  }

 done:
  return i;
}

/*
 *  Like find_declaration_end, but check for the end of a subscripted
 *  declaration.  That means look for commas outside of anything in
 *  a var declaration, or an initializer.  If we try to do this in 
 *  find_declaration_end, we start to get confused with method arguments, 
 *  and other elements.
 */
int find_subscript_declaration_end (MESSAGE_STACK messages, int msg_ptr) {

  int i, 
    end,
    subscriptlevel, 
    block_level;
  MESSAGE *m;

  end = get_stack_top (messages);

  for (i = msg_ptr, subscriptlevel = 0, block_level = 0; i > end; i--) {

    m = messages[i];
    if (M_ISSPACE(m)) continue;

    switch (M_TOK(m))
      {
      case ARRAYOPEN:
	++subscriptlevel;
	break;
      case ARRAYCLOSE:
	--subscriptlevel;
	break;
      case OPENBLOCK:
	++block_level;
	break;
      case CLOSEBLOCK:
	--block_level;
	break;
      case ARGSEPARATOR:
      case SEMICOLON:
	if (!subscriptlevel && !block_level)
	  goto done;
	break;
      }
  }

 done:
  return i;
}

/*
 *  Like find_declaration_end, but scans for the next ARGSEPARATOR
 *  or CLOSEPAREN token.
 */
int find_arg_end (MESSAGE_STACK messages, int i) {
  int comma_l,
    paren_l;
  comma_l = scanforward (messages, i, get_stack_top (messages), ARGSEPARATOR);
  paren_l = scanforward (messages, i, get_stack_top (messages), CLOSEPAREN);

  if (comma_l == ERROR)
    return paren_l;
  if (paren_l == ERROR)
    return comma_l;
  return (comma_l > paren_l) ? comma_l : paren_l;
}

/* Determines if a string contains a C function declaration. 
   Call with the complete function declaration.  The function 
   can also distinguish between function declarations
   and method declarations.

   For input that has already been tokenized, use 
   is_c_function_declaration_msg ().

   Returns TRUE or FALSE.

   For example: 

   int main (int argc, char **argv) {
   }

   FILE *f openfile (char *path) {
   }

   int get_number (void) {
   }

*/

/*
 *  Return true if the token is part of a function (or 
 *  or method) declaration.
 *
 *  This function is used by parse () to set frames.
 *
 *  NOTE - The function returns True even for functions with
 *  an implied return type, like "main () {}," which means
 *  that it can return True from any label in the function
 *  declaration.  When called from parse (), the declaration 
 *  should only be registered as True at the first token, in 
 *  order to avoid framing errors.
 *
 *  The variable in_ctrlblk_pred allows us to handle cases
 *  of functions within control blocks, which are often 
 *  followed by opening braces.
 *  
 *  Note that this function is only safe when calling with
 *  start_ptr starting on a label.
 */

int in_ctrlblk_pred = FALSE;

int is_c_function_declaration (char *start_ptr) {

  int i,
    start_idx,              /* Stack index of first and last token. */
    end_idx;
  int returnval = TRUE;
  int openparen = FALSE;    /* Stack index to variable decl. start. */
  int lasttoken = ERROR;    /* Saved tokens for lookback.           */
  int lasttoken_ptr= ERROR; /* Index of last non-whitespace token.  */
  int main_label = FALSE;
  int old_error_line,
    old_error_column;
  char *buf;
  char *open_block_ptr;     /* Start of function body.              */
  int n_parens;

  old_error_line = error_line;
  old_error_column = error_column;

  if ((open_block_ptr = index (start_ptr, '{')) == NULL)
    return FALSE;

  if ((buf = (char *) calloc ((size_t)(open_block_ptr - start_ptr + 2), 
			      sizeof (char))) == NULL)
    _error ("Is_c_function_declaration: %s", strerror (errno));

  strncpy (buf, start_ptr, (size_t)(open_block_ptr - start_ptr + 1));
  buf[open_block_ptr - start_ptr + 1] = 0;

  /*
   *  Handle braces within literals.
   */
  if (index (buf, '\'') || index (buf, '\"')) {
    free (buf);
    return FALSE;
  }

  start_idx = get_stack_top (c_messages);

  end_idx = tokenize (c_message_push, buf);

  if (c_messages[c_message_ptr + 1] -> tokentype != OPENBLOCK) {
    returnval = FALSE;
    goto done;
  }

  for (i = start_idx, n_parens = 0; i > end_idx; i--) {

    if (M_ISSPACE (c_messages[i])) continue;

    switch ( c_messages[i] -> tokentype )
      {
      case LABEL:
	if (!strcmp (c_messages[i] -> name, "main"))
	  main_label = TRUE;
	if (!strcmp (c_messages[i] -> name, "if") ||
	    !strcmp (c_messages[i] -> name, "while") ||
	    !strcmp (c_messages[i] -> name, "for"))
	  in_ctrlblk_pred = TRUE;
	break;
      case OPENPAREN:
	++n_parens;
	if (M_TOK(c_messages[lasttoken_ptr]) != LABEL) {
	  if (!param_is_fnptr (c_messages, i, n_parens)) {
	    returnval = FALSE;
	    goto done;
	  }
	}
	if (is_c_keyword (M_NAME(c_messages[lasttoken_ptr]))) {
	  returnval = FALSE;
	  goto done;
	}
	openparen = TRUE;
	break;
      case CLOSEPAREN:
	--n_parens;
	if (!openparen || (n_parens < 0))
	  returnval = FALSE;
	break;
      case SEMICOLON:
	returnval = FALSE;
	if (!in_ctrlblk_pred)
	  goto done;
	if (in_ctrlblk_pred && ! n_parens) {
	  in_ctrlblk_pred = FALSE;
	  goto done;
	}
	break;
      case OPENBLOCK:
	if (c_messages[lasttoken_ptr] -> tokentype != CLOSEPAREN)
	  returnval = FALSE;
	if (in_ctrlblk_pred) {
	  in_ctrlblk_pred = FALSE;
	  returnval = FALSE;
	}
	break;
      case CLOSEBLOCK:
	returnval = FALSE;
      case ARGSEPARATOR:
      case ASTERISK:
      case ELLIPSIS:
	break;
      default:
	returnval = FALSE;
	break;
      }

    lasttoken_ptr = i;
    lasttoken = c_messages[i] -> tokentype;
  }

 done:
  DELETE_MESSAGES(c_messages,c_message_ptr,N_MESSAGES)
  free (buf);

  error_line = old_error_line;
  error_column = old_error_column;

  if (returnval && main_label)
    main_declaration = TRUE;

  return returnval;
  
}

/* 
 *     Any valid label that is not an object or class reference is
 *     a C identifier.
 */

int is_c_identifier (char *s) {

  OBJECT *o;
  int i;

  for (i = 0; i < (int)strlen (s); i++) {
    if (!isalnum ((int)s[i]) && ((int)s[i] != '_'))
      return FALSE;
  }

  if ((o = get_object (s, NULL)) != NULL)
    return FALSE;

  if ((o = get_class_object (s)) != NULL)
    return FALSE;

  return TRUE;

}

/*
 *  Check if a declaration is a function.  Also, handle function
 *  function pointers.  The function does not check for C data type 
 *  declarations because a function can return an object, and the 
 *  program hasn't yet defined those.
 *
 *  A function consists of a unique tag before the opening parenthesis of a 
 *  parameter list, and the closing parenthesis of the parameter list followed 
 *  by the opening brace of the function body.  These criteria should
 *  also work for method declarations.
 *
 *  The function, function_param_states (), handles the parameter list.
 *  The return value is a data type, and this function contains
 *  a simplified version of is_c_var_declaration*.
 *
 *  TO DO - Test this more thoroughly.
 *  TO DO - Find out how far into the declaration the calling function 
 *  needs to look ahead after a successful match.  For example, 
 *  "int main (int argc, char **argv) {" is a successful declaration, 
 *  but so is "main (int argc, char **argv) {" so the calling function
 *  needs to look ahead at least past the function tag.  That means 
 *  we couldn't do macro processing or object lookup between the
 *  function tag and the start of the parameter list.
 *
 *  FIXME!  - The function returns False for declarations that have
 *  compiler-specific or non-standard extensions, like GNU C's 
 *  __THROW after the parameter list, but before the opening brace.
 */

int func_declaration_states[] = {
#include "fnstate.h"
  -1,  0
};

#define FUNCTION_DECL_STATE_COLS 2

#define FUNCTION_DECL_STATE(m,x) (check_state ((x), (m), \
               func_declaration_states, FUNCTION_DECL_STATE_COLS))

int is_c_fn_declaration_msg (MESSAGE_STACK messages, int msg_ptr) {

  int i,
    state,
    last_state,
    return_val,
    stack_end,
    param_start_ptr,
    param_end_ptr,
    n_parens;
  Boolean param_start,   /* Make sure we have at least seen the start */
    param_end,           /* and end of the param list, so a calling   */
                         /* function can still iterate through        */
                         /* without worrying about a successful       */
                         /* spurious match in the middle of the       */
                         /* declaration.  param_end also implies that */
                         /* the token following is the opening brace  */
                         /* of the function body. If it isn't, and    */
                         /* the scope is global, then the function    */
                         /* changes the scope to PROTOTYPE_VAR, and   */
                         /* a calling function can treat the          */
                         /* declaration as a function prototype.      */
    tag_is_unique;       /* The label preceding the param list is     */
                         /* unique.                                   */
  MESSAGE *m;
#ifdef __GNUC__
   Boolean have_trailing_attribute;
#endif

  /*
   *  Make sure the message isn't a control structure keyword, 
   *  which are handled in control.c.
   */
  if ((!strcmp (messages[msg_ptr] -> name, "case")) ||
      (!strcmp (messages[msg_ptr] -> name, "do")) ||
      (!strcmp (messages[msg_ptr] -> name, "for")) ||
      (!strcmp (messages[msg_ptr] -> name, "if")) ||
      (!strcmp (messages[msg_ptr] -> name, "switch")) ||
      (!strcmp (messages[msg_ptr] -> name, "while")))
    return FALSE;

  stack_end = get_stack_top (messages);

  param_start = param_end = tag_is_unique = False;

  for (i = msg_ptr, n_declarators = 0, last_state = -1,
	 param_start_ptr = param_end_ptr = -1, n_parens = 0,
	 return_val = return_is_unsigned = return_is_ptr = 
	 return_is_ptr_ptr = FALSE; 
       i > stack_end;
       i--) {

    m = messages[i];

    if (M_ISSPACE (m)) continue;

    if (IS_PRIMITIVE_METHOD(m))
      return FALSE;

    if ((state = FUNCTION_DECL_STATE (messages, i)) == ERROR)
      return False;

    switch (m -> tokentype) 
      {
      case LABEL:
#ifdef __GNUC__
 	if (!strcmp (M_NAME(m), "__attribute__")) {
 	  int _attr_next_idx;
 	  for (_attr_next_idx = i - 1; _attr_next_idx > stack_end;
 	       _attr_next_idx--)
 	    if (!M_ISSPACE(messages[_attr_next_idx]))
 	      break;
 	  if ((i = match_paren (messages, _attr_next_idx, stack_end))
 	      == ERROR)
 	    _error ("Parser error.\n");
 	  continue;
 	}
#endif
	if (!param_start) {
	  switch (state)
	    {
	    case 0:
	    case 1:
	    case 2:
	      if (add_fn_declarator (m -> name) == ERROR) return FALSE;
	      break;
	    case 5:
	      if ((last_state == 3) || (last_state == 4)) {
		decl_is_ptr = TRUE;
		if (add_fn_declarator (m -> name) == ERROR) return FALSE;
	      }
	      if ((last_state == 9) || (last_state == -1)) {  
		if (!param_start) {  /* Msg_ptr starts in the middle */
		  return FALSE;      /* of the declaration.          */
		} else {
		  decl_is_ptr = TRUE;
		  if (add_fn_declarator (m -> name) == ERROR) return FALSE;
		}
	      }
	      break;
	    }
	}
	break;
      case OPENPAREN:
	if (!n_parens) {
	  if (param_start == False)
	    param_start_ptr = i;
	  param_start = True;
	  switch (state)
	    {
	    case 3:  /* First arg starts with a pointer. */
	    case 4:  /* First arg is a label.            */
	    case 10: /* No args.                         */
	      if (n_declarators == 0)  /* Opening paren of PFI. */
		return FALSE;
	      if (!is_c_data_type (declarators[n_declarators-1].name) ||
		  !get_typedef (declarators[n_declarators-1].name)) {
		tag_is_unique = True;
	      }
	      break;
	    }
	}
	++n_parens;
	break;
      case ASTERISK:
	switch (last_state)
	  {
	  case 1:
	    if (!param_start) {
	      if (state == 15) {
		return_is_ptr_ptr = TRUE;
	      } else {
		return_is_ptr = TRUE;
	      }
	    }
	    break;
	  case 3:
	    if (!param_start) {
	      if (state == 15) {
		decl_is_ptr_ptr = TRUE;
	      } else {
		decl_is_ptr = TRUE;
	      }
	    }
	    break;
	  case 15:
	    break;
	  }
	break;
      case CLOSEPAREN:
	if (state == 8) {    /* ... ) { ... */
	  param_end = True;
	  param_end_ptr = i;
	  if (--n_parens == 0) {
	    if (param_start && param_end && tag_is_unique) {
	      fn_params (messages, param_start_ptr, param_end_ptr);
	      if (preamble) preamble = FALSE;
	      return TRUE;
	    }
	  }
	}
	if (state == 7) {    /* ... ) ; ... */
	  param_end = True;
	  param_end_ptr = i;
	  switch (interpreter_pass)
	    {
	      /*
	       *  TO DO - Check for function redefinitions.
	       */
	    case var_pass:
	      if (--n_parens == 0) {
		if (param_start && param_end && tag_is_unique) {
		  fn_params (messages, param_start_ptr, param_end_ptr);
		  if (!get_function (declarators[n_declarators - 1].name))
		    return TRUE;
		  else
		    return FALSE;
		}
	      }
	      break;
	    case parsing_pass:
	    case method_pass:
	      if (FRAME_SCOPE == GLOBAL_VAR) {
		FRAME_SCOPE = PROTOTYPE_VAR;
	      }
	      if (--n_parens == 0) {
		if (param_start && param_end && tag_is_unique) {
		  fn_params (messages, param_start_ptr, param_end_ptr);
		  if (FRAME_SCOPE == PROTOTYPE_VAR)
		    return TRUE;
		  else
		    return FALSE;
		}
	      }
	      break;
	    default:
	      break;
	    }  /* switch (interpreter_pass) */
	} /* if (state == 7) { */    /* ... ) ; ... */
	if ((state == 6) ||  /* ... )( ... */
	    (state == 19) || /* ... ) , ... */
	    (state == 20)) { /* ... ) ) ... */
	  if (--n_parens == 0)
	    return FALSE;
	  else
	    goto fn_loop_done;  /* Don't decrement again at end of case. */
/* 	  if (--n_parens == 0) */
/* 	    return FALSE; */
	}
#ifdef __GNUC__
	/*
	 *  Trailing __attribute__.
	 */
 	if (state == 24) { 
 	  if ((--n_parens == 0) && param_start) {  /* ) __attribute__ ... */
	    int _next_tok_idx;
	    for (_next_tok_idx = i - 1; _next_tok_idx > stack_end; _next_tok_idx--)
	      if (!M_ISSPACE(messages[_next_tok_idx]))
		break;
	    if (!strcmp (M_NAME(messages[_next_tok_idx]), "__attribute__")) {
	      param_end = TRUE;
	      have_trailing_attribute = TRUE;
	      param_end_ptr = i;
	      if (param_start && param_end && tag_is_unique) {
		fn_params (messages, param_start_ptr, param_end_ptr);
		if (!get_function (declarators[n_declarators - 1].name))
		  return TRUE;
		else
		  return FALSE;
	      }
	    }
	  }
 	}
#endif
	if (--n_parens == 0)  /* Any other state. */
	  return FALSE;
	break;
      case ARGSEPARATOR:
	break;
      default:
	break;
      }

  fn_loop_done:
    last_state = state;
  }

  return return_val;

}

/*
 *  Should be called with param_start_ptr and param_end_ptr pointing
 *  to the opening and closing parentheses of the parameter list.
 */

static CVAR params[MAXARGS];
static int param_ptr;

/* These functions are called in cvars.c. */
int get_param_ptr (void) { return param_ptr; }
CVAR get_param_n (int n) { return params[n]; }

int fn_params (MESSAGE_STACK messages, int param_start_ptr, int param_end_ptr){

  int i,
    type_level;

  param_ptr = 0;
  memset (&params[param_ptr], 0, sizeof (CVAR));
  strcpy (params[param_ptr].sig, "CVAR");

  /*
   *  Default type.
   */
  strcpy (params[param_ptr].type, "int");
  params[param_ptr].scope = ARG_VAR;
  type_level = 0;

  for (i = param_start_ptr - 1; i > param_end_ptr; i--) {

    switch (messages[i] -> tokentype)
      {
      case LABEL:
	if (is_c_storage_class (messages[i] -> name)) {
	  strcpy (params[param_ptr].storage_class, 
		  messages[i] -> name);
	} else {
 	  if (is_c_data_type (messages[i] -> name)) {
	    switch (type_level)
	      {
	      case 0:
		strcpy (params[param_ptr].type, messages[i] -> name);
		break;
	      case 1:
		strcpy (params[param_ptr].qualifier, 
			params[param_ptr].type);
		strcpy (params[param_ptr].type, messages[i] -> name);
		break;
	      case 2:
		strcpy (params[param_ptr].qualifier2, 
			params[param_ptr].qualifier);
		strcpy (params[param_ptr].qualifier, 
			params[param_ptr].type);
		strcpy (params[param_ptr].type, messages[i] -> name);
		break;
	      }
	    ++type_level;
	  } else {
	    if (get_typedef (M_NAME(messages[i]))) {
	      switch (type_level)
		{
		case 0:
		  strcpy (params[param_ptr].type, messages[i] -> name);
		  break;
		case 1:
		  strcpy (params[param_ptr].qualifier, 
			  params[param_ptr].type);
		  strcpy (params[param_ptr].type, messages[i] -> name);
		  break;
		case 2:
		  strcpy (params[param_ptr].qualifier2, 
			  params[param_ptr].qualifier);
		  strcpy (params[param_ptr].qualifier, 
			  params[param_ptr].type);
		  strcpy (params[param_ptr].type, messages[i] -> name);
		  break;
		}
	      ++type_level;
	    } else {
	      strcpy (params[param_ptr].name, messages[i] -> name);
	    }
	  }
	}
	break;
      case ASTERISK:
	++params[param_ptr].n_derefs;
	break;
      case ARGSEPARATOR:
	param_ptr++;
	memset (&params[param_ptr], 0, sizeof (CVAR));
	strcpy (params[param_ptr].sig, "CVAR");
	strcpy (params[param_ptr].type, "int");
	params[param_ptr].scope = ARG_VAR;
	type_level = 0;
	break;
      }
    
  }

  return SUCCESS;
}

/* 
 *   Check for a C variable declaration.  Also distinguishes
 *   ctalk declarations.
 *   N.B. - The function returns True on the rightmost token
 *   that comprises a C declaration, so it will not catch 
 *   type qualifiers unless called with the leftmost statement
 *   token.
 *   TO DO - 
 *     1.  Recurse for struct and enum declarations.
 *     2.  Determine if a separate function is necessary for array 
 *         initializers.
 */

int var_declaration_states[] = {
  CTYPE,        LABEL,            /* 0. */
  CTYPE,        CTYPE,            /* 1. */
  CTYPE,        ASTERISK,         /* 2. */
  LABEL,        LABEL,            /* 3. */
  LABEL,        CHAR,             /* 4. */
  LABEL,        SEMICOLON,        /* 5. */
  ASTERISK,     LABEL,            /* 6. */
  EQ,           INTEGER,          /* 7.  E.g., int i = 1;        */
  EQ,           FLOAT,            /* 8.  E.g., double f = 1.0;   */
  EQ,           LITERAL,          /* 9.  E.g., char s[] = "a";   */
  EQ,           LITERAL_CHAR,     /* 10. E.g., char s='c';       */
  EQ,           OPENBLOCK,        /* 11. E.g. char s[] = {...}   */
  ARRAYOPEN,    ARRAYCLOSE,       /* 12. E.g., []                */
  ARRAYCLOSE,   EQ,               /* 13. E.g., [] =              */
  CTYPE,        OPENBLOCK,        /* 14. E.g., struct {...       */
  LABEL,        OPENBLOCK,        /* 15. E.g., struct <x> {...   */
  CLOSEBLOCK,   LABEL,            /* 16. E.g. } <struct_name>    */
  OPENBLOCK,    CTYPE,            /* 17. */
  INTEGER,      SEMICOLON,        /* 18. */
  FLOAT,        SEMICOLON,        /* 19. */
  LITERAL,      SEMICOLON,        /* 20. */
  LITERAL_CHAR, SEMICOLON,        /* 21. */
  OPENBLOCK,    LABEL,            /* 22.  E.g., { int, { enum_type... */
  SEMICOLON,    CLOSEBLOCK,       /* 23.  E.g., int i; } ...          */
  LABEL,        CLOSEBLOCK,       /* 24.  E.g. enum_type }            */
  CHAR,         SEMICOLON,        /* 25.  E.g., ];                    */
  OPENBLOCK,    CLOSEBLOCK,       /* 26.  E.g., struct s{};           */
  CLOSEBLOCK,   SEMICOLON,        /* 27. */
  CLOSEBLOCK,   LABEL,            /* 28. */
  LABEL,        EQ,               /* 29. Initializers. */
  EQ,           LABEL,            /* 30. */
  EQ,           LONG,             /* 31. */
  EQ,           LONGLONG,         /* 32. */
  ARRAYOPEN,    INTEGER,          /* 33. e.g., ary<[n>] */
  INTEGER,      ARRAYCLOSE,       /* 34. e.g., ary[<n]> */
  SEMICOLON,    LABEL,            /* 35. Necessary because stack frames    */
  SEMICOLON,    CTYPE,            /* 36. might not be present.             */
  LABEL,        ASTERISK,         /* 37. In case we missed a derived type. */
  CTYPE,        OPENPAREN,        /* 38. Function pointers -               */
  OPENPAREN,    ASTERISK,         /* 39. */
  LABEL,        CLOSEPAREN,       /* 40. */
  CLOSEPAREN,   OPENPAREN,        /* 41. */
  OPENPAREN,    CTYPE,            /* 42. If token is analyzed.          */
  OPENPAREN,    LABEL,            /* 43. As state 42, if not.           */
  LABEL,        ARGSEPARATOR,     /* 44. */
  ARRAYOPEN,    LABEL,            /* 45. E.g. [<label>]                 */
  CTYPE,        ARRAYCLOSE,       /* 46. E.g. [<ctype>]                 */
  ARGSEPARATOR, ASTERISK,         /* 47. Variable lists - *s1, *s2...   */
  ARGSEPARATOR, LABEL,            /* 48. c, d, e;                       */
  ARRAYCLOSE,   ARGSEPARATOR,     /* 49. c[], d[],...                   */
  ASTERISK,     ASTERISK,         /* 50. **  ........                   */
  OPENPAREN,    OPENPAREN,        /* 51. GNUC Attribute declarations.   */
  CLOSEPAREN,   CLOSEPAREN,       /* 52.  */
  LABEL,        ARRAYOPEN,        /* 53.  e.g., |arrayname[| ...        */
  ARRAYOPEN,    LABEL,            /* 54.  e.g.  arraname|[label|...     */
  LABEL,        COLON,            /* 55.  Bit fields...                 */
  COLON,        INTEGER,          /* 56. */
  INTEGER,      SEMICOLON,        /* 57. */
  CTYPE,        COLON,            /* 58. */
  CTYPE,        CLOSEPAREN,       /* 59. Complements 42, above.        */
  CLOSEPAREN,   SEMICOLON,        /* 60.  ...also used for func protos */
  INTEGER,      ARGSEPARATOR,     /* 61. For initialized enum members. */
  INTEGER,      CLOSEBLOCK,       /* 62. */
  CLOSEBLOCK,   ASTERISK,         /* 63.  ... <} *>... */
  ARRAYCLOSE,   SEMICOLON,        /* 64.  ... <] ;>... */
  SEMICOLON,    PREPROCESS,       /* 65. Break off*/
  ASTERISK,     OPENPAREN,        /* 66. e.g., OBJECT <*(> *cfunc)() */
  OPENPAREN,    CLOSEPAREN,       /* 67. e.g., OBJECT *(*cfunc)<()> */
  LABEL,        OPENPAREN,        /* 68. e.g., int <fn_name (>...) */
  CLOSEPAREN,   OPENBLOCK,        /* 69. (<args>) {<fn_body>...    */
  CLOSEPAREN,   ARGSEPARATOR,     /* 70. ) , ... */
  CTYPE,        ARGSEPARATOR,     /* 71. */
  ASTERISK,     CLOSEPAREN,       /* 72. */
  EQ,           OPENPAREN,        /* 73. E.g. char s = (...   */
  EQ,           EXCLAM,           /* 74. E.g., int i = !... */
  -1,   0
};

#define VAR_DECLARATION_STATE_COLS 2

#define VAR_DECLARATION_STATE(m,x) (check_state ((x), (m), \
              var_declaration_states, VAR_DECLARATION_STATE_COLS))

static char type_val[MAXLABEL];
static char qualifier_val[MAXLABEL];
static char qualifier_val2[MAXLABEL];
static VARNAME vars[MAXARGS];
static char storage_class[MAXLABEL];
static Boolean is_unsigned;
static Boolean is_struct_decl = False;
CVAR *inner_struct_decl;     /* If there's a tag after an inner */
                             /* struct or union, then we need to */
                             /* locate it to add the tag to the */
                             /* inner struct members.           */

/*
 *  Attributes and type attributes of the declaration.  See cvars.h. 
 */
int attrs;
int type_attrs;

/*
 *  Type declarations.  Storage class is stored in the
 *  storage_class[] string, and sign is stored in is_unsigned.
 *
 *  C99 says only that there should be 12 allowed for 
 *  incomplete types. Use MAX_DECLARATORS = 12 for all data 
 *  types anyway.
 */
char type_decls[MAX_DECLARATORS+1][MAXLABEL];
int type_decls_ptr;

/*
 *  The global struct_members and struct_members_ptr are only
 *  used when returning from parsing from a struct, until we
 *  call parser_to_cvars () in cvars.c.  Otherwise, the struct
 *  members are stored within a struct_decl[] entry.
 */
CVAR *struct_members,            /* Also change in cvars.c when */
  *struct_members_ptr;           /* changing these definitions. */

STRUCT_DECL struct_decl[MAXARGS];
int struct_decl_ptr = 0;

#define C_STRUCT (struct_decl[struct_decl_ptr - 1])

/*
 *  Saved function declarations when parsing parameter lists.
 */
FN_DECL fn_decl[MAXARGS];
int fn_decl_ptr = 0;

#define C_FN (fn_decl[fn_decl_ptr - 1])

CVAR *fn_param_decls,
  *fn_param_decls_ptr;

VARNAME *get_var (int n) { return &vars[n]; }
int n_vars;

int get_type_decls_ptr (void) { return type_decls_ptr; }
char *get_type_decl (int n) { return type_decls[n]; }

int get_cvar_attrs (void) { return attrs; }
int get_cvar_type_attrs (void) {return type_attrs; }

char *get_type_val (void) { return ((*type_val) ? type_val : NULL); }
char *get_qualifier_val (void) { return ((*qualifier_val) ? qualifier_val :
					 NULL); }
char *get_qualifier_val2 (void) { return ((*qualifier_val2) ? qualifier_val2 :
					 NULL); }
char *get_var_storage_class (void) { 
  return ((*storage_class) ? storage_class : NULL); }
Boolean get_unsigned_declaration_val (void) { return is_unsigned; }
Boolean get_struct_decl (void) {return is_struct_decl; }

int get_n_vars (void) { return n_vars; }
void set_n_vars (int n) { n_vars = n; }

static void clear_variable (void) {
  memset ((void *)type_decls, 0, (MAXLABEL * MAX_DECLARATORS));
  memset ((void *)type_val, 0, MAXLABEL);
  memset ((void *)qualifier_val, 0, MAXLABEL);
  memset ((void *)qualifier_val2, 0, MAXLABEL);
  memset ((void *)vars, 0, MAXARGS * sizeof (VARNAME));
  is_unsigned = False;

  if (struct_decl_ptr == 0)
    reset_struct_decl ();

  /*
   *  The current struct_members and end pointer get reset
   *  when they are saved before recursing into an inner 
   *  struct, and at the beginning of finding the
   *  members of an outer struct.
   */
  return_is_unsigned = return_is_ptr = return_is_ptr_ptr = 
    decl_is_ptr = decl_is_ptr_ptr = decl_is_prototype = attrs = 
    type_attrs = FALSE;
}

int have_declaration (char *s) {
  CVAR *c;
  OBJECT *o;

  if (interpreter_pass != var_pass) {
    if (is_method_name (s) || is_method_selector (s))
      return TRUE;
  }
  /*
   *  Note: This only covers instance and class variable messages 
   *  of "self".  At the moment, that is all it should need to
   *  cover, as self expressions are handled by rt_self_expr ().
   */
  if (interpreter_pass == method_pass) {
    if (rcvr_class_obj) {
      OBJECT *__v;
      for (__v = rcvr_class_obj -> instancevars; __v; __v = __v -> next) {
	if (!strcmp (__v -> __o_name, s))
	  return TRUE;
      }
      for (__v = rcvr_class_obj -> classvars; __v; __v = __v -> next) {
	if (!strcmp (__v -> __o_name, s))
	  return TRUE;
      }
    }
  }

  if (!strcmp (s, "self"))
    return TRUE;
  if (((c = get_instance_method_local_cvar (s)) != NULL) ||
      ((o = get_instance_method_local_object (s)) != NULL))
    return TRUE;

  if (((c = get_local_var (s)) != NULL) || global_var_is_declared (s)) {
  /*
   *  get_typedef insures that we don't run into a case where a 
   *  struct tag is the same as a typedef tag, in which case, 
   *  we don't have an already declared struct, just the typedef.
   */
    if (get_typedef (s)) {
      return FALSE;
    } else {
      return TRUE;
    } 
  }
  return FALSE;
}

/*
 *  If the label is not a type prefix, check back until
 *  we either find a data type label or an operator.
 */

int have_type_prefix (MESSAGE_STACK messages, int ptr) {

  int i,
    start_ptr;
  MESSAGE *m;

  if ((messages == message_stack ()) && HAVE_FRAMES) {
    start_ptr = THIS_FRAME -> message_frame_top;
  } else {
    start_ptr = stack_start (messages);
  }

  if (ptr == start_ptr) {
    m = messages[ptr];
    if ((m -> tokentype == SEMICOLON) ||
	(m -> tokentype == OPENPAREN) ||
	(m -> tokentype == ARRAYOPEN) ||
	(m -> tokentype == OPENBLOCK) ||
	(m -> tokentype == ARGSEPARATOR) ||
	(m -> tokentype == DEREF) ||
	(m -> tokentype == PERIOD))
      return FALSE;
    if (m -> tokentype == LABEL) {
/*       if (is_c_data_type (m -> name) || */
/* 	  get_typedef (m -> name)) */
      if (is_c_data_type (m -> name) ||
	  is_c_derived_type (m -> name))
	return TRUE;
    }
  }

  for (i = ptr + 1; i <= start_ptr; i++) {
    m = messages[i];
    if ((m -> tokentype == SEMICOLON) ||
	(m -> tokentype == OPENPAREN) ||
	(m -> tokentype == ARRAYOPEN) ||
	(m -> tokentype == OPENBLOCK) ||
	(m -> tokentype == ARGSEPARATOR) ||
	(m -> tokentype == DEREF) ||
	(m -> tokentype == PERIOD))
      return FALSE;
    if (m -> tokentype == LABEL) {
/*       if (is_c_data_type (m -> name) || */
/* 	  get_typedef (m -> name)) */
      if (is_c_data_type (m -> name) ||
	  is_c_derived_type (m -> name))
	return TRUE;
    }
  }

  return FALSE;
}

int have_ref_prefix (MESSAGE_STACK messages, int ptr) {
  int lookback;

  if (messages[ptr] -> tokentype != LABEL)
    return FALSE;

  /*
   *  Not having a previous token is not an 
   *  error when a token starts the frame.
   */
  if ((lookback = prevlangmsg (messages, ptr)) == ERROR)
    return FALSE;

  if (messages[lookback] -> tokentype == ASTERISK) {
    if ((lookback = prevlangmsg (messages, lookback)) == ERROR)
      error (messages[ptr], "Parser error.");
    if (messages[lookback] -> tokentype == LABEL) {
      if (!is_c_data_type (messages[lookback] -> name) &&
	  !get_typedef (messages[lookback] -> name)) {
	return TRUE;
      } else {
	return FALSE;
      }
    } else {
      return TRUE;
    }
  }

  /*
   *  An ampersand can also be an operator, but it is
   *  never used in variable declarations, so 
   *  the function does not need to check its
   *  context, and it can be treated simply
   *  as an address of operator here.
   */
  if ((messages[lookback] -> tokentype == AMPERSAND) ||
      (messages[lookback] -> tokentype == DEREF) ||
      (messages[lookback] -> tokentype == PERIOD))
    return TRUE;

  return FALSE;
}

/*
 * NOTE - The function allows for some laziness in data typing, so
 * that a derived type, if qualified, need not be defined - it is 
 * treated as a label and parsed by syntax.  This is so the interpreter 
 * can accomodate derived types that may not be defined on certain 
 * systems, for example:
 *
 *   typedef struct _pthread_descr_struct *_pthread_descr;
 * 
 * Where _pthread_descr_struct is not defined on some systems.
 */

Boolean is_c_var_declaration_msg (MESSAGE_STACK messages, int msg_ptr,
				  int ignore_oop_names) {
  int i, j;
  int end_ptr,
    stack_begin;
  int state,
    last_state,
    last_state2;
  int r;
  int const_expr_end;          /* Pointer for constant expr substitution. */
  int n_subscripts;
  /*@unused@*/
  Boolean return_val = True;
  Boolean aggregate_type,      /* True if aggregate type.                 */
    aggregate_decl;            /* True if aggregate initializers.         */
  Boolean func_ptr_decl,       /* True if decl is a function pointer.     */
    fn_arg_list,             /* True if within function decl arg list.  */
    is_fn_decl;
  Boolean comment = False;
  Boolean have_data_type;
  Boolean pointer;
  int block_level,
    n_parens;
  MESSAGE *m;
  OBJECT *tmpobj;
  VAL macro_val;

  /*
   *  Check whether a label has already raised an exception.
   */
  if (__ctalkTrapExceptionInternal (messages[msg_ptr]))
    return False;

  clear_variable ();

  /*
   *  If the variable is already declared, make sure that its
   *  use is not also taken as a declaration.  A redeclaration
   *  needs an explicit type preceding the variable tag. So
   *  if there is a declaration like,
   *  
   *    OBJECT *val_obj;
   *
   *  then a statment like
   *
   *    val_obj = ...
   *
   *  is not mistaken for a declaration of type, "int."
   *
   *  The function issues the redeclaration warning below.
   */
  if (have_declaration (messages[msg_ptr] -> name)) {
    if (!have_type_prefix (messages, msg_ptr)) {
      return False;
    }
  }

  /*
   * Also exclude cases where the label is 
   * preceded by a ref operator, except 
   * where an asterisk prefix is preceded by
   * a type label.
   */
  if (have_ref_prefix (messages, msg_ptr))
    return FALSE;

  if (is_instance_method_or_variable (messages, msg_ptr)) 
    return FALSE;

  /*
   *  Declarations also don't start with pointer or 
   *  address of operators, or aggregate operators,
   *  or immediately follow them.  So take care of those 
   *  cases here.
   */
  if ((messages[msg_ptr] -> tokentype == ASTERISK) ||
      (messages[msg_ptr] -> tokentype == AMPERSAND) ||
      (messages[msg_ptr] -> tokentype == DEREF) ||
      (messages[msg_ptr] -> tokentype == PERIOD))
    return FALSE;

  end_ptr = get_stack_top (messages);
  stack_begin = stack_start (messages);
  block_level = n_parens = 0;
  type_decls_ptr = n_vars = 0;
  aggregate_type = aggregate_decl = is_unsigned = pointer = 
    func_ptr_decl = is_fn_decl = fn_arg_list = have_data_type = False;
  *type_val = *qualifier_val = *qualifier_val2 = 
    *storage_class = '\0';

  state = last_state = last_state2 = ERROR;
  
  /* Initialize in case the macro expression doesn't set a value. */
  memset ((void *)&macro_val, 0, sizeof (VAL));
  macro_val.type = 1;

  for (i = msg_ptr; i >= end_ptr; i--) {

    m = messages[i];

    /* It should only be necessary to evaluate a variable
       declaration once. */

    if (!m || !IS_MESSAGE (m))
      break;

    if (M_ISSPACE (m)) continue;

    /* These are needed until the parser is able to remove 
       all comment tokens. */
    if (m -> tokentype == OPENCCOMMENT) {
      comment = True;
      continue;
    }
    if (comment && (m -> tokentype == CLOSECCOMMENT)) {
      comment = False;
      continue;
    }
    
    if (!ignore_oop_names) {
      if ((tmpobj = get_object (m -> name, NULL)) != NULL)
	return FALSE;

      if ((tmpobj = get_class_object (m -> name)) != NULL)
	return FALSE;

      if (is_pending_class (m -> name))
	return FALSE;

      if (interpreter_pass == method_pass) {
	METHOD *_n_method;
	int _n_th_param;
	_n_method = new_methods[new_method_ptr+1];
	for (_n_th_param = 0; _n_th_param < _n_method -> n_params; 
	     _n_th_param++) {
	  if (!strcmp (M_NAME(m), _n_method->params[_n_th_param]->name))
	    return FALSE;
	}
      }
    }
    if (!strcmp (m -> name, "returnObjectClass") ||
	!strcmp (m -> name, "noMethodInit"))
      return FALSE;

#ifdef __GNUC__
    if ((r = is_c_data_type_attr (m -> name)) != FALSE) {
      if ((r == CVAR_TYPE_LONG) && (type_attrs & CVAR_TYPE_LONG)) {
	type_attrs |= CVAR_TYPE_LONGLONG;
      } else {
	type_attrs |= r;
      }
      m -> tokentype = CTYPE;
    } else {
      if (((r = is_gnuc_builtin_type (m -> name)) == TRUE) ||
	  ((r = is_c_derived_type (m -> name)) == TRUE)) {
	m -> tokentype = CTYPE;
      } else {
	if ((r = is_gnu_extension_keyword (m -> name)) == TRUE) {
	  if (warn_extension_opt) {
	    warning (m, "Compiler extension %s.", m -> name);
	  }
	  continue;
	}
      }
    }
#else
    if ((r = is_c_data_type_attr (m -> name)) != FALSE) {
      if ((r == CVAR_TYPE_LONG) && (type_attrs & CVAR_TYPE_LONG)) {
	type_attrs |= CVAR_TYPE_LONGLONG;
      } else {
	type_attrs |= r;
      }
      m -> tokentype = CTYPE;
    } else {
      if ((r = is_c_derived_type (m -> name)) == TRUE) {
	m -> tokentype = CTYPE;
      }
    }
#endif

    /* TO DO - Determine if tracking through the branches
       here, even if they are not legal statements, 
       can be recovered for later. */
    if ((state = VAR_DECLARATION_STATE (messages, i)) == ERROR) {
      /*
       *  Return an error if not at the end of the stack; i.e.,
       *  when not parsing parameter declarations.
       */
      if (format_method_output)
	goto done;
      if (nextlangmsg (messages, i) != ERROR) 
	return False;
    }

    /*
     *  Skip initializers.
     */
    if ((state == 7 || state == 8 || state == 9 || 
	 state == 10 || state == 11 || /* last_state == 29 || */
	 state == 30 || state == 32 || state == 73 || state == 74) 
	&& (aggregate_decl == False)) {
      i = initializer_end (messages, i);
      continue;
    }

    /* Don't analyze keywords like, "return," etc. */
    if (is_c_keyword (m -> name) && ! is_c_data_type (m -> name))
      return FALSE;

    switch (m -> tokentype)
      {
      case CTYPE:
	switch (state)
	  {
	  case 0:  /* 
		    *  C types except for sign labels and storage
		    *  classes go in the type_decls[] array.
		    */
	    /* signed|unsigned */
	    if (IS_SIGN_LABEL (m -> name))
	      if (!strcmp (m -> name, "unsigned"))
		is_unsigned = True;

	    /* const|extern|inline|register|static|volatile */
	    if (is_c_storage_class (m -> name))
	      strcpy (storage_class, m -> name);

	    /* enum|struct|union */
	    if (IS_AGGREGATE_TYPE (m -> name)) {
	      aggregate_type = True;
	      /* Enums get handled separately, but check here 
	       * for a struct or union anyway. 
	       */
	      if (!strcmp (m -> name, "struct") ||
		  !strcmp (m -> name, "union"))
		attrs |= CVAR_ATTR_STRUCT;
	    }

	    strcpy (type_decls[type_decls_ptr++], m -> name);

	    break;
	  case 2:   /* <type> * */
	    strcpy (type_decls[type_decls_ptr++], m -> name);
	    break;
	  case 14:      /* <struct|enum|union {> */
	    strcpy (type_decls[type_decls_ptr++], m -> name);
	    aggregate_type = aggregate_decl = True;
	    break;
	  case 38:      /* <type> (... */
	    strcpy (type_decls[type_decls_ptr++], m -> name);
	    break;
	  default:
	    strcpy (type_decls[type_decls_ptr++], m -> name);
	    break;
	  } /* switch (state) */
	break;
      case OPENBLOCK:
	++block_level;
	if (aggregate_type && block_level == 1) {
	  int t_block_level;
	  aggregate_decl = True;
	  attrs |= CVAR_ATTR_STRUCT_DECL;
	  save_struct_declaration (messages, i);
	  find_struct_members (messages, i);
	  for (j = i-1, t_block_level = block_level; 
	       (j >= end_ptr) && t_block_level; j--) {
	    switch (messages[j] -> tokentype)
	      {
	      case OPENBLOCK:
		++t_block_level;
		break;
	      case CLOSEBLOCK:
		--t_block_level;
		break;
	      }
	  }
	  /*
	   *  Back up to the token before the closing block.
	   *  On the following iterations, the state changes
	   *  from the closing block forward should be sufficient
	   *  to determine the context of a token, so we
	   *  don't need to worry about last_state* for 
	   *  the closing block.
	   */
	  i = j + 2;
	}
	break;
      case CLOSEBLOCK:
	--block_level;
	if (aggregate_decl && (block_level == 0)) {
	  aggregate_decl = False;
	  restore_struct_declaration ();  /* Fall through. */
	}
	if (state == 27) {  /* } ; */
	  /*
	   *  Copy the last type specifier to the variable tag.
	   */
	  if (type_decls_ptr > 1) {
	    if (strcmp (type_decls[type_decls_ptr - 1], "struct") &&
		strcmp (type_decls[type_decls_ptr - 1], "union") &&
		strcmp (type_decls[type_decls_ptr - 1], "enum"))
	      strcpy (vars[n_vars++].name, type_decls[--type_decls_ptr]);
	  }
	}
	break;
      case CLOSEPAREN:
	if ((state == 41) && pointer)
	  func_ptr_decl = True;
	--n_parens;
	break;
      case OPENPAREN:
	++n_parens;
	switch (state)
	  {
	  case 42:
	    switch (last_state)
	      {
	      case 41:
		fn_arg_list = True;
		break;
	      }
	    break;
	  case 43:  /* ( label */
	    switch (last_state)
	      {
		/*
		 *  Function param list.
		 */
	      case 41:  /* ) (  (fn_ptr_decl) */
	      case 68:  /* label ( */
		if (fn_is_declaration (messages, i)) {
		  fn_param_cvars (messages, i);
		  i = match_paren (messages, i, end_ptr);
		  if ((state = VAR_DECLARATION_STATE (messages, i)) == ERROR) {
#ifdef __GNUC__
		    if (fn_has_gnu_attribute (messages, i)) {
		      return TRUE;
		    } else {
		      _warning ("Parse error.\n");
		      return False;
		    }
#else
		    _warning ("Parse error.\n");
		    return False;
#endif
		  }
		  if (state == 60) {  /* <)> ; */
		    attrs |= CVAR_ATTR_FN_PROTOTYPE;
		    break;
		  }
		  if (state == 70)  /* <)> , */
		    break;
		} /* if (fn_is_declaration (messages, i)) */
	      }
	    break;
#ifdef __GNUC__
 	  case 51:  /* The start of a GNUC attribute: <(>( */
                    /* The state of the previous label before */
                    /* __attribute__ may have cause it to be */
                    /* interpreted as a type instead of a tag, */
                    /* so remove it from the types, and add to */
                    /* the var name. */
	    if ((i = match_paren (messages, i, end_ptr)) != ERROR) {
	      if (!is_c_data_type (type_decls[type_decls_ptr -1]) &&
		  !typedef_is_declared (type_decls[type_decls_ptr - 1])) {
		/*
		 *  We've reached the typedef tag.
		 */
		strcpy (vars[n_vars++].name, type_decls[--type_decls_ptr]);
		*type_decls[type_decls_ptr + 1] = 0;
		goto done;
		break;
	      }
	    }
	    break;
#endif
	  case 67:  /* <(> )  empty parameter list */
	    /*   fn_param_decls = fn_param_decls_ptr = NULL; */
	    attrs |= CVAR_ATTR_FN_NO_PARAMS;
 	    break;
	  }  /* switch (state) ... */
	break;
      case LABEL:
	switch (state)
	  {
	  case 3:   /* <this_label> <label> */
	    switch (last_state)
	      {
	      case 0:  /* <ctype> <this_label> */
		if (aggregate_type && typedef_check && !is_struct_decl) {
		                   /* Incomplete struct/enum typedef: e.g., */
		  return FALSE;    /* typedef struct <struct_tag> <type_tag>;*/
		} else {
		  strcpy (type_decls[type_decls_ptr++], m -> name);
		}
		break;
	      case 3:  /* <label> <this_label> */
		strcpy (type_decls[type_decls_ptr++], m -> name);
		break;
	      } /* switch (last_state) */
	    break;

	    /*
	     *  This should be sufficient for the case of
	     *  a label occurring on its own, because the
	     *  function checks for a default type below.
	     */
	  case 5: /* <this_label> ; */
	    switch (last_state) 
	      {
	      case 16:
		strcpy (vars[n_vars++].name, m -> name);
		break;
	      case 6:   /* * <this_label> ; */
		switch (last_state2)
		  {
		  case 63:  /* } * <this_label> ; */
		    strcpy (vars[n_vars++].name, m -> name);
		    break;
		  default:
		    strcpy (vars[n_vars++].name, m -> name);
		    if ((attrs == CVAR_ATTR_STRUCT) ||
		(attrs == (CVAR_ATTR_STRUCT | CVAR_ATTR_STRUCT_INCOMPLETE)))
		    {
 		      if (have_struct (type_decls[type_decls_ptr-1])) {
 			if (vars[n_vars-1].n_derefs) {
 			  attrs = CVAR_ATTR_STRUCT_PTR;
 			} else {
 			  attrs = CVAR_ATTR_STRUCT;
 			}
 		      } else {
		        attrs |= CVAR_ATTR_STRUCT_INCOMPLETE;
 		      }
		    }
		    break;
		  }
		break;
	      default:
		strcpy (vars[n_vars++].name, m -> name);
		    if ((attrs == CVAR_ATTR_STRUCT) ||
		(attrs == (CVAR_ATTR_STRUCT | CVAR_ATTR_STRUCT_INCOMPLETE)))
		    {
 		      if (have_struct (type_decls[type_decls_ptr-1])) {
 			if (vars[n_vars-1].n_derefs) {
 			  attrs = CVAR_ATTR_STRUCT_PTR;
 			} else {
 			  attrs = CVAR_ATTR_STRUCT;
 			}
 		      } else {
		        attrs |= CVAR_ATTR_STRUCT_INCOMPLETE;
 		      }
		    }
		    break;
	      }
	    break;
	  case 15:  /* <this_label> { */
	  case 37:  /* <this_label> * */
	    strcpy (type_decls[type_decls_ptr++], m -> name);
	    /*
	     *  Increment n_derefs only on an asterisk itself.
	     */
	    break;
	  case 40:  /* <this_label> ) */
	    if (!n_parens)
	      return FALSE;
	    switch (last_state)
	      {
	      case 6:  /* * <this_label> ) */
		strcpy (vars[n_vars++].name, m -> name);
		attrs |= CVAR_ATTR_FN_PTR_DECL;
		break;
	      default:
		strcpy (vars[n_vars++].name, m -> name);
		break;
	      }
	    break;
	  case 44:  /* <this_label> , */
	    strcpy (vars[n_vars++].name, m -> name);
	    break;
	  case 53:  /* <this_label> [ */
	    switch (last_state)
	      {
	      case 16:  /* } <this_label> [ */
	      default:  /* <this_label> [ */
		aggregate_decl = True;
		strcpy (vars[n_vars].name, m -> name);
		find_n_subscripts (messages, i, &n_subscripts);
		vars[n_vars].n_derefs += n_subscripts;
		++n_vars;
		switch (declaration_context)
		  {
		  case decl_param_list_context:
		    /*
		     *  ERROR here could mean we're at the end of the stack.
		     */
		    if ((i = find_arg_end (messages, i)) != ERROR) {
		      ++i;
		      if ((state = VAR_DECLARATION_STATE (messages, i)) 
			  == ERROR)
			return False;
		    } else {
		      i = end_ptr;
		      ++n_vars;
		    }
		    break;
 		  case decl_null_context:
 		  case decl_var_context:
		  default:
		    i = find_subscript_declaration_end (messages, i);
		    /*
		     *  Back up the stack pointer so it points to 
		     *  the expression separator or terminator on the
		     *  next iteration, and set the state for the 
		     *  closing bracket.
		     */
		    if ((i = prevlangmsg (messages, i)) != ERROR) {
		      if ((state = VAR_DECLARATION_STATE (messages, i)) 
			  == ERROR)
			return False;
		    }
		    break;
		  } /* switch (declaration_context) */
		break;
	      }  /* switch (last_state) */
	    break;
	  case 68:   /* <this_label> ( */
	    switch (last_state)
	      {
	      case 0:  /* ctype <this_label> */
		strcpy (vars[n_vars++].name, m -> name);
		if (!n_parens) {
		  is_fn_decl = True;
		  attrs |= CVAR_ATTR_FN_DECL;
		}
		break;
	      case 6:  /* ctype * <this_label> */
		strcpy (vars[n_vars++].name, m -> name);
		if (!n_parens) {
		  is_fn_decl = True;
		  attrs |= CVAR_ATTR_FN_PTR_DECL;
		}
		break;
	      }
	    break;
	  default:
	    /*
	     *  For cases like in a sub parser and at the end of the stack,
	     *  when the state is -1.
	     */
	    strcpy (vars[n_vars++].name, m -> name);
	    break;
	  } /* switch (state) */
	break;  /* case LABEL: */
      case ASTERISK:
	switch (state)
	  {
	  case 6:   /* <*> label */
	    switch (last_state)
	      {
	      case 2:   /* ctype <*> label */
 	      case 63:  /* End of struct: } <*> label */
	      case 37:   /* label <*> label */
	      case 39:   /* ( <*> label (fn ptr) */
	      case 47:  /* , *<label> */
	      case 50:  /* * <*> label */
		++vars[n_vars].n_derefs;
		break;
	      case 66:   /* RETURN_TYPE <*(> *pfi)() */
		break;
	      }
	    break;
	  case 50:  /* <*>* */
	    ++vars[n_vars].n_derefs;
	    break;
	  }
	break;
      case CHAR:
	break;
      case ARRAYOPEN:
	/* 
	 * Evaluating the constant expression makes the state
	 * transitions here much simpler.  We should not need
	 * to provide states for all the constant expression
	 * operators, just evaluate the expression and then 
	 * skip past it.
	 */
	if (!aggregate_decl)
	  pointer = True;

	for (const_expr_end = i; ; const_expr_end--) {
	  if (messages[const_expr_end] -> tokentype == ARRAYCLOSE)
	    break;
	}
	++const_expr_end;
	eval_constant_expr (messages, i - 1, &const_expr_end, 
			    &macro_val);
	i = const_expr_end - 1;
	break;
      case ARGSEPARATOR:
	pointer = False;
	break;
      case SEMICOLON:
	goto done;
      }
    last_state2 = last_state;
    last_state = state;

  }

 done:

/*
 *  Workaround for Solaris 2.10 /usr/include/sys/siginfo.h.
 *  Need to check if this is the definition for an incomplete
 *  type.
 */
#if defined (__sun__) && defined (__svr4__)
if (!strcmp (type_decls[0], "struct") &&
    !strcmp (type_decls[1], "siginfo") &&
    !strcmp (type_decls[2], "siginfo_t")) {
  strcpy (vars[0].name, type_decls[2]);
  n_vars = 1;
  type_decls_ptr--;
}
#endif

  /*
   *  Make sure there is at least one basic C type or 
   *  typedef in the declaration.
   */
  for (j = 0, have_data_type = False; 
       (j < type_decls_ptr) && !have_data_type; j++) {
    if ((is_c_data_type (type_decls[j]) &&
	 (!is_c_storage_class (type_decls[j]) &&
	  !IS_SIGN_LABEL (type_decls[j]) &&
	  !IS_TYPE_LENGTH (type_decls[j]))) ||
#ifdef __GNUC__
	is_gnuc_builtin_type (type_decls[j]) ||
#endif
	get_typedef (type_decls[j]) ||
	/* Include stuff like FILE, __FILE, pthread... ... */
	is_incomplete_type (type_decls[j]))
      have_data_type = True;
  }
  if (!have_data_type) {
    for (j = 0; j < type_decls_ptr; j++) {
      if (is_ctalk_keyword(type_decls[j]) ||
	  IS_CONSTRUCTOR_LABEL(type_decls[j]))
	return False;
    }
  }

  if (!have_data_type)
    strcpy (type_decls[type_decls_ptr++], "int");

  if (*vars[0].name) {
    return_val = True;
  } else {
    return_val = False;
  }

  return return_val;
}

int initializer_end (MESSAGE_STACK messages, int idx) {

  int i,
    n_parens,
    n_blocks,
    n_braces,
    prev_token = -1;

  n_parens = n_blocks = n_braces = FALSE;
  for (i = idx - 1; messages[i]; i--) {
    switch (M_TOK(messages[i]))
      {
      case OPENPAREN:
	++n_parens;
	break;
      case CLOSEPAREN:
	--n_parens;
	break;
      case ARRAYOPEN:
	++n_blocks;
	break;
      case ARRAYCLOSE:
	--n_blocks;
	break;
      case OPENBLOCK:
	++n_braces;
	break;
      case CLOSEBLOCK:
	--n_braces;
	break;
      case ARGSEPARATOR:
      case SEMICOLON:
	if (!n_parens || !n_blocks || !n_braces) {
	  if (prev_token == -1) {
	    return i;
	  } else {
	    return prev_token;
	  }
	}
	break;
      }
    prev_token = i;
  }

  return ERROR;
}

void save_fn_declaration (MESSAGE_STACK messages, int ptr) {

  memset ((void *)&fn_decl[fn_decl_ptr], 0, sizeof (struct _fn_decl));

  if (*type_val) { 
    strcpy (fn_decl[fn_decl_ptr].type_val, type_val); 
  }
  if (*qualifier_val) { 
    strcpy (fn_decl[fn_decl_ptr].qualifier_val, qualifier_val); 
  }
  if (*qualifier_val2) { 
    strcpy (fn_decl[fn_decl_ptr].qualifier_val2, qualifier_val2); 
  }
  if (*storage_class) { 
    strcpy (fn_decl[fn_decl_ptr].storage_class, storage_class); 
  }

  memcpy ((void *)&(fn_decl[fn_decl_ptr].type_decls),
		    (void *)&type_decls, (MAX_DECLARATORS * MAXLABEL));
  fn_decl[fn_decl_ptr].type_decls_ptr = get_type_decls_ptr ();

  memcpy ((void *)&(fn_decl[fn_decl_ptr].vars), (void *)&(vars),
	  (sizeof (VARNAME) * MAXARGS));
  fn_decl[fn_decl_ptr].n_vars = n_vars;

  fn_decl[fn_decl_ptr].attrs = attrs;

  fn_decl[fn_decl_ptr].params = fn_param_decls;

  if (n_vars > -1) {
    strcpy (fn_decl[fn_decl_ptr].name, vars[n_vars].name); 
  }

  ++fn_decl_ptr;
}

/*
 *   This should be called from find_struct_members () with 
 *   ptr pointing to the opening block of the struct.
 */
void save_struct_declaration (MESSAGE_STACK messages, int ptr) {

  int i, 
    n_braces,
    stack_begin,
    stack_end;

  stack_begin = stack_start (messages);
  stack_end = get_stack_top (messages);

  memset ((void *)&struct_decl[struct_decl_ptr], 
	  0, sizeof (struct _struct_decl));

  if (messages[ptr] -> tokentype != OPENBLOCK) {
    if ((struct_decl[struct_decl_ptr].block_start = 
	 scanback (messages, ptr, stack_begin, OPENBLOCK)) == ERROR)
      _error ("Parse error.\n");
  } else {
    struct_decl[struct_decl_ptr].block_start = ptr;
  }

  for (i = struct_decl[struct_decl_ptr].block_start, n_braces = 0; 
       i > stack_end; i--) {
    if (messages[i] -> tokentype == OPENBLOCK)
      ++n_braces;
    if (messages[i] -> tokentype == CLOSEBLOCK)
      --n_braces;
    if (!n_braces)
      break;
  }
  struct_decl[struct_decl_ptr].block_end = i;
  
  if (*type_val) { 
    strcpy (struct_decl[struct_decl_ptr].type_val, type_val); 
  }
  if (*qualifier_val) { 
    strcpy (struct_decl[struct_decl_ptr].qualifier_val, qualifier_val); 
  }
  if (*qualifier_val2) { 
    strcpy (struct_decl[struct_decl_ptr].qualifier_val2, qualifier_val2); 
  }
  if (*storage_class) { 
    strcpy (struct_decl[struct_decl_ptr].storage_class, storage_class); 
  }

  memcpy ((void *)&(struct_decl[struct_decl_ptr].type_decls),
		    (void *)&type_decls, (MAX_DECLARATORS * MAXLABEL));
  struct_decl[struct_decl_ptr].type_decls_ptr = get_type_decls_ptr ();

  memcpy ((void *)&(struct_decl[struct_decl_ptr].vars),
	  (void *)&(vars),
	  (sizeof (VARNAME) * MAXARGS));
  struct_decl[struct_decl_ptr].n_vars = n_vars;

  struct_decl[struct_decl_ptr].attrs = attrs;

  if (n_vars > -1) {
    strcpy (struct_decl[struct_decl_ptr].name, vars[n_vars].name); 
  }

  /*
   *  If struct_members and struct_decl_ptr == 0, then we're starting 
   *  to find the members of the outermost struct, and struct members
   *  should be NULL.  On inner structs, save the members
   *  we've already parsed in the outer struct.
   */
  if (struct_members) {
    struct_decl[struct_decl_ptr-1].members = struct_members;
    struct_decl[struct_decl_ptr-1].members_ptr = struct_members_ptr;
    struct_members = struct_members_ptr = NULL;
  }

  /*
   *  Struct_members should be NULL on entering the outermost 
   *  struct.
   */

  ++struct_decl_ptr;

}

void reset_struct_decl (void) {
  is_struct_decl = False; 
  struct_decl_ptr = 0;
  struct_members = struct_members_ptr = NULL;
}

void restore_fn_declaration (void) {

  int c_fn_ptr;

  c_fn_ptr = fn_decl_ptr - 1;

  clear_variable ();

  if (*fn_decl[c_fn_ptr].type_val)
    strcpy (type_val, fn_decl[c_fn_ptr].type_val); 

  if (*fn_decl[c_fn_ptr].qualifier_val)
    strcpy (qualifier_val, fn_decl[c_fn_ptr].qualifier_val); 

  if (*fn_decl[c_fn_ptr].qualifier_val2)
    strcpy (qualifier_val2, fn_decl[c_fn_ptr].qualifier_val2); 

  if (*fn_decl[c_fn_ptr - 1].storage_class)
    strcpy (storage_class, fn_decl[c_fn_ptr].storage_class); 

  strcpy (vars[0].name, fn_decl[c_fn_ptr].name);

  memcpy ((void *)&type_decls, 
	  (void *)&(fn_decl[c_fn_ptr].type_decls),
	  (MAX_DECLARATORS * MAXLABEL));

  type_decls_ptr = fn_decl[c_fn_ptr].type_decls_ptr;
  
  memcpy ((void *)&vars, (void *)&(fn_decl[c_fn_ptr].vars),
	  (MAX_DECLARATORS * MAXLABEL));

  n_vars = fn_decl[c_fn_ptr].n_vars;
  attrs = fn_decl[c_fn_ptr].attrs;

  memset ((void *)&fn_decl[c_fn_ptr], 0, sizeof (STRUCT_DECL));

  --fn_decl_ptr;

}

void restore_struct_declaration (void) {

  int c_struct_ptr;

  c_struct_ptr = struct_decl_ptr - 1;

  /*
   *  All member variables should be in the struct_members
   *  list from find_struct_members ().
   */
  clear_variable ();

  if (*struct_decl[c_struct_ptr].type_val)
    strcpy (type_val, struct_decl[c_struct_ptr].type_val); 

  if (*struct_decl[c_struct_ptr].qualifier_val)
    strcpy (qualifier_val, struct_decl[c_struct_ptr].qualifier_val); 

  if (*struct_decl[c_struct_ptr].qualifier_val2)
    strcpy (qualifier_val2, struct_decl[c_struct_ptr].qualifier_val2); 

  if (*struct_decl[c_struct_ptr - 1].storage_class)
    strcpy (storage_class, struct_decl[c_struct_ptr].storage_class); 

  strcpy (vars[0].name, struct_decl[c_struct_ptr].name);

  memcpy ((void *)&type_decls, 
	  (void *)&(struct_decl[c_struct_ptr].type_decls),
	  (MAX_DECLARATORS * MAXLABEL));

  type_decls_ptr = struct_decl[c_struct_ptr].type_decls_ptr;
  
  memcpy ((void *)&vars, 
	  (void *)&(struct_decl[c_struct_ptr].vars),
	  (MAX_DECLARATORS * MAXLABEL));

  n_vars = struct_decl[c_struct_ptr].n_vars;
  attrs = struct_decl[c_struct_ptr].attrs;

  struct_members = struct_decl[c_struct_ptr].members;
  struct_members_ptr = struct_decl[c_struct_ptr].members_ptr;

  memset ((void *)&struct_decl[c_struct_ptr], 0, sizeof (STRUCT_DECL));

  --struct_decl_ptr;

}

/*
 *  Called with the stack index at the opening brace of a 
 *  struct definition.
 *
 *  When finished, restores the struct declaration to the variable
 *  qualifiers, at the outermost level of a struct.
 *
 *  The inner structs get added as members to the next outer 
 *  struct declaration.
 */

int find_struct_members (MESSAGE_STACK messages, int ptr) {

  int i, j, end_ptr, n_blocks;
  MESSAGE *m;
  CVAR *mbr = NULL;  /* Avoid a warning. */
  Boolean in_inner_struct;

  /*
   *  Enum_decl () handles enums.
   */
  for (i = 0; i < get_type_decls_ptr (); i++) {
    if (!strcmp (get_type_decl (i), "enum"))
      return ERROR;
  }

  is_struct_decl = True;
  clear_variable ();

  end_ptr = get_stack_top (messages);

  for (i = ptr, n_blocks = 0, struct_members = struct_members_ptr = NULL; 
       i >= end_ptr; 
       i--) {

    m = messages[i];

    switch (m -> tokentype)
      {
      case OPENBLOCK:
	++n_blocks;
	break;
      case CLOSEBLOCK:
	--n_blocks;
	if (!n_blocks)
	  goto done;
	break;
      case LABEL:
	mbr = NULL;
 	if (is_c_var_declaration_msg (messages, i, FALSE)) {
	  attrs |= CVAR_ATTR_STRUCT_MBR;
	  if ((mbr = parser_to_cvars ()) != NULL) {
	    /*
	     *  If the result of a variable parse is a 
	     *  struct or a union, it is a nested struct
	     *  or union.  Add its struct members
	     *  to the CVAR, and add the CVAR to the list of
	     *  members of the next outer struct, and then
	     *  put back the outermost struct member list.
	     */
	    for (j = struct_decl_ptr - 1, in_inner_struct = False; 
		 (j >= 0) && !in_inner_struct; j--) {
	      if ((i <= struct_decl[j].block_start) &&
		  (i >= struct_decl[j].block_end)) {
		in_inner_struct = True;
		if (!struct_decl[j].members) {
		  if (mbr) {
		    struct_decl[j].members = mbr;
		    for (struct_decl[j].members_ptr = mbr; 
			 struct_decl[j].members_ptr -> next;
			 struct_decl[j].members_ptr = 
			   struct_decl[j].members_ptr -> next)
		      ;
		  }
		} else {
		  if (mbr) {
		    struct_decl[j].members_ptr -> next = mbr;
		    mbr -> prev = struct_decl[j].members_ptr;
		    for (struct_decl[j].members_ptr = mbr; 
			 struct_decl[j].members_ptr -> next; 
			 struct_decl[j].members_ptr = 
			   struct_decl[j].members_ptr -> next)
		      ;
		  }
		}
	      }
	    }
	  }
        } else {
          /* Handle a member that is an anonymous struct. */
          if (attrs & CVAR_ATTR_STRUCT_DECL) {
            int _n_type_decls;
            _new_cvar (CVARREF(mbr));
            mbr -> attrs = attrs;
            mbr -> members = struct_members;
            struct_members = NULL;
            for (_n_type_decls = type_decls_ptr - 1; _n_type_decls >= 0; 
                 _n_type_decls--) {
                 switch (_n_type_decls) 
                   {
                  case 4:
  		    strcpy (mbr -> qualifier4, type_decls[_n_type_decls]);
                    break;
                  case 3:
  		    strcpy (mbr -> qualifier3, type_decls[_n_type_decls]);
                    break;
                  case 2:
  		    strcpy (mbr -> qualifier2, type_decls[_n_type_decls]);
                    break;
                  case 1:
  		    strcpy (mbr -> qualifier, type_decls[_n_type_decls]);
                    break;
                  case 0:
  		    strcpy (mbr -> type, type_decls[_n_type_decls]);
                    break;
                   }

              }
           }
	    for (j = struct_decl_ptr - 1, in_inner_struct = False; 
		 (j >= 0) && !in_inner_struct; j--) {
	      if ((i <= struct_decl[j].block_start) &&
		  (i >= struct_decl[j].block_end)) {
		in_inner_struct = True;
		if (!struct_decl[j].members) {
		  if (mbr) {
		    struct_decl[j].members = mbr;
		    for (struct_decl[j].members_ptr = mbr; 
			 struct_decl[j].members_ptr -> next;
			 struct_decl[j].members_ptr = 
			   struct_decl[j].members_ptr -> next)
		      ;
		  }
		} else {
		  if (mbr) {
		    struct_decl[j].members_ptr -> next = mbr;
		    mbr -> prev = struct_decl[j].members_ptr;
		    for (struct_decl[j].members_ptr = mbr; 
			 struct_decl[j].members_ptr -> next; 
			 struct_decl[j].members_ptr = 
			   struct_decl[j].members_ptr -> next)
		      ;
		  }
		}
             }
           }
        }
	i = find_declaration_end (messages, i);
	break;
      default:
	break;
      }
  }

 done:

  return SUCCESS;
}

#define PARAM_IS_C_STORAGE_CLASS(__s) (*(__s) && is_c_storage_class(__s))

/*
 *  Idx should point to the opening parenthesis of the parameter list.
 */
int fn_param_cvars (MESSAGE_STACK messages, int idx) {

  int i, j, k,
    param_start,
    param_end, 
    n_params,
    end_ptr,
    n_tags,
    have_name_tag,
    n_derefs,
    n_parens,
    is_fn_ptr,
    prev_tok;
  char *param_ptrs[MAXARGS];
  char param_tags[MAXARGS][MAXLABEL];
  CVAR *param;
  DECLARATION_CONTEXT old_context;

  end_ptr = get_stack_top (messages);

  param_start = idx;

  if ((param_end = match_paren (messages, param_start, end_ptr)) == ERROR)
    _error ("parse_error.\n");

  if (fn_param_declarations (messages, param_start, param_end,
			     param_ptrs, &n_params) == ERROR) {
    check_constant_expr (messages, param_start, param_end);
    return ERROR;
  }
  save_fn_declaration (messages, idx);

  for (i = 0; i < n_params; i++) {

    old_context = declaration_context;
    declaration_context = decl_param_list_context;
    tokenize (c_message_push, param_ptrs[i]);
    memset (param_tags, 0, MAXARGS * MAXLABEL);
    for (j = N_MESSAGES, n_tags = 0, n_derefs = 0, n_parens = 0, 
	   have_name_tag = FALSE, is_fn_ptr = FALSE, prev_tok = -1; 
	 j > c_message_ptr; j--) {
      switch (M_TOK(c_messages[j]))
 	{
 	case LABEL:
	  if (!n_parens) {
	    strcpy (param_tags[n_tags++], M_NAME(c_messages[j]));
	    if (!is_c_data_type (M_NAME(c_messages[j])) &&
		!get_typedef (M_NAME(c_messages[j])))
	      have_name_tag = TRUE;
	  } else {
	    if (is_fn_ptr) {
	      if (!is_c_data_type (M_NAME(c_messages[j])) &&
		  !get_typedef (M_NAME(c_messages[j]))) {
		if (!have_name_tag) {
		  strcpy (param_tags[n_tags++], M_NAME(c_messages[j]));
		  have_name_tag = TRUE;
		}
	      }
	    }
	  }
 	  break;
 	case ASTERISK:
	  if (prev_tok == OPENPAREN)
	    is_fn_ptr = TRUE;
 	  ++n_derefs;
 	  break;
 	case ARRAYOPEN:
 	  ++n_derefs;
 	  break;
	case OPENPAREN:
	  ++n_parens;
	  break;
	case CLOSEPAREN:
	  --n_parens;
	  break;
 	}
      prev_tok = M_TOK(c_messages[j]);
    }
    DELETE_MESSAGES(c_messages,c_message_ptr,N_MESSAGES)

    _new_cvar (CVARREF(param));
    param -> attrs |= CVAR_ATTR_FN_PARAM;
    if (is_fn_ptr) param -> attrs |= CVAR_ATTR_FN_PTR_DECL;
    param -> n_derefs = n_derefs;

    k = n_tags - 1;
    if (have_name_tag)
      strcpy (param -> name, param_tags[k--]);
    switch (k)
      {
      case -1:
	strcpy (param -> type, "int");   /* Default. */
	break;
      case 0:
	if (PARAM_IS_C_STORAGE_CLASS(param_tags[k])) {
	  strcpy (param -> storage_class, param_tags[k--]);
	  strcpy (param -> type, "int");
	} else {
	  strcpy (param -> type, param_tags[k--]);
	}
	break;
      case 1:
	strcpy (param -> type, param_tags[k--]);
	if (PARAM_IS_C_STORAGE_CLASS (param_tags[k])) {
	  strcpy (param -> storage_class, param_tags[k--]);
	} else {
	  strcpy (param -> qualifier, param_tags[k--]);
	}
	break;
      case 2:
	strcpy (param -> type, param_tags[k--]);
	strcpy (param -> qualifier, param_tags[k--]);
	if (PARAM_IS_C_STORAGE_CLASS (param_tags[k])) {
	  strcpy (param -> storage_class, param_tags[k--]);
	} else {
	  strcpy (param -> qualifier2, param_tags[k--]);
	}
	break;
      case 3:
	strcpy (param -> type, param_tags[k--]);
	strcpy (param -> qualifier, param_tags[k--]);
	strcpy (param -> qualifier2, param_tags[k--]);
	if (PARAM_IS_C_STORAGE_CLASS (param_tags[k])) {
	  strcpy (param -> storage_class, param_tags[k--]);
	} else {
	  strcpy (param -> qualifier3, param_tags[k--]);
	}
	break;
      case 4:
	strcpy (param -> type, param_tags[k--]);
	strcpy (param -> qualifier, param_tags[k--]);
	strcpy (param -> qualifier2, param_tags[k--]);
	strcpy (param -> qualifier3, param_tags[k--]);
	if (PARAM_IS_C_STORAGE_CLASS (param_tags[k])) {
	  strcpy (param -> storage_class, param_tags[k--]);
	} else {
	  strcpy (param -> qualifier4, param_tags[k--]);
	}
	break;
      }

    if (!fn_param_decls) {
      fn_param_decls = fn_param_decls_ptr = param;
    } else {
      fn_param_decls_ptr -> next = param;
      param -> prev = fn_param_decls_ptr;
      fn_param_decls_ptr = param;
    }

    declaration_context = old_context;
    free (param_ptrs[i]);
  }
  restore_fn_declaration ();

  return SUCCESS;
}

/*
 *   TO DO - Trim trailing whitespace also.
 */

char *trim_whitespace (char *d, char *s) {

  int j;
  int whitespace = FALSE;

  tokenize (c_message_push, s);

  for (j = N_MESSAGES, *d = 0; j > c_message_ptr; j--) {

    switch (c_messages[j] -> tokentype)
      {
      case WHITESPACE:
      case NEWLINE:
	if (j == N_MESSAGES)
	  whitespace = TRUE;
	else
	  strcat (d, c_messages[j]->name);
	break;
      default:
	strcat (d, c_messages[j]->name);
	break;
      }

  }

  DELETE_MESSAGES(c_messages,c_message_ptr,N_MESSAGES)

  return d;
}

/*
 *    Return the stack index of the line end, including
 *    escaped newlines.
 */

int escaped_line_end (MESSAGE **message_stack, int this_message, 
		      int last_message) {

  int i;

  for (i = this_message; i > last_message; i--) {
    if (message_stack[i] -> tokentype == NEWLINE && 
	!LINE_SPLICE (message_stack, i))
      return i;
  }

  return ERROR;
}

int typedef_var (MESSAGE_STACK messages, int keyword_ptr) {

  int i;
  Boolean r,
    need_type;
  int end;
  int type_idx;
  int blocklevel;
  int func_identifier = 0;      /* Avoid a warning. */
  MESSAGE *m;
  enum {
    typedef_null,
    typedef_keyword,
    typedef_type,
    typedef_incomplete_type,
    typedef_tag,
  } typedef_state;

  end = get_stack_top (messages);

  for (i = keyword_ptr, type_idx = -1, typedef_state = typedef_null, 
	 blocklevel = 0,
	 need_type = False;
       i >= end; i--) {

    m = messages[i];

    if (M_ISSPACE (m)) continue;

    switch (m -> tokentype)
      {
      case OPENBLOCK:
	++blocklevel;
	break;
      case CLOSEBLOCK:
	--blocklevel;
	break;
      case SEMICOLON:
	if (!blocklevel) goto not_typed;
	break;
      case LABEL:
	/*
	 *  Structs and unions have separate namespaces for their
	 *  members.
	 */
	if (blocklevel) continue;

	switch (typedef_state)
	  {
	  case typedef_null:
	    if (strcmp (m -> name, "typedef")) {
	      warning (m, "Bad typedef keywords %s.", m -> name);
	      return ERROR;
	    }
	    typedef_state = typedef_keyword;
	    break;
	  case typedef_keyword:
	    type_idx = i;
	    if (!strcmp (m -> name, "struct") ||
		!strcmp (m -> name, "union") ||
		!strcmp (m -> name, "enum")) {
	      typedef_state = typedef_incomplete_type;
	    } else {
#ifdef __GNUC__
	      if (is_c_data_type (m -> name) || 
		  is_gnuc_builtin_type (m -> name) ||
		  get_typedef (m -> name) ||
		  is_incomplete_type (m -> name))
		typedef_state = typedef_type;
#else
	      if (is_c_data_type (m -> name) || get_typedef (m -> name))
		typedef_state = typedef_type;
#endif
	    }
	    break;
	  case typedef_type:
	    if (!get_typedef (m -> name) &&
		!is_c_data_type (m -> name)) {
	      typedef_state = typedef_tag; 
	      need_type = True;
	    }
	    break;
	  case typedef_incomplete_type:
	    typedef_state = typedef_tag;
	    if (!get_typedef (m -> name))
	      need_type = True;
	    break;
	  case typedef_tag:
#ifdef __GNUC__
	    if (is_gnu_extension_keyword (M_NAME(m))) {
	      int i2, attr_n_parens;
	      for (i2 = i, attr_n_parens = 0; i2 > end; i2--) {
		switch (messages[i2]->tokentype)
		  {
		  case OPENPAREN:
		    ++attr_n_parens;
		    break;
		  case CLOSEPAREN:
		    if (--attr_n_parens == 0) {
		      i = i2;
		      if (!blocklevel) goto not_typed;
		    }
		    break;
		  }
	      }
	    }
#endif
	    if (!get_typedef (m -> name) && !is_c_data_type (m -> name))
	      need_type = True;
	    break;
	  }
      }
  }

 not_typed:

  if (need_type == False)
    return ERROR;

  for (i = keyword_ptr - 1; i > end; i--) {

    if ((messages[i] -> tokentype == WHITESPACE) ||
	(messages[i] -> tokentype == NEWLINE))
      continue;

    switch (messages[i] -> tokentype)
      {
      case LABEL:
	if (!strcmp (messages[i] -> name, "enum")) {
	  add_typedef_from_cvar (enum_decl (messages, i));
	} else {
	  typedef_check = True;
	  if ((r = is_c_var_declaration_msg (messages, i, FALSE)) == True) {
	    add_typedef_from_parser ();
	  } else {
	    int __r;
	    /*
	     *  Note: The fill-in below should be qualified
	     *  by a preprocessor cache state if used to
	     *  delete duplicate typedefs.
	     */
	    for (__r = type_decls_ptr - 1; __r >= 0; __r--) {
	      if (is_gnuc_builtin_type (type_decls[__r]) || 
		  is_c_derived_type (type_decls[__r])) {
		warning (messages[keyword_ptr], 
			 "Redefinition of type %s.", type_decls[__r]); 
		return ERROR;
	      }
	    }
	    if (type_idx != -1) {
	      /*
	       *  Handle types that are not complete by the end of the 
	       *  declaration.
	       */
	      add_incomplete_type (messages, keyword_ptr, type_idx);
	    } else {
	      warning (messages[keyword_ptr], "error in typedef statement.");
	    }
	  } /* else */
	  typedef_check = False;
	} /* else */
	goto done;
	break;
      case OPENPAREN:
	if (!func_identifier)
	  func_identifier = TRUE;
	break;
      case CLOSEPAREN:
	if (func_identifier)
	  goto done;
	break;
      case SEMICOLON:
	goto done;
	break;
      }
  }

 done:
  return SUCCESS;
}

/* 
 *    Don't check the state because the statement is 
 *    terminated by a newline.
 */
int parse_include_directive (MESSAGE_STACK messages, int start, int end, 
			     INCLUDE *inc) {

  int i;
  int lasttoken = start;    /* Avoid a warning. */

  for (i = start, *inc -> name = 0; i >= end; i--) {
    
    if (messages[i] -> tokentype == WHITESPACE)
      continue;
    if (messages[i] -> tokentype == NEWLINE ||
	messages[i] -> tokentype == OPENCCOMMENT)
      break;

    switch (messages[i] -> tokentype)
      {
      case LITERAL:
	strcpy (inc -> name, messages[i] -> name);
	(void)substrcpy (inc -> name, inc -> name, 1, 
			 (int)(strlen (inc -> name) - 2));
	inc -> path_type = abs_path;
	break;
      case LT:
	inc -> path_type = inc_path;
	break;
      case FWDSLASH:
      case PERIOD:
      case MINUS:
      case PLUS:
	strcat (inc->name, messages[i] -> name);
	break;
      case LABEL:
	if (lasttoken == FWDSLASH || lasttoken == LT || lasttoken == PERIOD ||
	    lasttoken == MINUS || lasttoken == PLUS)
	  strcat (inc -> name, messages[i] -> name);
	break;
      }
    lasttoken = messages[i] -> tokentype;
  }

  return SUCCESS;
}

int find_n_subscripts (MESSAGE_STACK messages, int ptr, int *n_subscripts) {

  int i,
    end_ptr,
    n_array_lvl;
  Boolean in_subscript;
  MESSAGE *m;

  end_ptr = get_stack_top (messages);

  for (i = ptr, n_array_lvl = 0, in_subscript = False, *n_subscripts = 0; 
       i > end_ptr; i--) {

    m = messages[i];

    switch (m -> tokentype)
      {
      case SEMICOLON:
      case ARGSEPARATOR:
	if (!n_array_lvl) return SUCCESS;
	break;
      case ARRAYOPEN:
	if (!in_subscript) {
	  in_subscript = True;
	  ++*n_subscripts;
	}
	++n_array_lvl;
	break;
      case ARRAYCLOSE:
	--n_array_lvl;
	if (!n_array_lvl) in_subscript = False;
	break;
      }
  }

  return SUCCESS;
}

int math (MESSAGE *op_msg, MATH_OP op, VAL * op1, VAL *op2, VAL *result) {

  switch (op)
    {
    case add:
      switch (op1 -> type)
	{
	case INTEGER_T:
	  result -> type = INTEGER_T;
	  result -> value.i = op1 -> value.i + op2 -> value.i;
	  break;
	case LONG_T:
	  result -> type = LONG_T;
	  result -> value.l = op1 -> value.l + op2 -> value.l;
	  break;
	case LONGLONG_T:
	  result -> type = LONGLONG_T;
	  result -> value.l = (long int)(op1 -> value.ll + op2 -> value.ll);
	  break;
	case FLOAT_T:
	case DOUBLE_T:
	  result -> type = DOUBLE_T;
	  result -> value.d = op1 -> value.d + op2 -> value.d;
	  break;
	case LONGDOUBLE_T:
	  result -> type = LONGDOUBLE_T;
	  result -> value.ld = op1 -> value.ld + op2 -> value.ld;
	  break;
	default:
	  break;
	}
      break;
    case subtract:
      switch (op1 -> type)
	{
	case INTEGER_T:
	  result -> type = INTEGER_T;
	  result -> value.i = op1 -> value.i - op2 -> value.i;
	  break;
	case LONG_T:
	  result -> type = LONG_T;
	  result -> value.l = op1 -> value.l - op2 -> value.l;
	  break;
	case LONGLONG_T:
	  result -> type = LONGLONG_T;
	  result -> value.l = (long int)(op1 -> value.ll - op2 -> value.ll);
	  break;
	case FLOAT_T:
	case DOUBLE_T:
	  result -> type = DOUBLE_T;
	  result -> value.d = op1 -> value.d - op2 -> value.d;
	  break;
	case LONGDOUBLE_T:
	  result -> type = LONGDOUBLE_T;
	  result -> value.ld = op1 -> value.ld - op2 -> value.ld;
	  break;
	default:
	  break;
	}
      break;
    case mult:
      switch (op1 -> type)
	{
	case INTEGER_T:
	  result -> type = INTEGER_T;
	  result -> value.i = op1 -> value.i * op2 -> value.i;
	  break;
	case LONG_T:
	  result -> type = LONG_T;
	  result -> value.l = op1 -> value.l * op2 -> value.l;
	  break;
	case LONGLONG_T:
	  result -> type = LONGLONG_T;
	  result -> value.l = (long int)(op1 -> value.ll * op2 -> value.ll);
	  break;
	case FLOAT_T:
	case DOUBLE_T:
	  result -> type = DOUBLE_T;
	  result -> value.d = op1 -> value.d * op2 -> value.d;
	  break;
	case LONGDOUBLE_T:
	  result -> type = LONGDOUBLE_T;
	  result -> value.ld = op1 -> value.ld * op2 -> value.ld;
	  break;
	default:
	  break;
	}
      break;
    case divide:
      switch (op1 -> type)
	{
	case INTEGER_T:
	  result -> type = INTEGER_T;
	  if (!op2 -> value.i)
	    warning (op_msg, "Division by 0 error in math ().");
	  else
	    result -> value.i = op1 -> value.i / op2 -> value.i;
	  break;
	case LONG_T:
	  result -> type = LONG_T;
	  if (!op2 -> value.l)
	    warning (op_msg, "Division by 0 error in math ().");
	  else
	    result -> value.l = op1 -> value.l / op2 -> value.l;
	  break;
	case LONGLONG_T:
	  result -> type = LONGLONG_T;
	  if (!op2 -> value.ll)
	    warning (op_msg, "Division by 0 error in math ().");
	  else
	    result -> value.ll = (long int)(op1 -> value.ll / op2 -> value.ll);
	  break;
	case FLOAT_T:
	case DOUBLE_T:
	  result -> type = DOUBLE_T;
	  if (!op2 -> value.d)
	    warning (op_msg, "Division by 0 error in math ().");
	  else
	    result -> value.d = op1 -> value.d / op2 -> value.d;
	  break;
	case LONGDOUBLE_T:
	  result -> type = LONGDOUBLE_T;
	  if (!op2 -> value.ld)
	    warning (op_msg, "Division by 0 error in math ().");
	  else
	  result -> value.ld = op1 -> value.ld / op2 -> value.ld;
	  break;
	default:
	  break;
	}
      break;
      /* 
       *  TO DO - Determine if additional argument checking is
       *  necessary, and with asr, below, and whether the 
       *  arguments can be of different integer sizes.
       */
    case asl:
      switch (op1 -> type)
	{
	case INTEGER_T:
	  result -> type = INTEGER_T;
	  result -> value.i = op1 -> value.i << op2 -> value.i;
	  break;
	case LONG_T:
	  result -> type = LONG_T;
	  result -> value.l = op1 -> value.l << op2 -> value.l;
	  break;
	case LONGLONG_T:
	  result -> type = LONGLONG_T;
	  result -> value.l = (long int)(op1 -> value.ll << op2 -> value.ll);
	  break;
	default:
	  break;
	}
      break;
    case asr:
      switch (op1 -> type)
	{
	case INTEGER_T:
	  result -> type = INTEGER_T;
	  result -> value.i = op1 -> value.i >> op2 -> value.i;
	  break;
	case LONG_T:
	  result -> type = LONG_T;
	  result -> value.l = op1 -> value.l >> op2 -> value.l;
	  break;
	case LONGLONG_T:
	  result -> type = LONGLONG_T;
	  result -> value.l = (long int)(op1 -> value.ll >> op2 -> value.ll);
	  break;
	default:
	  break;
	}
      break;
    default:
      break;
    }

  return result -> type;
}

int eval_constant_expr (MESSAGE_STACK messages, int start, int *end, 
			VAL *result) {

  int i;
  MESSAGE *m;
  CVAR *c;
  int close_paren;
  int subexpr_result;
  int precedence = 0;
  int conditional_end_ptr,
    sizeof_end_ptr;

  /* Resolve the symbols and numeric values in the expression. */
  for (i = start; i >= *end; i--) {

    if (parse_exception != no_x)
      return FALSE;

    m = messages[i];
    switch (m -> tokentype) 
      {
      case CHAR:
	result -> value.i = m -> name[0] - '0';
	result -> type = INTEGER_T;
	m_print_val (m, result);
	break;
      case LITERAL_CHAR:
	result -> value.i = m -> name[1] - '0';
	result -> type = INTEGER_T;
	m_print_val (m, result);
	break;
      case LITERAL:
	result -> value.ptr = m -> name;
	result -> type = PTR_T;
	m_print_val (m, result);
	++m -> evaled;
	break;
      case INTEGER:
	result -> value.i = atoi (m -> name);
	result -> type = INTEGER_T;
	m_print_val (m, result);
	++m -> evaled;
	break;
      case LONG:
	result -> value.l = atol (m -> name);
	result -> type = LONG_T;
	m_print_val (m, result);
	++m -> evaled;
	break;
      case LONGLONG:
#ifdef __DJGPP__
	sscanf (m -> name, "%lld", &result -> value.ll);
#else
	result -> value.ll = atoll (m -> name);
#endif
	result -> type = LONGLONG_T;
	m_print_val (m, result);
	++m -> evaled;
	break;
      case FLOAT:
	result -> type = DOUBLE_T;
	result -> value.d = strtod(M_NAME(m), (char **)NULL);
	m_print_val (m, result);
	++m -> evaled;
	break;
      case LABEL:
	if (is_c_keyword ( m -> name)) {
	  /* FIXME !
	     Check for stray keywords that are not arguments
	     to sizeof (). */
	} else {
	  if ((c = get_local_var (m -> name)) == NULL) {
	    if ((c = get_typedef (m -> name)) == NULL) {
	      warning (m, "Undefined symbol, \"%s.\"", m -> name);
	      parse_exception = parse_error_x;
	    } /* if (c = get_typedef... */
	  } /* if (c = get_local_var... */
	} /* if (is_c_keyword) */
	break;
      default:
	break;
      }
  }

  re_eval: for (i = start; i >= *end; i--) {

    if (parse_exception != no_x)
      goto done;

    m = messages[i];

      switch (m -> tokentype) 
	{
	case SEMICOLON:
	  goto done;
	  break;
	case NEWLINE:
	  break;
	case CHAR:
	case LITERAL_CHAR:
	case LITERAL:
	case INTEGER:
	case LONG:
	case LONGLONG:
	case DOUBLE:
	case WHITESPACE:
	case PREPROCESS_EVALED:
	case RESULT:
	case LABEL:
	  break;
	default:
	  if (op_precedence (precedence, messages, i)) { 
	    switch (m -> tokentype)
	      {
	      case OPENPAREN:
		if ((close_paren = match_paren (messages, i,
						  get_stack_top (messages)))
		    == ERROR) {
		  parse_exception = mismatched_paren_x;
		  warning (messages[i], "Mismatched parentheses.");
		  goto done;
		}
		subexpr_result = 
		  constant_subexpr (messages, i, &close_paren, result);
		i = close_paren;
		break;
	      case CLOSEPAREN:  /* Matching parens are tagged as evaled by the 
				   parser on recursion. */
		parse_exception = mismatched_paren_x;
		warning (messages[i], "Mismatched parentheses.");
		goto done;
		break;
		/* Handle math operators */
	      case PLUS:
	      case MINUS:
	      case MULT:
	      case DIVIDE:
	      case ASL:
	      case ASR:
	      case BIT_AND:
	      case BIT_COMP:
	      case BIT_OR:
	      case BIT_XOR:
	      case MODULUS:
		eval_math (messages, i, result);
		++m -> evaled;
		break;
		/* Handle operators that return boolean values. */
	      case BOOLEAN_EQ:
	      case GT:
	      case LT:
	      case GE:
	      case LE:
	      case LOG_NEG:
	      case INEQUALITY:
	      case BOOLEAN_AND:
	      case BOOLEAN_OR:
		(void)eval_bool (messages, i, result);
		++m -> evaled;
		break;
	      case CONDITIONAL:
		/* This is the only ternary op that we need
		   to evaluate at the moment, so evaluate on 
		   its own. */
		(void)question_conditional_eval (messages, i, 
					   &conditional_end_ptr, result);
		i = conditional_end_ptr;
		++m -> evaled;
		break;
	      case SIZEOF:
		handle_sizeof_op (messages, i, &sizeof_end_ptr, result);
		i = sizeof_end_ptr;
		break;
		/* Catch errors of operators that assign values. */
	      case INCREMENT:
	      case DECREMENT:
	      case EQ:
	      case ASR_ASSIGN:
	      case ASL_ASSIGN:
	      case PLUS_ASSIGN:
	      case MINUS_ASSIGN:
	      case MULT_ASSIGN:
	      case DIV_ASSIGN:
	      case BIT_AND_ASSIGN:
	      case BIT_OR_ASSIGN:
	      case BIT_XOR_ASSIGN:
		parse_exception = parse_error_x;
		warning (messages[i], "Assignment in constant expression.");
		goto done;
		break;
	      default:
		break;
	      }
	  }
	  break;
	}  /* switch (m -> tokentype) */
  }

  /* TO DO -
   * Progressing to the next precedence relies too much on 
   * setting subexpressions to evaled token types.  Use the
   * eval message member, or find a way to step through 
   * precedence levels without changing the token type.
   */
  for (; precedence < 13; ++precedence) {
    for (i = start; i >= *end; i--) {
      if (op_precedence (precedence, messages, i)  &&
	  (messages[i] -> evaled == 0))
	goto re_eval;
    }
  }

 done:
  return is_val_true (result);
}

int fn_has_gnu_attribute (MESSAGE_STACK messages, int close_paren_idx) {
#ifdef __GNUC__
  int next_token_idx;

  if ((next_token_idx = nextlangmsg (messages, close_paren_idx)) != ERROR) {
    if (M_TOK(messages[next_token_idx]) == LABEL)
	return is_gnu_extension_keyword (M_NAME(messages[next_token_idx]));
  }
#endif
  return FALSE;
}

int fn_is_declaration (MESSAGE_STACK messages, int arglist_start_idx) {

  int arglist_end_idx;
  int next_tok_idx;
  int next_tok_type;

  if ((arglist_end_idx = match_paren (messages, arglist_start_idx,
				      get_stack_top (messages))) != ERROR) {
    if ((next_tok_idx = nextlangmsg (messages, arglist_end_idx)) != ERROR) {
      next_tok_type = M_TOK(messages[next_tok_idx]);
#ifdef __GNUC__
      if ((next_tok_type == OPENBLOCK) ||
	  (next_tok_type == SEMICOLON) ||
	  ((next_tok_type == LABEL) && 
	   fn_has_gnu_attribute (messages, arglist_end_idx)))
	return TRUE;
#else
      if ((next_tok_type == OPENBLOCK) ||
	  (next_tok_type == SEMICOLON))
	return TRUE;
#endif
    }
  }
  return FALSE;
}

/*
 *  If calling on another token than OPENPAREN, then this needs to
 *  be modified suitably.
 */
int param_is_fnptr (MESSAGE_STACK messages, int idx, int n_parens) {

  int prev_tok_idx, next_tok_idx;
  int next_close_paren_idx;

  if ((prev_tok_idx = prevlangmsg (messages, idx)) == ERROR)
    return FALSE;

  /*
   *  If the previous token is a closing paren, then its a (*fn)().
   */
  if (M_TOK(messages[prev_tok_idx]) == CLOSEPAREN)
    return TRUE;

  /*
   *  Otherwise, check for the token after the matching close
   *  paren.
   */
  if ((next_close_paren_idx = match_paren (messages, idx, 
					  get_stack_top (messages))) 
      == ERROR)
    return FALSE;
  
  if ((next_tok_idx = nextlangmsg (messages, next_close_paren_idx)) == ERROR)
    return FALSE;

  if (M_TOK(messages[next_tok_idx]) == OPENPAREN)
    return TRUE;

  return FALSE;
}

int is_c_prefix_op (MESSAGE_STACK messages, int idx) {

  int next_tok;
  CVAR *c;

  if (!IS_C_UNARY_MATH_OP(M_TOK(messages[idx])))
    return FALSE;

  if ((next_tok = nextlangmsg (messages, idx)) == ERROR)
    return FALSE;

  if (((c = get_local_var (M_NAME(messages[next_tok]))) != NULL) ||
      global_var_is_declared (M_NAME(messages[next_tok])))
    return TRUE;
  
  return FALSE;
}

char *subscript_var_basename (char *var) {
  static char buf[MAXLABEL];
  int i, start_idx, end_idx;

  start_idx = get_stack_top (c_messages);
  end_idx = tokenize (c_message_push, var);

  *buf = 0;
  for (i = start_idx; i > end_idx; i--) {
    if (M_ISSPACE(c_messages[i])) continue;

    if (M_TOK(c_messages[i]) == LABEL)
      strcat (buf, M_NAME(c_messages[i]));

    if (M_TOK(c_messages[i]) == ARRAYOPEN)
      break;
  }

  DELETE_MESSAGES(c_messages,c_message_ptr,N_MESSAGES)
  return buf;
}

char *deref_var_basename (char *var) {
  static char buf[MAXLABEL];
  int i, start_idx, end_idx;

  start_idx = get_stack_top (c_messages);
  end_idx = tokenize (c_message_push, var);

  *buf = 0;
  for (i = start_idx; i > end_idx; i--) {
    if (M_ISSPACE(c_messages[i])) continue;

    if (M_TOK(c_messages[i]) == LABEL)
      strcat (buf, M_NAME(c_messages[i]));

    if ((M_TOK(c_messages[i]) == DEREF) ||
	(M_TOK(c_messages[i]) == PERIOD))
      break;
  }

  DELETE_MESSAGES(c_messages,c_message_ptr,N_MESSAGES)
  return buf;
}

/*
 *  For now, simply look for an lvalue after the matching closing
 *  parenthesis, and let the compiler worry about whether the type 
 *  is valid. Return the index of the matching closing parenthesis.
 */
int is_typecast (MESSAGE_STACK messages, int start_idx) {

  int matching_paren_ptr, next_tok_ptr;

  if (M_TOK(messages[start_idx]) != OPENPAREN) return ERROR;

  if ((matching_paren_ptr = 
       match_paren (messages, start_idx, get_stack_top (messages))) != ERROR){
    if ((next_tok_ptr = nextlangmsg (messages, matching_paren_ptr)) 
	!= ERROR) {
      if (!IS_C_OP_TOKEN_NOEVAL(M_TOK(messages[next_tok_ptr]))) {
	return matching_paren_ptr;
      } 
    }
  }
  
  return ERROR;
}

