/*******************************************************************************
 * Simplified Wrapper and Interface Generator  (SWIG)
 * 
 * Author : David Beazley
 *
 * Department of Computer Science        
 * University of Chicago
 * 1100 E 58th Street
 * Chicago, IL  60637
 * beazley@cs.uchicago.edu
 *
 * Please read the file LICENSE for the copyright and terms by which SWIG
 * can be used and distributed.
 *******************************************************************************/

/*************************************************************************
 * $Header: /cvsroot/SWIG1.1/SWIG/scanner.cxx,v 1.3 1999/08/10 21:38:17 beazley Exp $
 * scanner.c
 *
 * Dave Beazley
 * January 1996
 *
 * Input scanner.   This scanner finds and returns tokens
 * for the wrapper generator.   Since using lex/flex from
 * C++ is so F'ed up, I've written this function to replace
 * them entirely.     It's not as fast, but hopefully it will
 * eliminate alot of compilation problems.
 *
 *************************************************************************/


#include "internal.h"
#include "parser.h"
#include <string.h>
#include <ctype.h>

#define  YYBSIZE  8192

struct InFile {
  FILE   *f;
  int    line_number;
  char  *in_file;
  int    extern_mode;	
  int    force_extern;
  struct InFile *prev;
};

// This structure is used for managing code fragments as
// might be used by the %inline directive and handling of
// nested structures.

struct CodeFragment {
  char          *text;
  int            line_number;
  CodeFragment  *next;
};

InFile  *in_head;

FILE    *LEX_in = NULL;

static String         header;
static String         comment;
       String         CCode;              // String containing C code
static char           *yybuffer;
static int            lex_pos = 0;
static int            lex_len = 0;         
static char           *inline_yybuffer = 0;
static int            inline_lex_pos = 0;
static int            inline_lex_len = 0;
static int            inline_line_number = 0;
static CodeFragment  *fragments = 0;      // Code fragments

static 
char           yytext[YYBSIZE];
static int     yylen = 0;
int            line_number = 1;
int            column = 1;
int            column_start = 1;
char          *input_file;
int            start_line = 0;
static  int    comment_start;
static  int    scan_init  = 0;
static  int    num_brace = 0;
static  int    last_brace = 0;
static  int    in_define = 0;
static  int    define_first_id = 0;   /* Set when looking for first identifier of a define */
extern  int    Error;


/**************************************************************
 * scanner_init()
 *
 * Initialize buffers
 **************************************************************/

void scanner_init() {

  yybuffer = (char *) malloc(YYBSIZE);
  scan_init = 1;
}

/**************************************************************
 * scanner_file(FILE *f)
 *
 * Start reading from new file 
 **************************************************************/
void scanner_file(FILE *f) {
  InFile *in;

  in = new InFile;
  in->f = f;
  in->in_file = input_file;
  in->extern_mode = WrapExtern;	
  in->force_extern = ForceExtern;
  if (in_head) in_head->line_number = line_number+1;
  if (!in_head) in->prev = 0;
  else in->prev = in_head;
  in_head = in;
  LEX_in = f;
  line_number = 1;
}

/**************************************************************
 * scanner_close()
 *
 * Close current input file and go to next 
 **************************************************************/

void scanner_close() {

  InFile *p;
  static int lib_insert = 0;
  fclose(LEX_in);
  if (!in_head) return;
  p = in_head->prev;
  if (p != 0) {
    LEX_in = p->f;
    line_number = p->line_number;
    input_file = p->in_file;
    WrapExtern = p->extern_mode;
    if (!WrapExtern) remove_symbol("SWIGEXTERN");
    ForceExtern = p->force_extern;
  } else {
    LEX_in = NULL;
  }
  delete in_head;
  in_head = p;

  // if LEX_in is NULL it means we're done with the interface file. We're now
  // going to grab all of the library files.

  if ((!LEX_in) && (!lib_insert)) {
    library_insert();
    lib_insert = 1;
  }

}

/**************************************************************
 * char nextchar()
 *
 * gets next character from input.
 * If we're in inlining mode, we actually retrieve a character
 * from inline_yybuffer instead.
 **************************************************************/

char nextchar() {

    char c = 0;

    if (Inline) {
      if (inline_lex_pos >= inline_lex_len) {
	// Done with inlined code.  Check to see if we have any
	// new code fragments.  If so, switch to them.
	delete inline_yybuffer;
	if (fragments) {
	  CodeFragment *f;
	  inline_yybuffer = fragments->text;
	  inline_lex_pos = 1;
	  inline_lex_len = strlen(fragments->text);
	  line_number = fragments->line_number;
	  f = fragments->next;
	  delete fragments;
	  fragments = f;
	  c = inline_yybuffer[0];
	} else {
	  c = 0;
	  Inline = 0;
	  line_number = inline_line_number;       // Restore old line number
	}
      } else {
	inline_lex_pos++;
	c = inline_yybuffer[inline_lex_pos-1];
      }
    }
    if (!Inline) {
      if (lex_pos >= lex_len) {
	if (!LEX_in) {
	  SWIG_exit(1);
	}
	while(fgets(yybuffer, YYBSIZE, LEX_in) == NULL) {
	  scanner_close();        // Close current input file
	  if (!LEX_in) return 0;  // No more, we're outta here
	}
	lex_len = strlen(yybuffer);
	lex_pos = 0;
      }
      
      lex_pos++;
      c = yybuffer[lex_pos-1];
    }
      
    if (yylen >= YYBSIZE) {
      fprintf(stderr,"** FATAL ERROR.  Buffer overflow in scanner.cxx.\nReport this to swig@cs.utah.edu.\n");
      SWIG_exit(1);
    }
    yytext[yylen] = c;
    yylen++;
    if (c == '\n') {
      line_number++;
      column = 1;
    } else {
      column++;
    }
    return(c);
}

void retract(int n) {
  int i, j, c;
  
  for (i = 0; i < n; i++) {
    if (Inline) {
      inline_lex_pos--;
      if (inline_lex_pos < 0) {
	fprintf(stderr,"Internal scanner error. inline_lex_pos < 0\n");
	SWIG_exit(1);
      }
    }
    else lex_pos--;
    yylen--;
    column--;
    if (yylen >= 0) {
      if (yytext[yylen] == '\n') {
	line_number--;
	// Figure out what column we're in
	c = yylen-1;
	j = 1;
	while (c >= 0){
	  if (yytext[c] == '\n') break;
	  j++;
	  c--;
	}
	column = j;
      }
    }
  }
  if (yylen < 0) yylen = 0;
}

/**************************************************************
 * start_inline(char *text, int line)
 * 
 * This grabs a chunk of text and tries to inline it into
 * the current file.  (This is kind of wild, but cool when
 * it works).
 *
 * If we're already in inlining mode, we will save the code
 * as a new fragment.
 **************************************************************/

void start_inline(char *text, int line) {

  if (Inline) {

    // Already processing a code fragment, simply hang on
    // to this one for later.

    CodeFragment *f,*f1;

    // Add a new code fragment to our list
    f = new CodeFragment;
    f->text = copy_string(text);
    f->line_number = line;
    f->next = 0;
    if (!fragments) fragments = f;
    else {
      f1 = fragments;
      while (f1->next) f1 = f1->next;
      f1->next = f;
    }
  } else {

    // Switch our scanner over to process text from a string.
    // Save current line number and other information however.

    inline_yybuffer = copy_string(text);
    inline_lex_len = strlen(text);
    inline_lex_pos = 0;
    inline_line_number = line_number;       // Make copy of old line number
    line_number = line;
    Inline = 1;
  }
}

/**************************************************************
 * yycomment(char *, int line)
 *
 * Inserts a comment into a documentation entry.    
 **************************************************************/

void yycomment(char *s, int line, int col) {
  comment_handler->add_comment(s,line,col,input_file);
}

/**************************************************************
 * void skip_brace(void)
 *
 * Found a {.
 * Skip all characters until we find a matching closed }.
 *
 * This function is used to skip over inlined C code and other
 * garbage found in interface files.
 ***************************************************************/

void skip_brace(void) {

  char c;
  CCode = "{";
  while (num_brace > last_brace) {
    if ((c = nextchar()) == 0) {
      fprintf(stderr,"%s : Line %d.  Missing '}'. Reached end of input.\n",
	      input_file, line_number);
      FatalError();
      return;
    }
    CCode << c;
    if (c == '{') num_brace++;
    if (c == '}') num_brace--;
    yylen = 0;
  }
}


/**************************************************************
 * void skip_template(void)
 *
 * Found a <.
 * Skip all characters until we find a matching closed >.
 *
 * This function is used to skip over C++ templated types
 * and objective-C protocols.
 ***************************************************************/

void skip_template(void) {

  char c;
  CCode = "<";
  int  num_lt = 1;
  while (num_lt > 0) {
    if ((c = nextchar()) == 0) {
      fprintf(stderr,"%s : Line %d.  Missing '>'. Reached end of input.\n",
	      input_file, line_number);
      FatalError();
      return;
    }
    CCode << c;
    if (c == '<') num_lt++;
    if (c == '>') num_lt--;
    yylen = 0;
  }
}

/**************************************************************
 * void skip_to_end(void)
 *
 * Skips to the @end directive in a Objective-C definition
 **************************************************************/

void skip_to_end(void) {
  char c;
  int  state = 0;
  yylen = 0;
  while ((c = nextchar())){
    switch(state) {
    case 0:
      if (c == '@') state = 1;
      else yylen = 0;
      break;
    case 1:
      if (isspace(c)) {
	if (strncmp(yytext,"@end",4) == 0) return; 
	else {
	  yylen = 0;
	  state = 0;
	}
      } else {
	state = 1;
      }
      break;
    }
  }
  fprintf(stderr,"%s : EOF. Missing @end. Reached end of input.\n",
	  input_file);
  FatalError();
  return;
}  

/**************************************************************
 * void skip_decl(void)
 *
 * This tries to skip over an entire declaration.   For example
 *
 *  friend ostream& operator<<(ostream&, const char *s);
 * 
 * or 
 *  friend ostream& operator<<(ostream&, const char *s) { };
 *
 **************************************************************/

void skip_decl(void) {
  char c;
  int  done = 0;
  while (!done) {
    if ((c = nextchar()) == 0) {
      fprintf(stderr,"%s : Line %d.  Missing semicolon. Reached end of input.\n",
	      input_file, line_number);
      FatalError();
      return;
    }
    if (c == '{') {
      last_brace = num_brace;
      num_brace++;
      break;
    }
    yylen = 0;
    if (c == ';') done = 1;
  }
  if (!done) {
    while (num_brace > last_brace) {
      if ((c = nextchar()) == 0) {
	fprintf(stderr,"%s : Line %d.  Missing '}'. Reached end of input.\n",
		input_file, line_number);
	FatalError();
	return;
      }
      if (c == '{') num_brace++;
      if (c == '}') num_brace--;
      yylen = 0;
    }
  }
}

/**************************************************************
 * void skip_define(void)
 *
 * Skips to the end of a #define statement.
 *
 **************************************************************/

void skip_define(void) {
  char c;
  while (in_define) {
    if ((c = nextchar()) == 0) return;
    if (c == '\\') in_define = 2;
    if (c == '\n') {
      if (in_define == 2) {
	in_define = 1;
      } else if (in_define == 1) {
	in_define = 0;
      }
    }
    yylen = 0;
  }
}

/**************************************************************
 * int skip_cond(int inthen)
 *
 * Skips the false portion of an #ifdef directive.   Looks
 * for either a matching #else or #endif 
 *
 * inthen is 0 or 1 depending on whether or not we're
 * handling the "then" or "else" part of a conditional.
 *
 * Returns 1 if the else part of the #if-#endif block was
 * reached.  Returns 0 otherwise.  Returns -1 on error.
 **************************************************************/

int skip_cond(int inthen) {
  int    level = 0;         /* Used to handled nested if-then-else */
  int    state = 0;
  char   c;
  int    start_line;
  char  *file;
  
  file = copy_string(input_file);
  start_line = line_number;
  yylen = 0;

  while(1) {
    switch(state) {
    case 0 :
      if ((c = nextchar()) == 0) {
	fprintf(stderr,"%s : Line %d.  Unterminated #if-else directive.\n", file, start_line);
	FatalError();
	return -1;     /* Error */
      }
      if ((c == '#') || (c == '%')) {
	state = 1;
      } else if (isspace(c)) {
	yylen =0;
	state = 0;
      } else {
	/* Some non-whitespace character. Look for line end */
	yylen = 0;
	state = 3;
      }
      break;
    case 1:
      /* Beginning of a C preprocessor statement */
      if ((c = nextchar()) == 0) {
	fprintf(stderr,"%s : Line %d.  Unterminated #if-else directive.\n", file, start_line);
	FatalError();
	return -1;     /* Error */
      }
      if (c == '\n') {
	state = 0;
	yylen = 0;
      }
      else if (isspace(c)) {
	state = 1;
	yylen--;
      } else {
	state = 2;
      }
      break;
    case 2:
      /* CPP directive */
      if ((c = nextchar()) == 0)  {
	fprintf(stderr,"%s : Line %d.  Unterminated #if-else directive.\n", file, start_line);
	FatalError();
	return -1;     /* Error */
      }
      if ((c == ' ') || (c == '\t') || (c=='\n')) {
	yytext[yylen-1] = 0;
	if ((strcmp(yytext,"#ifdef") == 0) || (strcmp(yytext,"%ifdef") == 0)) {
	  level++;
	  state = 0;
	} else if ((strcmp(yytext,"#ifndef") == 0) || (strcmp(yytext,"%ifndef") == 0)) {
	  level++;
	  state = 0;
	} else if ((strcmp(yytext,"#if") == 0) || (strcmp(yytext,"%if") == 0)) {
	  level++;
	  state = 0;
	} else if ((strcmp(yytext,"#else") == 0) || (strcmp(yytext,"%else") == 0)) {
	  if (level == 0) {    /* Found matching else.  exit */
	    if (!inthen) {
	      /* Hmmm.  We've got an "extra #else" directive here */
	      fprintf(stderr,"%s : Line %d.  Misplaced #else.\n", input_file, line_number);
	      FatalError();
	      yylen = 0;
	      state = 0;
	    } else {
	      yylen = 0;
	      delete file;
	      return 1;
	    }
	  } else {
	    yylen = 0;
	    state = 0;
	  }
	} else if ((strcmp(yytext,"#endif") == 0) || (strcmp(yytext,"%endif") == 0)) {
	  if (level <= 0) {    /* Found matching endif. exit */
	    yylen = 0;
	    delete file;
	    return 0;
	  } else {
	    state = 0;
	    yylen = 0;
	    level--;
	  }
	} else if ((strcmp(yytext,"#elif") == 0) || (strcmp(yytext,"%elif") == 0)) {
	  if (level <= 0) {
	    // If we come across this, we pop it back onto the input queue and return
	    retract(6);
	    delete file;
	    return 0;
	  } else {
	    yylen = 0;
	    state = 0;
	  }
	} else {
	  yylen = 0;
	  state = 0;
	}
      }
      break;
    case 3:
      /* Non-white space.  Look for line break */
      if ((c = nextchar()) == 0) {
	fprintf(stderr,"%s : Line %d.  Unterminated #if directive.\n", file, start_line);
	FatalError();
	return -1;     /* Error */
      }
      if (c == '\n') {
	yylen = 0;
	state = 0;
      } else {
	yylen = 0;
	state = 3;
      }
      break;
    }
  }
}
      
/**************************************************************
 * int yylook()
 *
 * Lexical scanner.
 * See Aho,Sethi, and Ullman,  pg. 106
 **************************************************************/

int yylook(void) {

    int      state;
    char     c = 0;

    state = 0;
    yylen = 0;
    while(1) {

/*	printf("State = %d\n", state);   */
	switch(state) {

	case 0 :
	  if((c = nextchar()) == 0) return(0);

	  /* Process delimeters */

	  if (c == '\n') {
	    state = 0;
	    yylen = 0;
	    if (in_define == 1) {
	      in_define = 0;
	      return(ENDDEF);
	    } else if (in_define == 2) {
	      in_define = 1;
	    }
	  } else if (isspace(c)) {
	    state = 0;
	    yylen = 0;
	  }
	      
	  /* Look for single character symbols */
	      
	  else if (c == '(') return (LPAREN);
	  else if (c == ')') return (RPAREN);
	  else if (c == ';') return (SEMI);
	  else if (c == ',') return (COMMA);
	  else if (c == '*') return (STAR);
	  else if (c == '}') {
	    num_brace--;
	    if (num_brace < 0) {
	      fprintf(stderr,"%s : Line %d. Error. Extraneous '}' (Ignored)\n",
		      input_file, line_number);
	      state = 0;
	      num_brace = 0;
	    } else {
	      return (RBRACE);
	    }
	  }
	  else if (c == '{') {
	    last_brace = num_brace;
	    num_brace++;
	    return (LBRACE);
	  }
	  else if (c == '=') return (EQUAL);
	  else if (c == '+') return (PLUS);
          else if (c == '-') return (MINUS);
	  else if (c == '&') return (AND);
	  else if (c == '|') return (OR);
	  else if (c == '^') return (XOR);
          else if (c == '<') state = 60;
	  else if (c == '>') state = 61;
	  else if (c == '~') return (NOT);
          else if (c == '!') return (LNOT);	                  
	  else if (c == '\\') {
	    if (in_define == 1) {
	      in_define = 2;
	      state = 0;
	    } else 
	      state = 99;
	  }
  	  else if (c == '[') return (LBRACKET);
	  else if (c == ']') return (RBRACKET);

	  /* Look for multi-character sequences */
	  
	  else if (c == '/') state = 1;    // Comment (maybe) 
	  else if (c == '\"') state = 2;   // Possibly a string
	  else if (c == '#') state = 3;    // CPP
	  else if (c == '%') state = 4;    // Directive
	  else if (c == '@') state = 4;    // Objective C keyword
	  else if (c == ':') state = 5;    // maybe double colon
	  else if (c == '0') state = 83;   // An octal or hex value
	  else if (c == '\'') state = 9;   // A character constant
	  else if (c == '.') state = 100;  // Maybe a number, maybe just a period
	  else if (isdigit(c)) state = 8;  // A numerical value
	  else if ((isalpha(c)) || (c == '_') || (c == '$')) state = 7;
	  else state = 99;
	  break;
	case 1:  /*  Comment block */
	  if ((c = nextchar()) == 0) return(0);
	  if (c == '/') {
	    comment_start = line_number;
	    column_start = column;
	    comment = "  ";
	    state = 10;        // C++ style comment
	  } else if (c == '*') {
	    comment_start = line_number;
	    column_start = column;
	    comment = "  ";
	    state = 11;   // C style comment
	  } else {
	    retract(1);
	    return(SLASH);
	  }
	  break;
	case 10:  /* C++ style comment */
	  if ((c = nextchar()) == 0) {
	    fprintf(stderr,"%s : EOF. Unterminated comment detected.\n",input_file);
	    FatalError();
	    return 0;
	  }
	  if (c == '\n') {
	    comment << c;
	    // Add the comment to documentation
	    yycomment(comment.get(),comment_start, column_start);
	    yylen = 0;
	    state = 0;
	    if (in_define == 1) {
	      in_define = 0;
	      return(ENDDEF);
	    }
	  } else {
	    state = 10;
	    comment << c;
	    yylen = 0;
	  }
	  break;
	case 11: /* C style comment block */
	  if ((c = nextchar()) == 0) {
	    fprintf(stderr,"%s : EOF. Unterminated comment detected.\n", input_file);
	    FatalError();
	    return 0;
	  }
	  if (c == '*') {
	    state = 12;
	  } else {
	    comment << c;
	    yylen = 0;
	    state = 11;
	  }
	  break;
	case 12: /* Still in C style comment */
	  if ((c = nextchar()) == 0) {
	    fprintf(stderr,"%s : EOF. Unterminated comment detected.\n", input_file);
	    FatalError();
	    return 0;
	  }
	  if (c == '*') {
	    comment << c;
	    state = 12;
	  } else if (c == '/') {
	    comment << "  \n";
	    yycomment(comment.get(),comment_start,column_start);
	    yylen = 0;
	    state = 0;
	  } else {
	    comment << '*' << c;
	    yylen = 0;
	    state = 11;
	  }
	  break;

	case 2: /* Processing a string */
	  if ((c = nextchar()) == 0) {
	    fprintf(stderr,"%s : EOF. Unterminated string detected.\n", input_file);
	    FatalError();
	    return 0;
	  }
	  if (c == '\"') {
	    yytext[yylen-1] = 0;
	    yylval.id = copy_string(yytext+1);
	    return(STRING);
	  } else if (c == '\\') {
	    state = 21;             /* Possibly an escape sequence. */
	    break;
	  } else state = 2;
	  break;
	case 21: /* An escape sequence. get next character, then go
		    back to processing strings */

	  if ((c = nextchar()) == 0) return 0;
	  state = 2;
	  break;

	case 3: /* a CPP directive */

	  if (( c= nextchar()) == 0) return 0;
	  if (c == '\n') {
	    retract(1);
	    yytext[yylen] = 0;
	    yylval.id = yytext;
	    return(POUND);
	  } else if ((c == ' ') || (c == '\t')) {  // Ignore white space after # symbol
	    yytext[yylen] = 0;
	    yylen--;
	    state = 3;
	  } else {
	    yytext[yylen] = 0;
	    state = 31;
	  }
	  break;
	case 31:
	  if ((c = nextchar()) == 0) return 0;
	  if ((c == ' ') || (c == '\t') || (c=='\n')) {
	    retract(1);
	    yytext[yylen] = 0;
	    if (strcmp(yytext,"#define") == 0) {
	      in_define = 1;
	      define_first_id = 1;
	      return(DEFINE);
	    } else if (strcmp(yytext,"#ifdef") == 0) {
	      return(IFDEF);
	    } else if (strcmp(yytext,"#ifndef") == 0) {
	      return(IFNDEF);
	    } else if (strcmp(yytext,"#else") == 0) {
	      return(ELSE);
	    } else if (strcmp(yytext,"#endif") == 0) {
	      return(ENDIF);
	    } else if (strcmp(yytext,"#undef") == 0) {
	      return(UNDEF);
	    } else if (strcmp(yytext,"#if") == 0) {
	      return(IF);
	    } else if (strcmp(yytext,"#elif") == 0) {
	      return(ELIF);
	    } else {
	      /* Some kind of "unknown CPP directive.  Skip to end of the line */
	      state = 32;
	    }
	  }
	  break;
	case 32:
	  if ((c = nextchar()) == 0) return 0;
	  if (c == '\n') {
	    retract(1);
	    yytext[yylen] = 0;
	    yylval.id = yytext;
	    return(POUND);
	  }
	  state = 32;
	  break;

	case 4: /* A wrapper generator directive (maybe) */
	  if (( c= nextchar()) == 0) return 0;
	  if (c == '{') {
	    state = 40;   /* Include block */
	    header = "";
	    start_line = line_number;
	  }
	  else if ((isalpha(c)) || (c == '_')) state = 7;
	  else {
	    retract(1);
	    state = 99;
	  }
	  break;
	  
	case 40: /* Process an include block */
	  if ((c = nextchar()) == 0) {
	    fprintf(stderr,"%s : EOF. Unterminated include block detected.\n", input_file);
	    FatalError();
	    return 0;
	  }
	  yylen = 0;
	  if (c == '%') state = 41;
	  else {
	    header << c;
	    yylen = 0;
	    state = 40;
	  }
	  break;
	case 41: /* Still processing include block */
	  if ((c = nextchar()) == 0) {
	    fprintf(stderr,"%s : EOF. Unterminated include block detected.\n", input_file);
	    FatalError();
	    return 0;
	  }
	  if (c == '}') {
	    yylval.id = header.get();
	    return(HBLOCK);
	  } else {
	    header << '%';
	    header << c;
	    yylen = 0;
	    state = 40;
	  }
	  break;

	case 5: /* Maybe a double colon */

	  if (( c= nextchar()) == 0) return 0;
	  if ( c == ':') return DCOLON;
	  else {
	    retract(1);
	    return COLON;
	  }


	case 60: /* shift operators */
	  if ((c = nextchar()) == 0) return (0);
	  if (c == '<') return LSHIFT;
	  else {
	    retract(1);
	    return LESSTHAN;
	  }
	  break;
	case 61: 
	  if ((c = nextchar()) == 0) return (0);
	  if (c == '>') return RSHIFT;
	  else {
	    retract(1);
            return GREATERTHAN;
	  }
	  break;
	case 7: /* Identifier */
	  if ((c = nextchar()) == 0) return(0);
	  if (isalnum(c) || (c == '_') || (c == '.') || (c == '$'))
	    //              || (c == '.') || (c == '-'))
	    state = 7;
	  else if (c == '(') {
	    /* We might just be in a CPP macro definition.  Better check */
	    if ((in_define) && (define_first_id)) {
	      /* Yep.  We're going to ignore the rest of it */
	      skip_define();
	      define_first_id = 0;
	      return (MACRO);
	    } else {
	      retract(1);
	      define_first_id = 0;
	      return(ID);
	    }
	  } else {
	    retract(1);
	    define_first_id = 0;
	    return(ID);
	  }
	  break;
	case 8: /* A numerical digit */
	  if ((c = nextchar()) == 0) return(0);
	  if (c == '.') {state = 81;}
	  else if ((c == 'e') || (c == 'E')) {state = 86;}
	  else if ((c == 'f') || (c == 'F')) {
             yytext[yylen] = 0;
	     yylval.id = copy_string(yytext);
	     return(NUM_FLOAT);
	  }
	  else if (isdigit(c)) { state = 8;}
	  else if ((c == 'l') || (c == 'L')) {
	    state = 87;
	  } else if ((c == 'u') || (c == 'U')) {
	    state = 88;
	  } else {
	      retract(1);
	      yytext[yylen] = 0;
	      yylval.id = copy_string(yytext);
	      return(NUM_INT);
	    }
	  break;
	case 81: /* A floating pointer number of some sort */
	  if ((c = nextchar()) == 0) return(0);
	  if (isdigit(c)) state = 81;
	  else if ((c == 'e') || (c == 'E')) state = 82;
          else if ((c == 'f') || (c == 'F') || (c == 'l') || (c == 'L')) {
	    yytext[yylen] = 0;
	    yylval.id = copy_string(yytext);
	    return(NUM_FLOAT);
	  } else {
	    retract(1);
	    yytext[yylen] = 0;
	    yylval.id = copy_string(yytext);
	    return(NUM_FLOAT);
	  }
	  break;
	case 82:
	  if ((c = nextchar()) == 0) return(0);
	  if ((isdigit(c)) || (c == '-') || (c == '+')) state = 86;
	  else {
	    retract(2);
	    yytext[yylen-1] = 0;
	    yylval.id = copy_string(yytext);
	    return(NUM_INT);
	  }
	  break;
	case 83:
	  /* Might be a hexidecimal or octal number */
	  if ((c = nextchar()) == 0) return(0);
	  if (isdigit(c)) state = 84;
	  else if ((c == 'x') || (c == 'X')) state = 85;
	  else if (c == '.') state = 81;
	  else if ((c == 'l') || (c == 'L')) {
	    state = 87;
	  } else if ((c == 'u') || (c == 'U')) {
	    state = 88;
	  } else {
	    retract(1);
	    yytext[yylen] = 0;
	    yylval.id = copy_string(yytext);
	    return(NUM_INT);
	  }
	  break;
	case 84:
	  /* This is an octal number */
	  if ((c = nextchar()) == 0) return (0);
	  if (isdigit(c)) state = 84;
	  else if ((c == 'l') || (c == 'L')) {
	    state = 87;
	  } else if ((c == 'u') || (c == 'U')) {
	    state = 88;
	  } else {
	    retract(1);
	    yytext[yylen] = 0;
	    yylval.id = copy_string(yytext);
	    return(NUM_INT);
	  }
	  break;
	case 85:
	  /* This is an hex number */
	  if ((c = nextchar()) == 0) return (0);
	  if ((isdigit(c)) || (c=='a') || (c=='b') || (c=='c') ||
	      (c=='d') || (c=='e') || (c=='f') || (c=='A') ||
	      (c=='B') || (c=='C') || (c=='D') || (c=='E') ||
	      (c=='F'))
	    state = 85;
	  else if ((c == 'l') || (c == 'L')) {
	    state = 87;
	  } else if ((c == 'u') || (c == 'U')) {
	    state = 88;
	  } else {
	    retract(1);
	    yytext[yylen] = 0;
	    yylval.id = copy_string(yytext);
	    return(NUM_INT);
	  }
	  break;

	case 86:
	  /* Rest of floating point number */

	  if ((c = nextchar()) == 0) return (0);
	  if (isdigit(c)) state = 86;
          else if ((c == 'f') || (c == 'F') || (c == 'l') || (c == 'L')) {
	    yytext[yylen] = 0;
	    yylval.id = copy_string(yytext);
	    return(NUM_FLOAT);
	  } else {
	    retract(1);
	    yytext[yylen] = 0;
	    yylval.id = copy_string(yytext);
	    return(NUM_FLOAT);
	  }
	  /* Parse a character constant. ie. 'a' */
	  break;

	case 87 :
	  /* A long integer of some sort */
	  if ((c = nextchar()) == 0) return (0);
	  if ((c == 'u') || (c == 'U')) {
	    yytext[yylen] = 0;
	    yylval.id = copy_string(yytext);
	    return(NUM_ULONG);
	  } else {
	    retract(1);
	    yytext[yylen] = 0;
	    yylval.id = copy_string(yytext);
	    return(NUM_LONG);
	  } 

	case 88:
	  /* An unsigned integer of some sort */
	  if ((c = nextchar()) == 0) return (0);
	  if ((c == 'l') || (c == 'L')) {
	    yytext[yylen] = 0;
	    yylval.id = copy_string(yytext);
	    return(NUM_ULONG);
	  } else {
	    retract(1);
	    yytext[yylen] = 0;
	    yylval.id = copy_string(yytext);
	    return(NUM_UNSIGNED);
	  } 

	case 9:
	  if ((c = nextchar()) == 0) return (0);
	  if (c == '\'') {
	    yytext[yylen-1] = 0;
	    yylval.id = copy_string(yytext+1);
	    return(CHARCONST);
	  }
	  break;

	case 100:
	  if ((c = nextchar()) == 0) return (0);
	  if (isdigit(c)) state = 81;
	  else {
	    retract(1);
	    return(PERIOD);
	  }
	  break;
	default:
	  if (!Error) {
	    fprintf(stderr,"%s : Line %d ::Illegal character '%c'=%d.\n",input_file, line_number,c,c);
	    FatalError();
	  }
	  state = 0;	
  	  Error = 1;	
	  return(ILLEGAL);
	}
    }
}

static int check_typedef = 0;

void scanner_check_typedef() {
  check_typedef = 1;
}

void scanner_ignore_typedef() {
  check_typedef = 0;
}


/**************************************************************
 * int yylex()
 *
 * Gets the lexene and returns tokens.
 *************************************************************/

extern "C" int yylex(void) {
    
    int   l;

    if (!scan_init) {
      scanner_init();
      //      if (LEX_in == NULL) LEX_in = stdin;
      //      scanner_file(LEX_in);
    }
    
    l = yylook();

    /* We got some sort of non-white space object.  We set the start_line 
       variable unless it has already been set */

    if (!start_line) {
      start_line = line_number;
    }

    /* Copy the lexene */

    yytext[yylen] = 0;

    /* Hack to support ignoring of CPP macros */

    if (l != DEFINE) {
      define_first_id = 0;
    }

    switch(l) {

      case ID:

	/* Look for keywords now */

	if (strcmp(yytext,"int") == 0) {
	  yylval.type = new DataType;
	  yylval.type->type = T_INT;
	  strcpy(yylval.type->name,yytext);
	  return(TYPE_INT);
	}
	if (strcmp(yytext,"double") == 0) {
	  yylval.type = new DataType;
	  yylval.type->type = T_DOUBLE;
	  strcpy(yylval.type->name,yytext);
	  return(TYPE_DOUBLE);
	}
	if (strcmp(yytext,"void") == 0) {
	  yylval.type = new DataType;
	  yylval.type->type = T_VOID;
	  strcpy(yylval.type->name,yytext);
	  return(TYPE_VOID);
	}
	if (strcmp(yytext,"char") == 0) {
	  yylval.type = new DataType;
	  yylval.type->type = T_CHAR;
	  strcpy(yylval.type->name,yytext);
	  return(TYPE_CHAR);
	}
	if (strcmp(yytext,"short") == 0) {
	  yylval.type = new DataType;
	  yylval.type->type = T_SHORT;
	  strcpy(yylval.type->name,yytext);
	  return(TYPE_SHORT);
	}
	if (strcmp(yytext,"long") == 0) {
	  yylval.type = new DataType;
	  yylval.type->type = T_LONG;
	  strcpy(yylval.type->name,yytext);
	  return(TYPE_LONG);
	}
	if (strcmp(yytext,"float") == 0) {
	  yylval.type = new DataType;
	  yylval.type->type = T_FLOAT;
	  strcpy(yylval.type->name,yytext);
	  return(TYPE_FLOAT);
	}
	if (strcmp(yytext,"signed") == 0) {
	  yylval.type = new DataType;
	  yylval.type->type = T_SINT;
	  strcpy(yylval.type->name,yytext);
	  return(TYPE_SIGNED);
	}
	if (strcmp(yytext,"unsigned") == 0) {
	  yylval.type = new DataType;
	  yylval.type->type = T_UINT;
	  strcpy(yylval.type->name,yytext);
	  return(TYPE_UNSIGNED);
	}
	if (strcmp(yytext,"bool") == 0) {
	  yylval.type = new DataType;
	  yylval.type->type = T_BOOL;
	  strcpy(yylval.type->name,yytext);
	  return(TYPE_BOOL);
	}
	// C++ keywords
	
	if (CPlusPlus) {
	  if (strcmp(yytext,"class") == 0) return(CLASS);
	  if (strcmp(yytext,"private") == 0) return(PRIVATE);
	  if (strcmp(yytext,"public") == 0) return(PUBLIC);
	  if (strcmp(yytext,"protected") == 0) return(PROTECTED);
	  if (strcmp(yytext,"friend") == 0) return(FRIEND);
	  if (strcmp(yytext,"virtual") == 0) return(VIRTUAL);
	  if (strcmp(yytext,"operator") == 0) return(OPERATOR);
	  if (strcmp(yytext,"throw") == 0) return(THROW);
	  if (strcmp(yytext,"inline") == 0) return(yylex());
	  if (strcmp(yytext,"template") == 0) return(TEMPLATE);
	}

	// Objective-C keywords
        if (ObjC) {
	  if (strcmp(yytext,"@interface") == 0) return (OC_INTERFACE);
	  if (strcmp(yytext,"@end") == 0) return (OC_END);
	  if (strcmp(yytext,"@public") == 0) return (OC_PUBLIC);
	  if (strcmp(yytext,"@private") == 0) return (OC_PRIVATE);
	  if (strcmp(yytext,"@protected") == 0) return (OC_PROTECTED);
	  if (strcmp(yytext,"@class") == 0) return(OC_CLASS);
	  if (strcmp(yytext,"@implementation") == 0) return(OC_IMPLEMENT);
          if (strcmp(yytext,"@protocol") == 0) return(OC_PROTOCOL);
	}

	// Misc keywords

	if (strcmp(yytext,"static") == 0) return(STATIC);
	if (strcmp(yytext,"extern") == 0) return(EXTERN);
	if (strcmp(yytext,"const") == 0) return(CONST);
	if (strcmp(yytext,"struct") == 0) return(STRUCT);
	if (strcmp(yytext,"union") == 0) return(UNION);
	if (strcmp(yytext,"enum") == 0) return(ENUM);
	if (strcmp(yytext,"sizeof") == 0) return(SIZEOF);
	if (strcmp(yytext,"defined") == 0) return(DEFINED);

	// Ignored keywords 

	if (strcmp(yytext,"volatile") == 0) return(yylex());

	// SWIG directives

	if (strcmp(yytext,"%module") == 0) return(MODULE);
	if (strcmp(yytext,"%init") == 0)  return(INIT);
	if (strcmp(yytext,"%wrapper") == 0) return(WRAPPER);
	if (strcmp(yytext,"%readonly") == 0) return(READONLY);
	if (strcmp(yytext,"%readwrite") == 0) return(READWRITE);
	if (strcmp(yytext,"%name") == 0) return(NAME);
        if (strcmp(yytext,"%rename") == 0) return(RENAME);
	if (strcmp(yytext,"%include") == 0) return(INCLUDE);
	if (strcmp(yytext,"%extern") == 0) return(WEXTERN);
        if (strcmp(yytext,"%checkout") == 0) return(CHECKOUT);
	if (strcmp(yytext,"%val") == 0) return(CVALUE);
	if (strcmp(yytext,"%out") == 0) return(COUT);

	if (strcmp(yytext,"%section") == 0) {
	  yylval.ivalue = line_number;
	  return(SECTION);
	}
	if (strcmp(yytext,"%subsection") == 0) {
	  yylval.ivalue = line_number;
	  return(SUBSECTION);
	}
	if (strcmp(yytext,"%subsubsection") == 0) {
	  yylval.ivalue = line_number;
	  return (SUBSUBSECTION);
	}
	if (strcmp(yytext,"%title") == 0) {
	  yylval.ivalue = line_number;
	  return(TITLE);
	} 
	if (strcmp(yytext,"%style") == 0) return(STYLE);
	if (strcmp(yytext,"%localstyle") == 0) return(LOCALSTYLE);
	if (strcmp(yytext,"%typedef") == 0) {
	  yylval.ivalue = 1;
	  return(TYPEDEF);
	}
	if (strcmp(yytext,"typedef") == 0) {
	  yylval.ivalue = 0;
	  return(TYPEDEF);
	}
	if (strcmp(yytext,"%alpha") == 0) return(ALPHA_MODE);
	if (strcmp(yytext,"%raw") == 0) return(RAW_MODE);
	if (strcmp(yytext,"%text") == 0) return(TEXT);
	if (strcmp(yytext,"%native") == 0) return(NATIVE);
	if (strcmp(yytext,"%disabledoc") == 0) return(DOC_DISABLE);
	if (strcmp(yytext,"%enabledoc") == 0) return(DOC_ENABLE);
	if (strcmp(yytext,"%ifdef") == 0) return(IFDEF);
	if (strcmp(yytext,"%else") == 0) return(ELSE);
	if (strcmp(yytext,"%ifndef") == 0) return(IFNDEF);
	if (strcmp(yytext,"%endif") == 0) return(ENDIF);
	if (strcmp(yytext,"%if") == 0) return(IF);
	if (strcmp(yytext,"%elif") == 0) return(ELIF);
	if (strcmp(yytext,"%pragma") == 0) return(PRAGMA);
	if (strcmp(yytext,"%addmethods") == 0) return(ADDMETHODS);
	if (strcmp(yytext,"%inline") == 0) return(INLINE);
	if (strcmp(yytext,"%typemap") == 0) return(TYPEMAP);
	if (strcmp(yytext,"%except") == 0) return(EXCEPT);
	if (strcmp(yytext,"%import") == 0) return(IMPORT);
	if (strcmp(yytext,"%echo") == 0) return(ECHO);
        if (strcmp(yytext,"%new") == 0) return(NEW);
	if (strcmp(yytext,"%apply") == 0) return(APPLY);
	if (strcmp(yytext,"%clear") == 0) return(CLEAR);
        if (strcmp(yytext,"%doconly") == 0) return(DOCONLY);
 
	// Have an unknown identifier, as a last step, we'll
	// do a typedef lookup on it.

	if (check_typedef) {
	  if (DataType::is_typedef(yytext)) {
	    yylval.type = new DataType;
	    yylval.type->type = T_USER;
	    strcpy(yylval.type->name,yytext);
	    yylval.type->typedef_resolve();
	    return(TYPE_TYPEDEF);
	  }
	}

	yylval.id = copy_string(yytext);
	return(ID);
      default:
	return(l);
    }
}

// --------------------------------------------------------------
// scanner_clear_start()
//
// Clears the start of a declaration
// --------------------------------------------------------------

void scanner_clear_start() {
  start_line = 0;
}
