/*
** mg_opt.c for  in 
** 
** Made by 
** Login   <root@epita.fr>
** 
** Started on  Wed Sep  1 06:14:23 1999 
** Last update Thu Oct 28 20:15:48 1999 
*/
#include <stdio.h>
#include "mg.h"

/* is a t_opt_proc.
   Option value is the option itself. */
int			opt_is_arg(oc,idx,addr,status)
t_opt_context		*oc;
int			idx;
char			**addr;
t_status		*status;
{
  (*addr) = oc->argv[idx];
  oc->tr_args[idx] = TRUE;
  return (1);
}

/* is a t_opt_proc.
   Option value is TRUE. */
int			opt_boolean_true(oc,idx,addr,status)
t_opt_context		*oc;
int			idx;
t_boolean		*addr;
t_status		*status;
{
  (*addr) = TRUE;
  oc->tr_args[idx] = TRUE;
  return (1);
}

/* is a t_opt_proc.
   Option value is FALSE. */
int			opt_boolean_false(oc,idx,addr,status)
t_opt_context		*oc;
int			idx;
t_boolean		*addr;
t_status		*status;
{
  (*addr) = FALSE;
  oc->tr_args[idx] = TRUE;
  return (1);
}

/* is a t_opt_proc.
   Option value is the string after option. */
int			opt_str(oc,idx,addr,status)
t_opt_context		*oc;
int			idx;
char			**addr;
t_status		*status;
{
  if ((idx + 1) >= *(oc->argc))
    {
      (*status) = ERR_MG_NEED_MORE;
      return (-1);
    }
  (*addr) = oc->argv[idx + 1];
  oc->tr_args[idx] = TRUE;
  oc->tr_args[idx + 1] = TRUE;
  return (2);
}

/* is a t_opt_proc.
   Option value is a integer after option. */
int			opt_s32(oc,idx,addr,status)
t_opt_context		*oc;
int			idx;
t_s32			*addr;
t_status		*status;
{
  if ((idx + 1) >= *(oc->argc))
    {
      (*status) = ERR_MG_NEED_MORE;
      return (-1);
    }
  (*addr) = atoi(oc->argv[idx + 1]);
  oc->tr_args[idx] = TRUE;
  oc->tr_args[idx + 1] = TRUE;
  return (2);
}

/* is a t_opt_proc.
   Option value is a integer after option. */
int			opt_int(oc,idx,addr,status)
t_opt_context		*oc;
int			idx;
int			*addr;
t_status		*status;
{
  if ((idx + 1) >= *(oc->argc))
    {
      (*status) = ERR_MG_NEED_MORE;
      return (-1);
    }
  (*addr) = atoi(oc->argv[idx + 1]);
  oc->tr_args[idx] = TRUE;
  oc->tr_args[idx + 1] = TRUE;
  return (2);
}

int			opt_tr_args_matched(oc)
t_opt_context		*oc;
{
  int			matched;
  int			i;

  matched = 0;
  i = 0;
  while (i < *(oc->argc))
    {
      if (oc->tr_args[i] == TRUE)
	matched++;
      i++;
    }
  return (matched);
}

VOID_FUNC		opt_build_new_args(oc,matched)
t_opt_context		*oc;
int			matched;
{
  int			new_argc;
  int			i;
  int			j;    
  
  new_argc = *(oc->argc) - matched;
  i = 0;
  j = 0;
  while (i < *(oc->argc))
    {
      if (oc->tr_args[i] == FALSE)
	{
	  oc->argv[j] = oc->argv[i];
	  j++;
	}
      i++;
    }
  *(oc->argc) = new_argc;
}

t_status		opt_init_tr_args(oc)
t_opt_context		*oc;
{
  int			i;

  if (*(oc->argc) > oc->nb_tr_args)
    return (-ERR_MG_TOO_MANY);
  i = 0;
  while (i < *(oc->argc))
    {
      oc->tr_args[i] = FALSE;
      i++;
    }
  return (0);
}

t_status		opt_init_tr_opts(oc)
t_opt_context		*oc;
{
  int			i;

  if (oc->nb_opts > oc->nb_tr_opts)
    return (-ERR_MG_TOO_MANY);
  i = 0;
  while (i < oc->nb_opts)
    {
      oc->tr_opts[i] = 0; 
      i++;
    }
  return (0);
}

/* gets all the options defined by the context.
   Note that t_opt_context must be entirely filled by the user. 
   Returns 0 if OK, might return various errors */
int			opt_get(oc,status)
t_opt_context		*oc;	/* see a_opt.h */
t_status		*status;
{
  int			i;

  if (((*status) = opt_init_tr_args(oc)) < 0)
    return (-1);
  if (((*status) = opt_init_tr_opts(oc)) < 0)
    return (-1);
  i = 1;
next:
  while (i < *(oc->argc))
    {
      int		j;
      
      j = 0;
      while (j < oc->nb_opts)
	{
	  if (!strcmp(oc->argv[i],oc->opts[j].name))
	    {
	      char	*addr;
	      int	nmatch;

	      if (oc->tr_opts[j] == TRUE &&
		  !(oc->opts[j].flags & OPT_ALLOWMULTI))
		return (-ERR_MG_EXIST);
	      addr = oc->base_addr + oc->opts[j].offset;
	      if ((nmatch = oc->opts[j].proc(oc,
					     i,
					     addr,
					     status)) < 0)
		return (-1);
	      oc->tr_opts[j] = TRUE;
	      i += nmatch;
	      goto next;
	    }
	  j++;
	}
      i++;
    }
  {
    int			tr_args_matched;
    
    tr_args_matched = opt_tr_args_matched(oc);
    opt_build_new_args(oc,tr_args_matched);
    return (tr_args_matched);
  }
}

/* gives a nice usage messages according to an array of options.
   If full_usage is TRUE, then long version of options is printed*/
VOID_FUNC		opt_usage(f,opts,nb_opts,full_usage)
FILE			*f;
t_opt			*opts;
int			nb_opts;
t_boolean		full_usage;
{
  int			i;

  i = 0;
  while (i < nb_opts)
    {
      if (opts[i].param_comment != NULL)
	fprintf(f,"[%s %s]",opts[i].name,opts[i].param_comment);
      else
	fprintf(f,"[%s]",opts[i].name);
      if (full_usage)
	if (opts[i].comment != NULL)
	  fprintf(f," - %s\n",opts[i].comment);
	else
	  fprintf(f,"\n");
      i++;
    }
}

/* checks if there is no identical options in an array.
   This is used mainly because in some super-meta programs the number
   of options is incredibely large and it might be confusing.
   This function is used in conjonction with assert(3) */
int			opt_check(opts,nb_opts)
t_opt			*opts;
int			nb_opts;
{
  int			i;

  i = 0;
  while (i < nb_opts)
    {
      int		j;

      j = 0;
      while (j < nb_opts)
	{
	  if (i != j && !strcmp(opts[i].name,opts[j].name))
	    return (-ERR_MG_EXIST);
	  j++;
	}
      i++;
    }
  return (0);
}

