/*
    Clif - A C-like Interpreter Framework
    Copyright (C) 1998, 2000 L. Koren

    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., 675 Mass Ave, Cambridge, MA 02139, USA.

*/
/* s-conv.c parsing of format strings and error checking.  */

#define BACKSLASH '\\'
#define SP ' '
#define ALERT '\a'
#define BS '\b'
#define FF '\f'
#define NL '\n'
#define CR '\r'
#define HT '\t'
#define VT '\v'

#define HASH '#'
#define DOT '.'
#define STAR '*'
#define CIRCUMFLEX '^'
#define TICK '\''

extern int yyleng;

#include "global.h"
#include "allocx.h"
#include "type.h"
#include "struct.h"
#include "comp_maint.h"
#include "s-conv.h"
#include "flags.h"

extern FILEATTR spf[];

int num_args[256];		/* Number of arguments for function
				   (extern or local). */
int args[256];			/* Counter for number of arguments for
				   function (extern or local). Used
				   when parsing a call. */

FORMAT_ARGS format_args[256][256];

static void alloc_type PROTO((struct internal_type **));
static void store_arg_type PROTO((int, int));

void
s_conv (yytext)
     char *yytext;
{
  int i, j, help, wide_string = 0;

  if (yytext[0] == 'L')
    {
      yytext++;
      wide_string = 1;
    }

  if (proc)
    num_args[proc] = args[proc] + 1;

  for (i = 1, j = 0; i < yyleng - 1; i++, j++)
    {
      if (BACKSLASH == yytext[i])
	{
	  switch (yytext[i + 1])
	    {
	    case 'a': yytext[j] = ALERT; i++;
	      break;
	    case 'b': yytext[j] = BS; i++;
	      break;
	    case 'f': yytext[j] = FF; i++;
	      break;
	    case 'n': yytext[j] = NL; i++;
	      break;
	    case 'r': yytext[j] = CR; i++;
	      break;
	    case 't': yytext[j] = HT; i++;
	      break;
	    case 'v': yytext[j] = VT; i++;
	      break;
	      /* 
	       * Control characters
	       */
	    case BACKSLASH : yytext[j] = BACKSLASH; i++;
	      break;
	    case NL : 
	      spf[s].line_counter++;
	      yytext[j] = SP; i++;
	      /* 
	       * Changes \new-line sequence
	       *  to space 
	       */
	      break;
	    case 'x' : sscanf (&yytext[i + 2], "%2x", &help); i += 3;
	      yytext[j] = help;
	      break;
	    case TICK:
	      yytext[j] = TICK;
	      i++;
	      break;
	    case '\"':
	      yytext[j] = '\"';	/* " */
	      i++;
	      break;
	    }
	}
      else if ('%' == yytext[i] && '%' != yytext[i + 1])
	{
	  num_args[proc]++;
	  if (! warning_yes && ! warning_comment)
	    {
	      yytext[j] = yytext[i];
	      continue;
	    }
	  yytext[j++] = yytext[i++];

	  if (wide_string == 1)
	    {
	      error_message (6018);
	      wide_string++;
	    }
	  
	  while (1)
	    {
	      if ('-' == yytext[i] || '+' == yytext[i] ||
		  HASH == yytext[i])
		{
		  yytext[j++] = yytext[i++];
		  continue;
		}
	      else if (SP == yytext[i])
		{
		  if (SP == yytext[j - 1])
		    error_message (6020);
		  else
		    yytext[j++] = yytext[i++];
		  continue;
		}
	      else if ('0' <= yytext[i] && '9' >= yytext[i])
		{
		  yytext[j++] = yytext[i++];
		  continue;
		}
	      else if (DOT == yytext[i])
		{
		  yytext[j++] = yytext[i++];
		  continue;
		}
	      else if (STAR == yytext[i])
		{
		  yytext[j++] = yytext[i++];
		  continue;
		}
	      else if ('h' == yytext[i])
		{
		  yytext[j++] = yytext[i++];
		  continue;
		}
	      else if ('l' == yytext[i] || 'L' == yytext[i])
		{
		  yytext[j++] = yytext[i++];
		  continue;
		}
	      else if (0 == yytext[i])
		{
		  if ('%' == yytext[j - 1])
		    error_message (6018);
		  else
		    error_message (6021);
		  break;
		}
	      else if ('d' == yytext[i] || 'i' == yytext[i])
		{
		  alloc_type (&typeh[++type_spec_count]);
		  typeh[type_spec_count]->attribute.arit_class =
		    INTEGER;
		  if ('l' == yytext[j - 1])
		    typeh[type_spec_count]->attribute.arit_class +=
		      LONG_AC;
		  else if ('L' == yytext[j - 1])
		    error_message (6015, num_args[proc]);
		  else if ('h' == yytext[j - 1])
		    typeh[type_spec_count]->attribute.arit_class +=
		      SHORT_AC;
		  store_arg_type (proc, num_args[proc]);
		  yytext[j] = yytext[i];
		  break;
		}
	      else if ('o' == yytext[i] || 'u' == yytext[i] ||
		       'x' == yytext[i] || 'X' == yytext[i])
		{
		  alloc_type (&typeh[++type_spec_count]);
		  typeh[type_spec_count]->attribute.arit_class =
		    INTEGER;
		  typeh[type_spec_count]->attribute.arit_class +=
		    UNSIGNED_AC;
		  if ('l' == yytext[j - 1])
		    typeh[type_spec_count]->attribute.arit_class +=
		      LONG_AC;
		  else if ('L' == yytext[j - 1])
		    error_message (6016, num_args[proc]);
		  else if ('h' == yytext[j - 1])
		    typeh[type_spec_count]->attribute.arit_class +=
		      SHORT_AC;
		  store_arg_type (proc, num_args[proc]);
		  yytext[j] = yytext[i];
		  break;
		}
	      else if ('f' == yytext[i])
		{
		  alloc_type (&typeh[++type_spec_count]);
		  typeh[type_spec_count]->attribute.arit_class = FLT;
		  if (('l' == yytext[j - 1] || 'h' == yytext[j - 1])
		      && PRINTF_P)
		    error_message (6014, yytext[j - 1], yytext[i]);
		  else if ('l' == yytext[j - 1] 
		      && SCANF_P)
		    typeh[type_spec_count]->attribute.arit_class =
		      DOUB;
		  else if ('L' == yytext[j - 1])
		    typeh[type_spec_count]->attribute.arit_class +=
		      LONG_AC;
		  store_arg_type (proc, num_args[proc]);
		  yytext[j] = yytext[i];
		  break;
		}
	      else if ('e' == yytext[i] || 'E' == yytext[i] ||
		       'g' == yytext[i] || 'G' == yytext[i])
		{
		  alloc_type (&typeh[++type_spec_count]);
		  typeh[type_spec_count]->attribute.arit_class = DOUB;
		  if ('l' == yytext[j - 1] || 'h' == yytext[j - 1])
		    error_message (6014, yytext[j - 1], yytext[i]);
		  else if ('L' == yytext[j - 1])
		    typeh[type_spec_count]->attribute.arit_class +=
		      LONG_AC;
		  store_arg_type (proc, num_args[proc]);
		  yytext[j] = yytext[i];
		  break;
		}
	      else if ('c' == yytext[i])
		{
		  alloc_type (&typeh[++type_spec_count]);
		  typeh[type_spec_count]->attribute.arit_class =
		    CHR;
		  typeh[type_spec_count]->attribute.arit_class +=
		    UNSIGNED_AC;
		  if ('l' == yytext[j - 1] || 'L' == yytext[j - 1])
		    error_message (6014, yytext[j - 1], yytext[i]);
		  else if ('h' == yytext[j - 1])
		    typeh[type_spec_count]->attribute.arit_class +=
		      SHORT_AC;
		  store_arg_type (proc, num_args[proc]);
		  yytext[j] = yytext[i];
		  break;
		}
	      else if ('s' == yytext[i])
		{
		  struct internal_type *array;
		  alloc_type (&array);
		  array->attribute.arit_class = CHR;
		  array->attribute.arit_class += UNSIGNED_AC;
		  if ('l' == yytext[j - 1])
		    {
		      struct internal_type *error_type;
		      alloc_type (&error_type);
		      error_type->attribute.arit_class = INTEGER;
		      error_message (6017, error_type,
				     num_args[proc]);
		      }
		  else if ('L' == yytext[j - 1])
		    error_message (6014, yytext[j - 1], yytext[i]);
		  else if ('h' == yytext[j - 1])
		    array->attribute.arit_class += SHORT_AC;
		  alloc_type (&typeh[++type_spec_count]);
		  typeh[type_spec_count]->attribute.function_class =
		    ARRAY;
		  typeh[type_spec_count]->output = array;
		  store_arg_type (proc, num_args[proc]);
		  yytext[j] = yytext[i];
		  break;
		}
	      else if ('p' == yytext[i])
		{
		  struct internal_type *pointer;
		  alloc_type (&pointer);
		  pointer->attribute.arit_class = VID;
		  if ('l' == yytext[j - 1] || 'L' == yytext[j - 1] ||
		      'h' == yytext[j - 1])
		    error_message (6014, yytext[j - 1], yytext[i]);
		  alloc_type (&typeh[++type_spec_count]);
		  typeh[type_spec_count]->attribute.function_class =
		    POINTER;
		  typeh[type_spec_count]->output = pointer;
		  store_arg_type (proc, num_args[proc]);
		  yytext[j] = yytext[i];
		  break;
		}
	      else if ('n' == yytext[i])
		{
		  struct internal_type *pointer;
		  alloc_type (&pointer);
		  pointer->attribute.arit_class = INTEGER;
		  if ('l' == yytext[j - 1])
		    pointer->attribute.arit_class += LONG_AC;
		  else if ('L' == yytext[j - 1])
		    error_message (6014, yytext[j - 1], yytext[i]);
		  else if ('h' == yytext[j - 1])
		    pointer->attribute.arit_class += SHORT_AC;
		  alloc_type (&typeh[++type_spec_count]);
		  typeh[type_spec_count]->attribute.function_class =
		    POINTER;
		  typeh[type_spec_count]->output = pointer;
		  store_arg_type (proc, num_args[proc]);
		  yytext[j] = yytext[i];
		  break;
		}
	      else if ('[' == yytext[i] && SCANF_P)
		{
		  struct internal_type *array;
		  alloc_type (&array);
		  array->attribute.arit_class = CHR;
		  array->attribute.arit_class += UNSIGNED_AC;
		  yytext[j++] = yytext[i++];
		  if (']' == yytext[i])
		    yytext[j++] = yytext[i++];
		  else if (CIRCUMFLEX == yytext[i]
			   && ']' == yytext[i + 1])
		    {
		      yytext[j++] = yytext[i++];
		      yytext[j++] = yytext[i++];
		    }
		  while (']' != yytext[i] && '\0' != yytext[i])
		    yytext[j++] = yytext[i++];
		  alloc_type (&typeh[++type_spec_count]);
		  typeh[type_spec_count]->attribute.function_class =
		    ARRAY;
		  typeh[type_spec_count]->output = array;
		  store_arg_type (proc, num_args[proc]);
		  yytext[j] = yytext[i];
		  break;
		}
	      else
		{
		  error_message (6019);
		  break;
		}
	    }
	}
      else if ('%' == yytext[i] && '%' == yytext[i + 1])
	{
	  yytext[j++] = yytext[i++];
	  yytext[j] = yytext[i];
	}
      else if ('?' == yytext[i] && '?' == yytext[i + 1])
	{
	  error_message (6032);
	  /* Brain-damaged trihraphs handling. */
	  switch (yytext[i + 2])
	    {
	    case '=':
	      yytext[j] = HASH;
	      i += 2;
	      break;
	    case '(':
	      yytext[j] = '[';
	      i += 2;
	      break;
	    case '/':
	      yytext[j] = BACKSLASH;
	      i += 2;
	      if ('\0' != yytext[i + 1])
		{
		  register int k;
		  /* We have to re-process the escape
		     characters. First, copy string to the new
		     location. (We have replaced the trigraph '??/'
		     already.) Then, all control variables (i, j,
		     yyleng) must be reset. */
		  yytext[j + 1] = yytext[++i];
		  for (k = j + 2, i++; '\0' != yytext[i]; k++, i++)
		    yytext[k] = yytext[i];
		  /* Should be null all the time. */
		  yytext[k] = yytext[i];
		  /* We have to change the yyleng, because the string
                     shrunk. */
		  yyleng = k;
		  j--;
		  i = j;
		}
	      break;
	    case ')':
	      yytext[j] = ']';
	      i += 2;
	      break;
	    case TICK:
	      yytext[j] = CIRCUMFLEX;
	      i += 2;
	      break;
	    case '<':
	      yytext[j] = '{';
	      i += 2;
	      break;
	    case '!':
	      yytext[j] = '|';
	      i += 2;
	      break;
	    case '>':
	      yytext[j] = '}';
	      i += 2;
	      break;
	    case '-':
	      yytext[j] = '~';
	      i += 2;
	      break;
	    default:
	      yytext[j] = yytext[i];
	    }
	}
      else if ('"' == yytext[i])
	{
	  char *buf = NULL;

	  while ('"' != yytext[++i])
	    if ('L' == yytext[i] && '"' == yytext[i + 1])
	      wide_string++;
	    else if (' ' == yytext[i] || '\t' == yytext[i] ||
		     '\v' == yytext[i] || '\b' == yytext[i] ||
		     '\f' == yytext[i])
	      ;
	    else if ('\n' == yytext[i] || '\r' == yytext[i])
	      {
		extern int s;
		extern FILEATTR spf[];
		spf[s].line_counter++;
	      }
	    else if ('\0' == yytext[i])
	      break;
	    else
	      {
		buf = yytext + i;
		break;
	      }
	  
	  if (buf)
	    {
	      while (' ' != yytext[i] && '\t' != yytext[i] &&
		     '\n' != yytext[i] && '\r' != yytext[i] &&
		     '\f' != yytext[i] && '\v' != yytext[i] &&
		     '\0' != yytext[i] && '\"' != yytext[i])
		i++;
	      yytext[i] = '\0';
	      error_message (1004, buf);
	    }
	  j--;
	}
      else
	yytext[j] = yytext[i];
    }
	  
  if (proc && wide_string)
    error_message (6023);
  yytext[j] = '\0';
}


static void
alloc_type (type)
  struct internal_type **type;
{
  *type = (struct internal_type *) 
    allocate (sizeof(struct internal_type), 
	      scope_level > PERM ? BLOCK : PERM);
  init_zero ((char *)*type, sizeof(struct internal_type));
  (*type)->attribute.function_class = SIMPLE;
  (*type)->attribute.type_qualifier = UNDEF_TQ;
}


/* Types of arguments are stored in the array for later checking. */
static void
store_arg_type (proc, num)
  int proc, num;
{
  format_args[proc][num] = typeh[type_spec_count];
  typeh[type_spec_count--] = NULL;
}


/* Checks if the argument is compatible with the specified format. */
void
compare_format_args (type1, type2, type)
  struct internal_type *type1, *type2;
  enum intern_arit_class type;
{
  if (NULL == type1 && NULL == type2)
    return;
  else if (NULL == type1 && NULL != type2)
    {
      if (! ARRAY_P(type2) || ! CHAR_P(type2->output))
	error_message (6022);
      return;
    }
  else if (NULL != type1 && NULL == type2)
    {
      error_message (6021);
      return;
    }
  
  if (call_by_value && SCANF_P && POINTER_P(type2))
    {
      type2 = type2->output;
      type = type2->attribute.arit_class;
    }
  
  if ((POINTER_P(type1) && POINTER_P(type2)) ||
      (POINTER_P(type1) && ARRAY_P(type2)) ||
      (ARRAY_P(type1) && POINTER_P(type2)) ||
      (ARRAY_P(type1) && ARRAY_P(type2)))
    {
      if (! VOID_P(type1->output) &&
	  ! TYPES_EQ_P(type1->output, type2->output) &&
	  (! (CHAR_P(type1->output) && INTEGER_P(type2->output)) ||
	   (INTEGER_P(type1->output) && CHAR_P(type2->output))))
	error_message (6017, type1, args[proc]);
    }
  else if (DOUBLE_P(type1) && (type & FLT))
    {
      gen_cast_needed ();
    }
  else if (! (type1->attribute.arit_class & type) &&
	   ! (INTEGER_P(type1) && (type & CHR)))
    error_message (6017, type1, args[proc]);
  else if (! (ARRAY_P(type1) || POINTER_P(type1)) &&
	   (ARRAY_P(type2) || POINTER_P(type2)))
    error_message (6031, type1, args[proc]);
}
