/*--------------------------------------------------------------------------*/
/* ALBERTA_UTIL:  tools for messages, memory allocation, parameters, etc.   */
/*                                                                          */
/* file:     parameters.c                                                   */
/*                                                                          */
/* description:  utilities for handling parameters                          */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  authors:   Alfred Schmidt                                               */
/*             Zentrum fuer Technomathematik                                */
/*             Fachbereich 3 Mathematik/Informatik                          */
/*             Universitaet Bremen                                          */
/*             Bibliothekstr. 2                                             */
/*             D-28359 Bremen, Germany                                      */
/*                                                                          */
/*             Kunibert G. Siebert                                          */
/*             Institut fuer Mathematik                                     */
/*             Universitaet Augsburg                                        */
/*             Universitaetsstr. 14                                         */
/*             D-86159 Augsburg, Germany                                    */
/*                                                                          */
/*  http://www.mathematik.uni-freiburg.de/IAM/ALBERTA                       */
/*                                                                          */
/*  (c) by A. Schmidt and K.G. Siebert (1996-2003)                          */
/*                                                                          */
/*--------------------------------------------------------------------------*/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdarg.h>
#include <string.h>
#include <strings.h>

#include "alberta_util.h"

/*--------------------------------------------------------------------------*/
/*  utilities for parameters and parameter initialization                   */
/*--------------------------------------------------------------------------*/

#define COMMENT   '%'
#define N_KEY     100
#define N_CHAR    1000
#define LENGTH    256

static void read_parameters(const char *filename);

struct param
{
  char       *key;
  char       *parameters;
  const char *filename;
  const char *funcname;
  int        line_no;
};

static int cpp_read = 0;

#define IS_BLANK_CHAR(s)   (s==' '||s=='\t'||s =='\f'||s=='\r')


static char *get_key(char *s, int n_line, const char *filename)
{
  FUNCNAME("get_key");
  static  char key[LENGTH];
  char    fn[LENGTH], c;
  int     i;

  if (cpp_read)
  {
    if (*s == '#')  /*  Praeprozessor line   */
      return(s);
  }

  while (*s  &&  IS_BLANK_CHAR(*s)) s++;
  
  if (*s == COMMENT  ||  *s == '\0'  ||  *s == '\n')
    return(NULL);

  if (*s == '#')
  {
    if (strstr(s, "#include") == s)
/*--------------------------------------------------------------------------*/
/*  another init file has to be included                                    */
/*--------------------------------------------------------------------------*/
    {
      s += strlen("#include");
      while (*s  &&  IS_BLANK_CHAR(*s))
	s++;	

      i = 0;
      switch (c = *s++)
      {
      case '<':
	c = '>';
      case '\"':
	while(*s  &&  *s != c  &&  !IS_BLANK_CHAR(*s))
	{
	  if (i == LENGTH-1)
	  {
	    ERROR("filename more than %d characters.\n", i);
	    ERROR("Skipping line %d of file %s\n", n_line, filename);
	    return(NULL);
	  }
	  fn[i++] = *s++;
	}
	if (*s != c)
	{
	  ERROR("filename of include not terminated by %c\n", c);
	  ERROR("skipping line %d of file %s\n", n_line, filename);
	  return(NULL);
	}
	fn[i] = '\0';
	break;
      default:
	ERROR("no filename of include file found\n");
	ERROR("skipping line %d of file %s\n", n_line, filename);
	return(NULL);
      }

      read_parameters(fn);
      return(NULL);
    }
    else
    {
      ERROR("# must not be the first character on a line; except #include\n");
      return(NULL);
    }
  }

/*--------------------------------------------------------------------------*/
/*  now get the key                                                         */
/*--------------------------------------------------------------------------*/
  i = 0;
  while (*s  &&  *s != ':')
  {
    if (*s == COMMENT)
    {
      ERROR("key must not contain '%c'.\n", COMMENT);
      ERROR("Skipping line %d of file %s\n", n_line, filename);
      return(NULL);
    }

    if (i == LENGTH-1)
    {
      ERROR("key more than %d characters.\n", i);
      ERROR("Skipping line %d of file %s\n", n_line, filename);
      return(NULL);
    }
    key[i++] = *s++;
  }
  key[i] = '\0';

  if (*s != ':')
  {
    ERROR("key was not terminated by ':'.\n");
    ERROR("Skipping line %d of file %s\n", n_line, filename);
    return(NULL);
  }

  if (i == 0)
  {
    ERROR("use of ':' without key.\n");
    ERROR("Skipping line %d of file %s\n", n_line, filename);
    return(NULL);
  }
  return(key);
}

static char *get_par(char *key, char *s, int *nl, const char *fn, FILE *fp)
{
  FUNCNAME("get_par");
  static char    parameter[2*LENGTH];
  int     i, ol = *nl;

  while (*s  &&  *s++ != ':');
  while (*s  &&  IS_BLANK_CHAR(*s))  s++;
  
  i = 0;
  while (*s  &&  *s != COMMENT  && *s != '\n')
  {
    if (*s == '\\'  &&  *(s+1) == '\n')
    {
      (*nl)++;
      if (fgets(s, LENGTH, fp) == NULL)
      {
	ERROR("EOF reached while reading parameters of key %s\n", key);
	if (ol == *nl-1) ERROR("Skipping line %d of file %s\n", *nl, fn);
	else     ERROR("Skipping lines %d-%d of file %s\n", ol, *nl, fn);
	
	return(NULL);
      }
    }
    else
    {
      if (i == 2*LENGTH-1)
      {
	ERROR("parameter has more than %d characters.\n", i);
	if (ol == *nl)  ERROR("Skipping line %d of file %s\n", *nl, fn);
	else    ERROR("Skipping lines %d-%d of file %s\n", ol, *nl, fn);

	return(NULL);
      }
      if (IS_BLANK_CHAR(*s))
      {
	parameter[i++] = ' ';
	s++;
	while (*s  &&  IS_BLANK_CHAR(*s)) s++;
      }
      else
      {
	parameter[i++] = *s++;
      }
    }
  }

  do
  {
    parameter[i--] = '\0';
  } while (i  &&  IS_BLANK_CHAR(parameter[i]));

  if (i == 0  &&  IS_BLANK_CHAR(parameter[0]))
  {
    ERROR("no parameter of key %s.\n", key);
    if (ol == *nl) ERROR("Skipping line %d of file %s\n", *nl, fn);
    else   ERROR("Skipping lines %d-%d of file %s\n", ol, *nl, fn);

    return(NULL);
  }

  return(parameter);
}


static int bin_search(struct param *param, const char *key, int n_keys)
{
  int      cond, left, right, mid;

  left = 0;
  right = n_keys-1;
  
  while (left <= right)
  {
    mid = (left+right)/2;
    if ((cond = strcmp(param[mid].key, key)) < 0)
    {
      left = mid + 1;
    }
    else if (cond > 0)
    {
      right = mid - 1;
    }
    else
      return(mid);
  }

  return(-1);
}

static struct param  *all_param = NULL;
static int  n_param = 0, max_param = 0;

static char     *buffer = NULL;
static unsigned buffer_size = 0, buffer_used = 0;
static unsigned total_buffer_size = 0;

static int  param_info = 1;
int         msg_info = 10;
bool        msg_wait = true;


static const char *get_actfile(const char *filename)
{
#if !(ALBERTA_DEBUG && (ALBERTA_EFENCE || ALBERTA_ALLOC_RECORD))
  FUNCNAME("get_actfilename");
#endif
  static char **filenames = NULL;
  static int  n_files = 0, max_files = 0;
  int         i, size_f;
  char        *actfile;


  for (i = 0; i < n_files; i++)
    if (!strcmp(filename, filenames[i])) break;
  
  if (i < n_files)
  {
    return((const char *) filenames[i]);
  }
  else
  {
    if (n_files == max_files)
    {
      filenames = MEM_REALLOC(filenames, max_files, max_files+10, char *);
      max_files += 10;
    }
    size_f = strlen(filename)+1;
    if (buffer_used + size_f >= buffer_size)
    {
      buffer_used = 0;
      buffer_size = N_CHAR;
      buffer = MEM_ALLOC(N_CHAR, char);
    }
    actfile = buffer+buffer_used;
    buffer_used += size_f;
    strncpy(actfile, filename, size_f);
    return((const char *) actfile);
  }
}

static void add_param(const char *key, const char *parameter, 
		      const char *actfile, int  n_line, const char *fname)
{
#if !(ALBERTA_DEBUG && (ALBERTA_EFENCE || ALBERTA_ALLOC_RECORD))
  FUNCNAME("add_param");
#endif
  unsigned size_k, size_p;
  int      i, j, scmp = 0;

  size_k = strlen(key)+1;
  size_p = strlen(parameter)+1;

  for (i = 0; i < n_param; i++)
  {
    if ((scmp = strcmp(all_param[i].key, key)) >= 0)  break;
  }


  if (i < n_param  &&  scmp == 0)
  {
/*--------------------------------------------------------------------------*/
/*  key does already exist: save new parameters                             */
/*--------------------------------------------------------------------------*/
    if (strlen(all_param[i].parameters)+1 >= size_p)
      strcpy(all_param[i].parameters, parameter);
    else
    {
      if (buffer_used + size_p >= buffer_size)
      {
	buffer_used = 0;
	buffer_size = N_CHAR;
	buffer = MEM_ALLOC(N_CHAR, char);
      }

      all_param[i].parameters = buffer+buffer_used;
      buffer_used += size_p;
      total_buffer_size += size_p;
      strcpy(all_param[i].parameters, parameter);
    }
    all_param[i].filename = actfile;
    all_param[i].funcname = fname;
    all_param[i].line_no = n_line;

    return;
  }

  if (n_param == max_param)
  {
    all_param = MEM_REALLOC(all_param, max_param, max_param + N_KEY, 
			    struct param);
    max_param += N_KEY;
  }

  for (j = n_param; j > i; j--)
    all_param[j] = all_param[j-1];

  if (buffer_used + size_k >= buffer_size)
  {
    if (buffer_used + size_p < buffer_size)
    {
      all_param[i].parameters = buffer+buffer_used;
      buffer_used += size_p;
      total_buffer_size += size_p;
      size_p = 0;
      strcpy(all_param[i].parameters, parameter);
    }
    buffer_used = 0;
    buffer_size = N_CHAR;
    buffer = MEM_ALLOC(N_CHAR, char);
  }

  all_param[i].key = buffer+buffer_used;
  buffer_used += size_k;
  total_buffer_size += size_k;
  strcpy(all_param[i].key, key);

  if (size_p)
  {
    if (buffer_used + size_p >= buffer_size)
    {
      buffer_used = 0;
      buffer_size = N_CHAR;
      buffer = MEM_ALLOC(N_CHAR, char);
    }
    
    all_param[i].parameters = buffer+buffer_used;
    buffer_used += size_p;
    total_buffer_size += size_p;
    strcpy(all_param[i].parameters, parameter);
  }
  all_param[i].filename = actfile;
  all_param[i].funcname = fname;
  all_param[i].line_no = n_line;

  n_param++;

  return;
}

static void read_parameters(const char *filename)
{
  FUNCNAME("read_parameters");
  FILE        *init_file;
  char        line[LENGTH];
  int         n_line = 0;
  char        *key, *parameter;
  const char  *actfile = NULL;

  if (!(init_file = fopen(filename, "r")))  return;

  if (!cpp_read)
  {
    INFO(param_info, 2, "reading from file %s\n", filename);
    actfile = get_actfile(filename);
  }

  while (fgets(line, LENGTH, init_file))
  {
    n_line++;
    if (strlen(line) == LENGTH-1)
    {
      ERROR("line %d of file %s too long; skipping it\n", n_line, filename);
      continue;
    }

    key = get_key(line, n_line, filename);
    
    if (key == NULL)
      continue;
    
    if (cpp_read  &&  *key == '#')
    {
      char  *s;

      sscanf(key, "#%d", &n_line);
      n_line--;
      while (*key  &&  *key++ != '\"');
      s = key;
      while (*s  &&  *s != '\"') s++;
      *s = '\0';
      actfile = get_actfile(key);

      continue;
    }

    parameter = get_par(key, line, &n_line, filename, init_file);
    if (!parameter)  continue;

    add_param(key, parameter, actfile, n_line, NULL);
  }

  fclose(init_file);

  return;
}

#if 0

static void p_swap(struct param *param, int i, int j)
{
  struct param tmp;
  tmp = param[i];
  param[i] = param[j];
  param[j] = tmp;
  return;
}

static void p_qsort(struct param *param, int left, int right)
{
  int     i, last;

  if (left >= right)  return;

  p_swap(param, left, (left+right)/2);
  last = left;
  for (i = left+1; i <= right; i++)
    if (strcmp(param[i].key, param[left].key) < 0)
      p_swap(param, ++last, i);

  p_swap(param, left, last);
  p_qsort(param, left, last-1);
  p_qsort(param, last+1, right);
  return;
}
#endif

void print_parameters(void)
{
  FUNCNAME("print_parameters");
  int   i;

  for (i = 0; i < n_param; i++)
  {
    MSG("%s:  %s\n", all_param[i].key, all_param[i].parameters);
    if (all_param[i].funcname)
    {
      MSG("initialized by %s() on line %3d of file \"%s\"\n", 
	  all_param[i].funcname, all_param[i].line_no, all_param[i].filename);
    }
    else if (all_param[i].filename)
    {
      MSG("initialized on line %2d of file \"%s\"\n", all_param[i].line_no, 
	  all_param[i].filename);
    }
    else
    {
      MSG("can not locate initialization location\n");
    }
  }
}

void save_parameters(const char *file, int info)
{
  FILE  *fp;
  int   i;

  if (!(fp = fopen(file, "w")))
    return;

  for (i = 0; i < n_param; i++)
  {
    fprintf(fp, "%s:  %s\n", all_param[i].key, all_param[i].parameters);

    if (info)
    {
      if (all_param[i].funcname)
      {
	fprintf(fp, "%%initialized by %s() on line %3d of file \"%s\"\n", 
		all_param[i].funcname, all_param[i].line_no, 
		all_param[i].filename);
      }
      else if (all_param[i].filename)
      {
	fprintf(fp, "%%initialized on line %2d of file \"%s\"\n", 
		all_param[i].line_no, all_param[i].filename);
      }
    }
  }
  fclose(fp);
}

void init_parameters(int p, const char *file_name)
{
  FUNCNAME("init_parameters");

  if (!file_name)
    return;

  read_parameters(file_name);

  GET_PARAMETER(0, "level of information", "%d", &msg_info);
  GET_PARAMETER(0, "WAIT", "%B", &msg_wait);
  GET_PARAMETER(0, "parameter information", "%d", &param_info);
  if (!msg_info)  param_info = 0;

  if (p  &&  msg_info) print_parameters();

  return;
}  

void add_parameter(int p, const char *key, const char *par)
{
  Add_parameter(p, key, par, NULL, NULL, 0);
}

void Add_parameter(int p, const char *key, const char *par, 
		   const char *fname, const char *file, int line)
{
  FUNCNAME("add_parameter");

  if (!key  ||  !par)
    return;

  add_param(key, par, file, line, fname);

  GET_PARAMETER(0, "level of information", "%d", &msg_info);
  GET_PARAMETER(0, "WAIT", "%B", &msg_wait);
  GET_PARAMETER(0, "parameter information", "%d", &param_info);
  if (!msg_info)  param_info = 0;

  if (p  &&  msg_info) print_parameters();

  return;
}  

#if 1
static const char *get_filename(const char *path_file)
{
  const char *s = path_file;
  if (s)
  {
    while (*s) s++;  /* goto end of string */
    while (s > path_file && *s != '/') s--;
    if (*s == '/') s++;
  }
  return(s);
}

static int sys_test(const char *command, const char *file)
{
  char  line[1024];

  if (!file || !command) return(0);
  sprintf(line, "%s %s > /dev/null", command, file);
  return(!system(line));
}
#endif

#include <time.h>

void init_parameters_cpp(int p, const char *fn, const char *flags)
{
  FUNCNAME("init_parameters_cpp");
  FILE    *fp;
#ifdef CPP
  int      val;
  char    tmp_file[LENGTH];
  char    call_cpp[3*LENGTH];
  time_t  act_time;
#endif

  if (!fn)
    return;

  if (!(fp = fopen(fn, "r")))
  {
    ERROR("can not read from file %s\n", fn);
    return;
  }
  if (fp)  fclose(fp);
#ifndef CPP
  ERROR("no cpp available; reading file %s without help of cpp\n", fn);
  init_parameters(p, fn);

  return;
#else
  fp = NULL;

  time(&act_time);
  if (sys_test("test -w", "/tmp"))               /* you may write to /tmp  */
  {
    const char  *file = get_filename(fn);
    sprintf(tmp_file, "/tmp/%s.cpp", file);
    if (sys_test("test -f", tmp_file))           /* file exists :-( */
    {
      sprintf(tmp_file, "/tmp/%s.cpp.%d", file, (int) act_time);
      if (sys_test("test -f", tmp_file))            /* file exists :-( */
	*tmp_file = 0;
    }
  }

  if (*tmp_file == 0)
  {
    sprintf(tmp_file, "%s.cpp", fn);
    if (sys_test("test -f", tmp_file))
    {
      sprintf(tmp_file, "%s.cpp.%d", fn, (int) act_time);
      if (sys_test("test -f", tmp_file))
	*tmp_file = 0;
    }
  }

  if (*tmp_file  && (fp = fopen(tmp_file, "w")))
    fclose(fp);
  else
  {
    ERROR("could not open temporary file for CPP\n");
    ERROR("can not write to /tmp and files %s.cpp and\n", fn);
    ERROR_EXIT("%s.cpp.%d either exist or are not writable\n", fn, act_time);
  }

  sprintf(call_cpp, "%s %s %s > %s", CPP, flags, fn, tmp_file);

/*--------------------------------------------------------------------------*/
/*  invoke cpp:                                                             */
/*--------------------------------------------------------------------------*/

  val = system(call_cpp);
  if (val)
  {
    MSG("val = %d\n", val);
    ERROR("error during cpp call; reading file %s without help of cpp\n", fn);
    WAIT;
    init_parameters(p, fn);
    return;
  }

/*--------------------------------------------------------------------------*/
/*  read parameters from temp file                                          */
/*--------------------------------------------------------------------------*/

  cpp_read = 1;
  read_parameters(tmp_file);
  cpp_read = 0;

/*--------------------------------------------------------------------------*/
/*  remove temp file                                                        */
/*--------------------------------------------------------------------------*/

  sprintf(call_cpp, "/bin/rm %s", tmp_file);
  val = system(call_cpp);

  GET_PARAMETER(0, "level of information", "%d", &msg_info);
  GET_PARAMETER(0, "WAIT", "%B", &msg_wait);
  GET_PARAMETER(0, "parameter information", "%d", &param_info);
  if (!msg_info)  param_info = 0;
  if (p && msg_info) print_parameters();

  return;
#endif
}  


static char *get_next_word(char **s)
{
  static char  Val[2*LENGTH];
  char         *val = Val, *line;
  
  for (line = *s; *line  &&  *line == ' '; line++);
  for (; *line  &&  (*val = *line) != ' '; val++, line++);

  *s = line;
  *val = '\0';
  return(Val);
}

static const char *param_call_fct = NULL, *param_call_file = NULL;
static int        param_call_line = 0;

int init_param_func_name(const char *call_fct, const char *call_file, 
			 int call_line)
{
  param_call_fct = call_fct;
  param_call_file = call_file;
  param_call_line = call_line;

  return(1);
}

int get_parameter(int info, const char *key, const char *format, ...)
{
  const char   *funcName, *func_name = "get_parameter";
  int          count, i, *ival;
  char         *sval, *s, *word, *cval;
  const char   *p;
  REAL         *rval;
  U_CHAR       *Uval;
  S_CHAR       *Sval;
  bool         *Bval;
  double       dval;

  va_list   arg;

  funcName = param_call_fct != NULL ? param_call_fct : func_name;

  if (param_info)
  {
    if (param_info > 1)
      info = MAX(info, param_info-1);
  }
  else
    info = 0;

  if (!all_param)
  {
    INFO(info, 1, "no parameters defined\n");
    return(0);
  }

  i = bin_search(all_param, key, n_param);

  if (i  < 0)
  {
    if (funcName != func_name)
    {
      INFO(info, 1, "initialization of parameter `%s'\n", key);
      INFO(info, 1, "fails on line %d of file %s\n", param_call_line, 
		   param_call_file);
    }
    else
      INFO(info, 1, "initialization of parameter `%s' fails\n", key);

    param_call_fct = NULL;
    return(0);
  }

  va_start(arg, format);
  count = 0;
  
  INFO(info, 2, "parameter `%s' initialized with: ", key);

  s = all_param[i].parameters;
 
  for (p = format; *p; p++)
  {
    if (*p != '%')  continue;

    word = get_next_word(&s);
    if (!*word)
    {
      PRINT_INFO(info, 2, "\n");
      if (all_param[i].funcname)
      {
	INFO(info, 4, "parameter initialized by %s()\n", 
		     all_param[i].funcname);
	INFO(info, 4, "on line %d of file \"%s\"\n", all_param[i].line_no, 
		     all_param[i].filename);
      }
      else if (all_param[i].filename)
      {
	INFO(info, 4, 
	  "parameter initialized on line %2d of init file \"%s\"\n", 
	   all_param[i].line_no, all_param[i].filename);
      }
      else
      {
	INFO(info, 4, "location of initialization unknown\n");
      }
      va_end(arg);
      param_call_fct = NULL;
      return(count);
    }
    count++;

    switch(*++p) {
    case 'B':
      Bval = va_arg(arg, bool *);      
      if (strcasecmp(word, "true") == 0 ||
	  strcasecmp(word, "t") == 0 ||
	  strcasecmp(word, "yes") == 0 ||
	  strcasecmp(word, "y") == 0 ||
	  strcasecmp(word, "1") == 0) {	
	*Bval = true;
	PRINT_INFO(info, 2, "%c ", *Bval);
      } else if (strcasecmp(word, "false") == 0 ||
		 strcasecmp(word, "f") == 0 ||
		 strcasecmp(word, "no") == 0 ||
		 strcasecmp(word, "n") == 0 ||
		 strcasecmp(word, "0") == 0 ||
		 strcasecmp(word, "nil") == 0 /* hommage a lisp */) {
	*Bval = false;
	PRINT_INFO(info, 2, "%c ", *Bval);
      } else {
	PRINT_INFO(info, 1, "\n");
	INFO(info, 1, 
	     "invalid initializer \"%s\" for boolean \"%s\",\n",
	     word, key);
	INFO(info, 1, "allowed are\n");
	INFO(info, 1,
	     "{ true, t, yes, y, 1, false, f, no, n, nil, 0 }\n");
	INFO(info, 1, "(case does not matter)\n");
      }
      break;
    case 'S':
      Sval = va_arg(arg, S_CHAR *);
      *Sval = (S_CHAR) atoi(word);
      PRINT_INFO(info, 2, "%d ", *Sval);
      break;
    case 'U':
      Uval = va_arg(arg, U_CHAR *);
      *Uval = (U_CHAR) atoi(word);
      PRINT_INFO(info, 2, "%d ", *Uval);
      break;
    case 'c':
      cval = va_arg(arg, char *);
      *cval = *word;
      PRINT_INFO(info, 2, "%c ", *cval);
      break;
    case 's':
      sval = va_arg(arg, char *);
      strcpy(sval, word);
      PRINT_INFO(info, 2, "%s ", sval);
      break;
    case 'd':
      ival = va_arg(arg, int *);
      *ival = atoi(word);
      PRINT_INFO(info, 2, "%d ", *ival);
      break;
    case 'i':
      ival = va_arg(arg, int *);
      *ival = strtol(word, NULL, 0);
      PRINT_INFO(info, 2, "%d (0x%x) ", *ival, *ival);
      break;
    case 'x':
      ival = va_arg(arg, int *);
      *ival = strtol(word, NULL, 16);
      PRINT_INFO(info, 2, "%d (0x%x) ", *ival, *ival);
      break;
    case 'o':
      ival = va_arg(arg, int *);
      *ival = strtol(word, NULL, 8);
      PRINT_INFO(info, 2, "%d (0x%x) ", *ival, *ival);
      break;
    case 'e':
    case 'f':
    case 'g':
      rval = va_arg(arg, REAL *);
      *rval = dval = atof(word);
      PRINT_INFO(info, 2, "%lg ", dval);
      break;
    case '*':
      break;
    default: 
      PRINT_INFO(info, 2, "\n");
      INFO(info, 2, 
	"unknow format specifier `%%%c', skipping initialization of %s\n", 
	 *p, key);
    }
  }

  PRINT_INFO(info, 2, "\n");
  if (funcName != func_name) {
    INFO(info, 2, "on line %d of file %s\n", param_call_line, param_call_file);
  }

  if (all_param[i].funcname)
  {
    INFO(info, 4, "parameter initialized by %s()\n", all_param[i].funcname);
    INFO(info, 4, "on line %d of file \"%s\"\n", all_param[i].line_no, 
		 all_param[i].filename);
  }
  else if (all_param[i].filename)
  {
    INFO(info, 4, 
      "parameter initialized on line %2d of init file \"%s\"\n", 
       all_param[i].line_no, all_param[i].filename);
  }
  else
  {
    INFO(info, 4, "location of initialization unknown\n");
  }
  
  va_end(arg);
  param_call_fct = NULL;
  return(count);
}
