/* Utility functions for scan-decls and fix-header programs.
   Copyright (C) 1993, 1994, 1998, 2002, 2003, 2007 Free Software Foundation, Inc.

   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 3, 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; see the file COPYING3.  If not see
   <http://www.gnu.org/licenses/>.  */

#include "bconfig.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "scan.h"

int lineno = 1;
int source_lineno = 1;
sstring source_filename;

void
make_sstring_space (sstring *str, int count)
{
  int cur_pos = str->ptr - str->base;
  int cur_size = str->limit - str->base;
  int new_size = cur_pos + count + 100;

  if (new_size <= cur_size)
    return;

  str->base = xrealloc (str->base, new_size);
  str->ptr = str->base + cur_size;
  str->limit = str->base + new_size;
}

void
sstring_append (sstring *dst, sstring *src)
{
  char *d, *s;
  int count = SSTRING_LENGTH (src);

  MAKE_SSTRING_SPACE (dst, count + 1);
  d = dst->ptr;
  s = src->base;
  while (--count >= 0) *d++ = *s++;
  dst->ptr = d;
  *d = 0;
}

int
scan_ident (FILE *fp, sstring *s, int c)
{
  s->ptr = s->base;
  if (ISIDST (c))
    {
      for (;;)
	{
	  SSTRING_PUT (s, c);
	  c = getc (fp);
	  if (c == EOF || ! ISIDNUM (c))
	    break;
	}
    }
  MAKE_SSTRING_SPACE (s, 1);
  *s->ptr = 0;
  return c;
}

int
scan_string (FILE *fp, sstring *s, int init)
{
  int c;

  for (;;)
    {
      c = getc (fp);
      if (c == EOF || c == '\n')
	break;
      if (c == init)
	{
	  c = getc (fp);
	  break;
	}
      if (c == '\\')
	{
	  c = getc (fp);
	  if (c == EOF)
	    break;
	  if (c == '\n')
	    continue;
	}
      SSTRING_PUT (s, c);
    }
  MAKE_SSTRING_SPACE (s, 1);
  *s->ptr = 0;
  return c;
}

/* Skip horizontal white spaces (spaces, tabs, and C-style comments).  */

int
skip_spaces (FILE *fp, int c)
{
  for (;;)
    {
      if (c == ' ' || c == '\t')
	c = getc (fp);
      else if (c == '/')
	{
	  c = getc (fp);
	  if (c != '*')
	    {
	      ungetc (c, fp);
	      return '/';
	    }
	  c = getc (fp);
	  for (;;)
	    {
	      if (c == EOF)
		return EOF;
	      else if (c != '*')
		{
		  if (c == '\n')
		    source_lineno++, lineno++;
		  c = getc (fp);
		}
	      else if ((c = getc (fp)) == '/')
		return getc (fp);
	    }
	}
      else
	break;
    }
  return c;
}

int
read_upto (FILE *fp, sstring *str, int delim)
{
  int ch;

  for (;;)
    {
      ch = getc (fp);
      if (ch == EOF || ch == delim)
	break;
      SSTRING_PUT (str, ch);
    }
  MAKE_SSTRING_SPACE (str, 1);
  *str->ptr = 0;
  return ch;
}

int
get_token (FILE *fp, sstring *s)
{
  int c;

  s->ptr = s->base;
 retry:
  c = ' ';
  c = skip_spaces (fp, c);
  if (c == '\n')
    {
      source_lineno++;
      lineno++;
      goto retry;
    }
  if (c == '#')
    {
      c = get_token (fp, s);
      if (c == INT_TOKEN)
	{
	  source_lineno = atoi (s->base) - 1; /* '\n' will add 1 */
	  get_token (fp, &source_filename);
	}
      for (;;)
	{
	  c = getc (fp);
	  if (c == EOF)
	    return EOF;
	  if (c == '\n')
	    {
	    source_lineno++;
	    lineno++;
	    goto retry;
	    }
	}
    }
  if (c == EOF)
    return EOF;
  if (ISDIGIT (c))
    {
      do
	{
	  SSTRING_PUT (s, c);
	  c = getc (fp);
	} while (c != EOF && ISDIGIT (c));
      ungetc (c, fp);
      c = INT_TOKEN;
      goto done;
    }
  if (ISIDST (c))
    {
      c = scan_ident (fp, s, c);
      ungetc (c, fp);
      return IDENTIFIER_TOKEN;
    }
  if (c == '\'' || c == '"')
    {
      c = scan_string (fp, s, c);
      ungetc (c, fp);
      return c == '\'' ? CHAR_TOKEN : STRING_TOKEN;
    }
  SSTRING_PUT (s, c);
 done:
  MAKE_SSTRING_SPACE (s, 1);
  *s->ptr = 0;
  return c;
}

unsigned int
hashstr (const char *str, unsigned int len)
{
  unsigned int n = len;
  unsigned int r = 0;
  const unsigned char *s = (const unsigned char *) str;

  do
    r = r * 67 + (*s++ - 113);
  while (--n);
  return r + len;
}
