/* Memory access checker.
   Copyright 1993 Tristan Gingold
		  Written October 1993 by Tristan Gingold

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

This library 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
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; see the file COPYING.LIB.  If
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
Cambridge, MA 02139, USA.

   The author may be reached (Email) at the address marc@david.saclay.cea.fr,
   or (US/French mail) as Tristan Gingold 
   			  8 rue Parmentier
   			  F91120 PALAISEAU
   			  FRANCE
*/

#include <fcntl.h>
#include "checker.h"
#include "errlist.h"
#include "message.h"
#include "dmstrings.h"

static int silent;
unsigned int null_pointer_zone = 0x04;
#ifdef CHKR_SAVESTACK
int nosymtab;
#endif
#ifdef CHKR_PROFILE
int profile = 0;
#endif

extern int chkr_is_init;

/* This is for parse_args() */
#define isspace(c) ((c)==' ' || (c)=='\t')
#define INITIAL_MAXARGC 16
#define EOS '\0'

void init_morecore(void);
static void parse_args(char *input);
static void parse_opt(char *option);
static void print_message(char **message);
static int atod(const char *c);
static int ctod(const char c);

static char* init_message[]=
{
  M_COPY_M1,
  M_COPY_M2,
  M_COPY_M3,
  M_COPY_M4,
  M_COPY_M5,
  M_COPY_M6,
  M_COPY_M7,
  "\n",
  NULL
};

static char* help_message[]=
{ M_HELP_M1,
  M_HELP_M2,
  M_HELP_M3,
  M_HELP_M4,
  M_HELP_M5
  M_HELP_M6,
#ifdef CHKR_SAVESTACK  
  M_HELP_M7,
#endif
  M_HELP_M8,
  M_HELP_M9,
#ifdef CHKR_PROFILE
  M_HELP_M10,
#endif    
  "\n",
  NULL
};
  
static char* abort_message[]=
{
  M_ABORT_M1,
  NULL
};
   
/* __chkr_init_chkr is called before main() */
void __chkr_init_chkr(int argc, char *argv[], char *argp[])
{
 char *args;
 
 if (argc == 0)		/* when called by ldd */
   {
     print_message(init_message);
     _exit(0);
   }
 
 chkr_prog_path = (char*)0; 
 
 args = getenv("CHECKEROPTS");
 if (args != (char*)0)
   parse_args(args);
  
 if (!silent)
   print_message(init_message);
    
 chkr_errno = E_UNKNOWN;
 /* some trivial checks. ??_red_zone must be a multiple of 4 */
 if ( be_red_zone % sizeof(void*) || af_red_zone % sizeof(void*))
   {
     chkr_errno = E_BADRDSIZE;
     chkr_perror();
   }
#ifdef CHKR_USE_BITMAP 
 init_morecore();
#endif 
 if (chkr_prog_path == (char*)0)
   chkr_prog_path = chkr_find_executable(argv[0]);
 if (chkr_prog_path == (char*)0)
   chkr_header(M_PRGNAME_NOT_FOUND);
 chkr_is_init = 1;
}

/* parse_args is a modified version of buildargv. TG made the changes.
   buildargv comes from liberty(argv.c). Here is the copyright: */
/* Create and destroy argument vectors (argv's)
   Copyright (C) 1992 Free Software Foundation, Inc.
   Written by Fred Fish @ Cygnus Support

This file is part of the libiberty library.
Libiberty is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.

Libiberty 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
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with libiberty; see the file COPYING.LIB.  If
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
Cambridge, MA 02139, USA.  */   
/*

NAME

	buildargv -- build an argument vector from a string

SYNOPSIS

	char **buildargv (sp)
	char *sp;

DESCRIPTION

	Given a pointer to a string, parse the string extracting fields
	separated by whitespace and optionally enclosed within either single
	or double quotes (which are stripped off), and build a vector of
	pointers to copies of the string for each field.  The input string
	remains unchanged.

	All of the memory for the pointer array and copies of the string
	is obtained from malloc.  All of the memory can be returned to the
	system with the single function call freeargv, which takes the
	returned result of buildargv, as it's argument.

	The memory for the argv array is dynamically expanded as necessary.

RETURNS

	Returns a pointer to the argument vector if successful. Returns NULL
	if the input string pointer is NULL or if there is insufficient
	memory to complete building the argument vector.

NOTES

	In order to provide a working buffer for extracting arguments into,
	with appropriate stripping of quotes and translation of backslash
	sequences, we allocate a working buffer at least as long as the input
	string.  This ensures that we always have enough space in which to
	work, since the extracted arg is never larger than the input string.

	If the input is a null string (as opposed to a NULL pointer), then
	buildarg returns an argv that has one arg, a null string.

	Argv is always kept terminated with a NULL arg pointer, so it can
	be passed to freeargv at any time, or returned, as appropriate.
*/

static void
parse_args(char *input)
{
  char *arg;
  char *copybuf;
  int squote = 0;
  int dquote = 0;
  int bsquote = 0;

  copybuf = alloca (strlen (input) + 1);
  /* Is a do{}while to always execute the loop once.  Always return an
     argv, even for null strings.  See NOTES above, test case below. */
  do
  {
    /* Pick off argv[argc] */
    while (isspace(*input))
      input++;
    /* Begin scanning arg */
    arg = copybuf;
    while (*input != EOS)
    {
      if (isspace (*input) && !squote && !dquote && !bsquote)
	{
	  break;
	}
      else
	{
	  if (bsquote)
	    {
	      bsquote = 0;
	      *arg++ = *input;
	    }
	  else if (*input == '\\')
	    {
	      bsquote = 1;
	    }
	  else if (squote)
	    {
	      if (*input == '\'')
		{
		  squote = 0;
		}
	      else
		{
		  *arg++ = *input;
		}
	    }
	  else if (dquote)
	    {
	      if (*input == '"')
		{
		  dquote = 0;
		}
	      else
		{
		  *arg++ = *input;
		}
	    }
	  else
	    {
	      if (*input == '\'')
		{
		  squote = 1;
		}
	      else if (*input == '"')
		{
		  dquote = 1;
		}
	      else
		{
		  *arg++ = *input;
		}
	    }
	  input++;
	}
    }
    *arg = EOS;
    parse_opt(copybuf);
  }
  while (*input != EOS);
}

/* The options are:
   --silent -s: no message at the begin
   --quiet -q: idem
   --output=file -o=file : set the output file
   --image=file -i=file : set the image file
   --nosymtab -n : do not use symbole table
   --abort -a : abort
   --zonenull -z : size of the 'null' zone (address between 0 and this limit 
   --profile -p: display profile infos 
   --NULLzone=xxx -N=xxx: set the size of thz NULL zone
 */
static void
parse_opt(char *option)
{
 if(*option == '-' && option[1]!='\0')
   {
     if(option[1] == 's' || strcmp(option,"--silent") == 0
        || option[1] == 'q' || strcmp(option, "--quiet") == 0)
       {
         silent = 1;
         return;
       }
     if(option[1] == 'h' || strcmp(option,"--help") == 0)
       {
         print_message(init_message);
         print_message(help_message);
         silent |= 2;
         return;
       }
     if(option[1] == 'a' || strcmp(option,"--abort") == 0)
       {
         print_message(abort_message);
         chkr_abort();
       }
#ifdef CHKR_SAVESTACK   
     if(option[1] == 'n' || strcmp(option,"--nosymtab") == 0)
       {
         nosymtab = 1;
         return;
       }
#endif   
     if(option[1] == 'o' && option[2] == '=')
       {
         chkr_out = open(&option[3], O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
         if (chkr_out == 0)
           chkr_out = 2;
         return;
       }
     if(strncmp(option,"--output=", 9) == 0)
       {
         chkr_out = open(&option[9], O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
         if (chkr_out == 0)
           chkr_out = 2;
         return;
       }
#ifdef CHKR_PROFILE
     if (option[1] == 'p' || strcmp(option,"--profile") == 0)
       {
         profile = 1;
         return;
       }
#endif   
     if (option[1] == 'N' && option[2] == '=')
       {
         null_pointer_zone = atod(&option[3]);
         return;
       }
     if (strncmp(option,"--NULLzone=",11) == 0)
       {
         null_pointer_zone = atod(&option[11]);
         return;
       }
#if 1 /* BUG */   
     if (option[1] == 'i' && option[2] == '=')
       {
         chkr_prog_path = copy_of_exec_file(&option[3]);
         return;
       }
     if (strncmp(option, "--image=", 8) == 0)
       {
         chkr_prog_path = copy_of_exec_file(&option[8]);
         return;
       }
#endif   
     chkr_printf(M_UNKNOWN_OP, option);
   }
}

static void print_message(char **message)
{
 while(*message != (char*)0)
   {
      write(chkr_out, *message, strlen(*message));
      message++;
   }
}

/* convert a string to an int.
   Format are:
   0nnnnn...	base 8,
   0xnnnn...	base 16,
   0bnnnn...	base 2,
   nnnnnn...	base 10
 */
static int atod(const char *c)
{
  int val=0;
  int base=10;
  int digit;
  
  if(*c == '0')
    {
      if(c[1] == 'x' || c[1] == 'X')
        {
          base = 16;
          c++;
        }
      else if(c[1] == 'b' || c[1] == 'B')
        {
          base = 2;
          c++;
        }
      else
        base = 8;
      c++;
    }
  while(*c != '\0')
    {
       digit = ctod(*c);
       c++;
       if (digit == -1 || digit >= base)
         break;
       val *= base;
       val += digit;
    }
  return val;
}
  
/* convert a char to an int */
static int
ctod(const char c)
{
  if (c >= '0' && c <= '9')
    return c - '0';
  if (c >= 'a' && c <= 'f')
    return c - 'a' + 10;
  if (c >= 'A' && c <= 'F')
    return c - 'A' + 10;
  return -1;
}
  
void chkr_do_end()
{
 chkr_remove_symtabfile();
#ifdef CHKR_PROFILE
 if (profile)
   display_profile();
#endif  
}
