/*
** xipvar.c for  in 
** 
** Made by 
** Login   <vianney@epita.fr>
** 
** Started on  Wed Sep  1 08:26:24 1999 
** Last update Fri Nov 12 03:52:07 1999 
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <fcntl.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Command.h>
#include "XmgMacro.h"
#include "XmgFancy.h"
#include "XmgPulldown.h"
#include "Xmg/Group.h"
#include "XmgI.h"
#include "Tabs.h"
#include "TextField.h"
#include "pat_data.h"
#include "pat_ip.h"
#include "pat_udp.h"
#include "pat_pad.h"
#include "pat_bootp.h"
#include "pat_pat.h"
#include "pat_ethaddr.h"
#include "pat_boolean.h"
#include "pat.h"
#include "nbgethost.h"
#include "xip.h"
#include "xipvar.h"
#ifdef HAVE_DLOPEN
# include <dlfcn.h>
#endif

extern Widget		toplevel;
extern t_boolean	verbose;
extern Pixmap		xipicon_pixmap;
extern char		xiprc[];
extern t_u32		xipdump_do_opts;

t_u32			tmpl_bufsiz = 16 * BUFSIZ;
t_u32			html_bufsiz = 16 * BUFSIZ;
t_u32			htmlize_bufsiz = 16 * BUFSIZ;
struct in_addr		my_ip = {-2};
t_ether_addr		my_ether = {0x42,0x42,0x42,0x42,0x42,0x43};
t_u16			ip_id = 1;

char			*tmpl_buf = NULL;
char			*html_buf = NULL;
char			*htmlize_buf = NULL;

int	memoryzone_chan;

PAT_NAME_GENERIC(memoryzone_pat_name,
		 (VOID_PTR)&memoryzone_chan,
		 "memoryzone")

PAT_INSERT_DECL(memoryzone_pat_insert)
{
  char		**zone;
  t_u32		siz;	
  t_status	status;

  assert(u32_pat.insert_proc);
  if ((status = u32_pat.insert_proc(NULL,
				    buf,
				    len,
				    value)) != 0)
    return (status);
  assert(len == sizeof (siz));
  siz = *((t_u32 *)(buf));
  zone = (char **)(data);
  if (*zone)
    {
      if (((*zone) = XIP_REALLOC_PROC(*zone,
				      siz,
				      "xipcf",
				      "typ_memoryzone_msg:ptr",
				      &status)) == NULL)
	{
	  err_print(status,"XIP_REALLOC_PROC");
	  exit(1);
	}
    }
  else
    {
      if (((*zone) = XIP_ALLOC_PROC(siz,
				    "xipcf",
				    "typ_memoryzone_msg:ptr",
				    &status)) == NULL)
	{
	  err_print(status,"XIP_ALLOC_PROC");
	  exit(1);
	}
    }
  return (0);
}

t_pat				memoryzone_pat = 
{
  memoryzone_pat_name,		/* t_pat_name_proc		*/
  NULL,				/* t_pat_off_proc		*/
  NULL,				/* t_pat_sub_proc		*/
  NULL,				/* t_pat_sum_proc		*/
  NULL,				/* t_pat_get_field_proc		*/
  NULL,				/* t_pat_set_field_proc		*/
  NULL,				/* t_pat_get_fields_proc	*/
  NULL,				/* t_pat_get_tmpl_proc		*/
  NULL,				/* t_pat_get_tmpl2_proc		*/
  NULL,				/* t_pat_has_opt_proc		*/
  NULL,				/* t_pat_adapt_len_proc		*/
  NULL,				/* t_pat_get_field_pat_proc	*/
  u32_pat_extract,		/* t_pat_extract_proc		*/
  memoryzone_pat_insert,	/* t_pat_insert_proc		*/
  NULL,				/* t_pat_get_choices_proc	*/
};


#ifdef HAVE_DLOPEN

int	plugins_chan;

PAT_NAME_GENERIC(plugins_pat_name,
		 (VOID_PTR)&plugins_chan,
		 "plugins")

t_vec	*plugins_vec = NULL;

PAT_EXTRACT_DECL(plugins_pat_extract)
{
  t_status	status;

  VEC_FOR(plugins_vec,char *path)
    {
      if ((status = str_cat_str(str,
				max_len,
				path)) != 0)
	return (status);
      if (VEC_IDX != (VEC_COUNT(plugins_vec) - 1))
	if ((status = str_cat_char(str,
				   max_len,
				   ';')) != 0)
	  return (status);
    }
  VEC_ENDFOR;
  return (0);
}

PAT_INSERT_DECL(plugins_pat_insert)
{
  t_vec		*vec;
  t_status	status;

  if ((vec = XIP_VEC_NEW(VEC_BASE,
			 &status)) == NULL)
    return (status);
  if ((status = vec_str_split(vec,
			      value,
			      ';')) != 0)
    {
      vec_str_delete(vec);
      return (status);
    }
  VEC_FOR(vec,char *path)
    {
      VOID_PTR		plugin;
      t_xip_plugin_proc	xip_plugin_proc;
      
      if (vec_str_index(plugins_vec,path) >= 0)
	{
	  continue ;
	}
#ifdef __OpenBSD__
      /* Thanks to George Dodd (root@siliconinc.net) */
# define RTLD_NOW RTLD_LAZY
#endif
      if ((plugin = dlopen(path,RTLD_NOW)) == NULL)
	{
	  err_print(ERR_XIP_SUBROUTINE,"%s: %s",dlerror());
	}
      else
	{
	  if ((xip_plugin_proc =
	       (t_xip_plugin_proc)dlsym(plugin,"xip_plugin_init"))
	      == NULL)
	    {
	      err_print(ERR_XIP_SUBROUTINE,"%s: %s",dlerror());
	      dlclose(plugin);
	    }
	  else
	    {
	      if ((status = xip_plugin_proc()) != 0)
		return (status);
	      if ((status = vec_str_add(plugins_vec,path)) != 0)
		return (status);
	    }
	}
    }
  VEC_ENDFOR;
  vec_str_delete(vec);
  return (0);
}

t_pat				plugins_pat = 
{
  plugins_pat_name,		/* t_pat_name_proc		*/
  NULL,				/* t_pat_off_proc		*/
  NULL,				/* t_pat_sub_proc		*/
  NULL,				/* t_pat_sum_proc		*/
  NULL,				/* t_pat_get_field_proc		*/
  NULL,				/* t_pat_set_field_proc		*/
  NULL,				/* t_pat_get_fields_proc	*/
  NULL,				/* t_pat_get_tmpl_proc		*/
  NULL,				/* t_pat_get_tmpl2_proc		*/
  NULL,				/* t_pat_has_opt_proc		*/
  NULL,				/* t_pat_adapt_len_proc		*/
  NULL,				/* t_pat_get_field_pat_proc	*/
  plugins_pat_extract,		/* t_pat_extract_proc		*/
  plugins_pat_insert,		/* t_pat_insert_proc		*/
  NULL,				/* t_pat_get_choices_proc	*/
};
#endif

t_32				xip_verb_level = 0;

t_mask_def			xip_verb_level_mask_defs[] = 
{
  {"misc",			VERB_XIP_MISC},
  {"all",			~0L},
  {NULL,			0}
};

#ifdef DEBUG
/* initializes the xip debug env. 
   This is a debug function */
VOID_FUNC			xip_debug_init(VOID_DECL)
{
  char				*str;
  t_mask			mask;

  if (str = getenv("XIPDUMP_VERB_LEVEL"))
    {
      t_vec			*vec;
      t_status			status;

      vec = XIP_VEC_NEW(VEC_ONE_BASE,
			&status);
      assert(vec);
      if ((status = vec_str_split(vec,str,'|')) != 0)
	{
	  err_print(status,"xip_debug_init");
	  abort();
	}
      if ((status = mask_from_vec_str(xip_verb_level_mask_defs,
				      vec,
				      &mask)) != 0)
	{
	  err_print(status,"xip_debug_init");
	  abort();
	}
      xip_verb_level = (t_32)mask;
      vec_str_delete(vec);
    }
}
#endif

t_assoc			pat_base_assocs[] = 
{
  {"octal",		(VOID_PTR)8},
  {"decimal",		(VOID_PTR)10},
  {"hexadecimal",	(VOID_PTR)16},
  {"Hexadecimal",	(VOID_PTR)BASE_HEX_CAP},
  NULL_ASSOC
};

t_xip_var		xip_vars[] = 
{
#define NOT_A_REAL_OFFSET	((t_off)(NULL))
#define NOT_A_REAL_SIZE		0

  {"tmpl_bufsiz",
   (t_off)(&tmpl_bufsiz),
   sizeof (tmpl_bufsiz),
   &memoryzone_pat,
   &tmpl_buf},
  {"html_bufsiz",
   (t_off)(&html_bufsiz),
   sizeof (html_bufsiz),
   &memoryzone_pat,
   &html_buf},
  {"htmlize_bufsiz",
   (t_off)(&htmlize_bufsiz),
   sizeof (htmlize_bufsiz),
   &memoryzone_pat,	
   &htmlize_buf},
  {"pat_data_bytes_per_line",
   (t_off)(&pat_data_bytes_per_line),
   sizeof (pat_data_bytes_per_line),
   &u32_pat,
   NULL},
  {"pat_udp_perform_sum",
   (t_off)(&pat_udp_perform_sum),
   sizeof (pat_udp_perform_sum),
   &boolean_pat,
   NULL},
  {"pat_udp_guess_mode",
   (t_off)(&pat_udp_guess_mode),
   sizeof (pat_udp_guess_mode),
   &boolean_pat,
   NULL},
  {"pat_verb_level",
   (t_off)(&pat_verb_level),
   sizeof (pat_verb_level),
   &u32mask_pat,
   pat_verb_level_mask_defs},
  {"pat_base",(t_off)(&pat_base),
   sizeof (pat_base),
   &u32assoc_pat,
   pat_base_assocs},
  {"my_ip",
   (t_off)(&my_ip),
   sizeof (my_ip),
   &inaddr_pat,
   NULL},
  {"my_ether",
   (t_off)(&my_ether),
   sizeof (my_ether),
   &etheraddr_pat,
   NULL},
  {"ip_id",
   (t_off)(&ip_id),
   sizeof (ip_id),
   &u16_pat,
   NULL},
  {"nbgethost_timeout",
   (t_off)(&nbgethost_timeout),
   sizeof (nbgethost_timeout),
   &u32_pat,
   NULL},
  {"pat_pad_nbytes",
   (t_off)(&pat_pad_nbytes),
   sizeof (pat_pad_nbytes),
   &u32_pat,
   NULL},
  {"pat_pad_sub_pat",
   (t_off)(&pat_pad_sub_pat),
   sizeof (pat_pad_sub_pat),
   &pat_pat,
   NULL},
  {"pat_etheraddr_dec_way",
   (t_off)(&pat_etheraddr_dec_way),
   sizeof (pat_etheraddr_dec_way),
   &boolean_pat,
   NULL},
  {"xipdump_do_opts",
   (t_off)(&xipdump_do_opts),
   sizeof (xipdump_do_opts),
   &boolean_pat,
   NULL},
  {"pat_bootp_do_vendor",
   (t_off)(&pat_bootp_do_vendor),
   sizeof (pat_bootp_do_vendor),
   &boolean_pat,
   NULL},
  {"pat_bootp_vendor_gt_64",
   (t_off)(&pat_bootp_vendor_gt_64),
   sizeof (pat_bootp_vendor_gt_64),
   &boolean_pat,
   NULL},
#ifdef HAVE_DLOPEN
  {"plugins",
   NOT_A_REAL_OFFSET,
   NOT_A_REAL_SIZE,
   &plugins_pat,
   NULL},
#endif
  NULL_XIP_VAR
};

t_vec			*xip_vars_vec = NULL;

t_status		xip_var_add(xv)
t_xip_var		*xv;
{
  t_xip_var		*own_xv;
  t_status		status;
  
  if ((own_xv = XIP_ALLOC_PROC(sizeof (t_xip_var),
			       "xip",
			       "xip_pkt_bar_add:ptr",
			       &status)) == NULL)
    return (status);
  FBCOPY(xv,own_xv,sizeof (t_xip_var));
  if ((status = vec_add(xip_vars_vec,
			own_xv)) != 0)
    {
      XIP_FREE_PROC(own_xv,
		    "xip",
		    "*:ptr");
      return (status);
    }
  return (0);
}

/* inits vars context.
   Currently, it allocates html_buf, tmpl_buf and htmlize_buf.
   Returns 0 if OK. Might return various errors */
t_status		xip_vars_init(VOID_DECL)
{
  t_status		status;
  t_xip_var		*xv;

#ifdef HAVE_DLOPEN
  /* reported by Peter Peters pp@win.tue.nl */ 
  if ((plugins_vec = XIP_VEC_NEW(VEC_BASE,
				 &status)) == NULL)
    return (status);
#endif
  if ((xip_vars_vec = XIP_VEC_NEW(VEC_ONE_BASE,
				 &status)) == NULL)
    return (status);
  xv = xip_vars;
  while (xv->name)
    {
      if ((status = xip_var_add(xv)) != 0)
	return (status);
      xv++;
    }
  if ((html_buf = XIP_ALLOC_PROC(html_bufsiz,
				 "xipcf",
				 "xip_vars_init:ptr",
				 &status)) == NULL)
    return (status);
  if ((tmpl_buf = XIP_ALLOC_PROC(tmpl_bufsiz,
				 "xipcf",
				 "xip_vars_init:ptr",
				 &status)) == NULL)
    return (status);
  if ((htmlize_buf = XIP_ALLOC_PROC(htmlize_bufsiz,
				    "xipcf",
				    "xip_vars_init:ptr",
				    &status)) == NULL)
    return (status);
  return (0);
}

/* destroys vars context.
   Currently, it destroys html_buf, tmpl_buf and htmlize_buf. */
VOID_FUNC		xip_vars_destroy(VOID_DECL)
{
  vec_ptr_delete(xip_vars_vec);
  XIP_FREE_PROC(html_buf,
		"xipcf",
		"*:ptr");
  XIP_FREE_PROC(tmpl_buf,
		"xipcf",
		"*:ptr");
  XIP_FREE_PROC(htmlize_buf,
		"xipcf",
		"*:ptr");
}

/* gets t_xip_var structure associated with name.
   Returns NULL of not found. */
t_xip_var		*xip_var_get_from_name(name)
char			*name;
{
  VEC_FOR(xip_vars_vec,t_xip_var *xv)
    {
      if (!strcmp(name,xv->name))
	return (xv);
    }
  VEC_ENDFOR;
  return (NULL);
}

/* sets a configuration variable.
   Note: it prints an error message and returns 0 if var doesn't exist.
   Returns 0 if OK. Might return various errors. */
t_status		xip_var_set(name,value)
char			*name;
char			*value;
{
  t_status		status;
  t_xip_var		*xv;

  if (verbose)
    fprintf(stderr,"setting %s to %s\n",name,value);
  if (!(xv = xip_var_get_from_name(name)))
    {
      err_print(ERR_XIP_NO_SUCH_VAR,"no such var %s",name);
      return (0);
    }
  if (!xv->pat->insert_proc)
    {
      err_print(ERR_XIP_NO_METHOD,"");
      return (0);
    }
  if ((status = xv->pat->insert_proc(xv->data,
				     (char *)xv->offset,
				     xv->size,
				     value)) != 0)
    {
      err_print(status,"insert_proc %s",xv->name);
      return (0);
    }
  return (0);
}

/* gets a variable value to a string.
   Returns 0 if OK. ERR_XIP_NO_SUCH_VAR if var doesn't exist. Might
   return various errors. */
t_status		xip_var_get(name,str,max_len)
char			*name;
char			*str;
int			max_len;
{
  t_status		status;
  t_xip_var		*xv;
  
  if (!(xv = xip_var_get_from_name(name)))
    return (ERR_XIP_NO_SUCH_VAR);
  if (!xv->pat->extract_proc)
    return (ERR_XIP_NO_METHOD);
  return (xv->pat->extract_proc(xv->data,
				(char *)xv->offset,
				xv->size,
				str,
				max_len));
}

/* saves configuration to a file.
   Returns 0 if OK. ERR_XIP_OPEN if fopen(3) fails. Might return various
   errors as it calls typ methods. */
t_status		xip_cf_save(fname)
char			*fname;
{
  FILE			*f;
  t_status		status;

  if ((f = fopen(fname,"w+")) == NULL)
    return (ERR_XIP_OPEN);
  fprintf(f,"#XIPCF\n");
  VEC_FOR(xip_vars_vec,t_xip_var *xv)
    {
      char		value[STR_BUFSIZ];
            
      value[0] = 0;
      if (!xv->pat->extract_proc)
	return (ERR_XIP_NO_METHOD);
      if ((status = xv->pat->extract_proc(xv->data,
					  (char *)(xv->offset),
					  xv->size,
					  value,
					  sizeof (value))) != 0)
	{
	  fclose(f);
	  return (status);
	}
      fprintf(f,"%s=%s\n",xv->name,value);
    }
  VEC_ENDFOR;
  fclose(f);
  return (status);
}

/* is a t_vec_cmp_proc.
   Sorts vars with "plugins" first. */
int			xip_cf_load_cmp(p1,p2)
VOID_PTR		*p1;
VOID_PTR		*p2;
{
  t_hash_elt		*he1;
  t_hash_elt		*he2;
  
  he1 = (t_hash_elt *)(*p1);
  he2 = (t_hash_elt *)(*p2);
  if (!strcmp(he1->key,"plugins"))
    {
      if (!strcmp(he2->key,"plugins"))
	return (0);
      else
	return (-1);
    }
  else
    {
      if (!strcmp(he2->key,"plugins"))
	return (1);
      else
	return (0);
    }
}

/* is a t_dict_walk_proc.
   Assign value to key in using xip_var_set(3).
   If global verbose is TRUE, then it prints a shortcut of the operation.
   Anyway this function will print an error message if there is a problem
   with xip_var_set(3).
   Returns 0 if OK. Might return various errors. */
t_status		xip_cf_load_walk(he,unused)
t_hash_elt		*he;
VOID_PTR		unused;
{
  t_status		status;
   
  if ((status = xip_var_set(he->key,he->value)) != 0)
    err_print(status,"xip_var_set %s",he->key);
  return (status);
}

/* loads a configuration file.
   Note: it might print various error and verbose messages as it calls
   xip_cf_load_walk(3).
   Returns 0 if OK. ERR_XIP_OPEN if open(2) fails.
   Might return various errors. */
t_status		xip_cf_load(fname)
char			*fname;
{
  int			fd;
  t_status		status;
  t_dict		*vars;
  t_hash_elt		*he;

  if ((fd = open(fname,O_RDONLY)) < 0)
    return (ERR_XIP_OPEN);
  vars = NULL;
  if ((vars = XIP_DICT_NEW(HASH_SMALL_BASE,
			   VEC_BASE,
			   &status)) == NULL)
    goto end;
  if ((status = parse_vars(fd,
			   '=',
			   vars,
			   dict_str_override)) != 0)
    goto end;
  if ((status = dict_walk_sorted_cmp(vars,
				     (t_dict_walk_proc)xip_cf_load_walk,
				     (t_vec_cmp_proc)xip_cf_load_cmp,
				     NULL)) != 0)
    goto end;
  status = 0;
end:
  close(fd);
  if (vars)
    dict_str_delete(vars);
  return (status);
}

VOID_FUNC		XipCfCancelCommand(w,shell,cbs)
Widget			w;
Widget			shell;
VOID_PTR		cbs;
{
  XtDestroyWidget(shell);
}

char			*xip_var_get_static(var)
char			*var;
{
  t_status		status;
  static char		str[BUFSIZ];

  str[0] = 0;
  if ((status = xip_var_get(var,str,sizeof (str))) != 0)
    {
      err_print(status,"xip_var_get %s",var);
    }
  return (str);
}

VOID_FUNC		XipCfOkCommand(w,shell,cbs)
Widget			w;
Widget			shell;
VOID_PTR		cbs;
{
  t_status		status;

  VEC_FOR(xip_vars_vec,t_xip_var *xv)
    {
      Widget		w;
      char		fqvarname[STR_BUFSIZ];

      fqvarname[0] = 0;
      if ((status = str_cat_fmt_va(fqvarname,
				   sizeof (fqvarname),
				   "*%s",
				   xv->name)) != 0)
	{
	  err_print(ERR_XIP_INTERNAL,"str_cat_fmt_va %s",xv->name);
	  xv++;
	  continue ;
	}
      if (w = XtNameToWidget(shell,fqvarname))
	{
	  char		*value;

	  value = NULL;
	  XtVaGetValues(w,
			XtNstring,	&value,
			NULL);
	  if (!value)
	    XtVaGetValues(w,
			XtNlabel,	&value,
			NULL);
	  if (strcmp(value,xip_var_get_static(xv->name)))
	    {
	      if ((status = xip_var_set(xv->name,value)) != 0)
		{
		  err_print(status,"xip_var_set %s to %s",xv->name,value);
		}
	    }
	}
      else
	err_print(ERR_XIP_INTERNAL,"no such widget %s",fqvarname);
    }
  VEC_ENDFOR;
  if ((status = xip_cf_save(xiprc)) != 0)
    err_print(status,"xip_cf_save %s",xiprc);
  XtDestroyWidget(shell);
}

t_xip_cf_box_entry		buffers_entries[] =
{
  {"tmplBufsizEntry",		"tmpl_bufsiz"},
  {"htmlBufiszEntry",		"html_bufsiz"},
  {"htmlizeBufsizEntry",	"htmlize_bufsiz"},
  NULL_XIP_CF_BOX_ENTRY
};

t_xip_cf_box_entry		pats_entries[] = 
{
  {"patDataBytesPerLineEntry",	"pat_data_bytes_per_line"},
  {"patUdpPerformSumEntry",	"pat_udp_perform_sum"},
  {"patUdpGuessModeEntry",	"pat_udp_guess_mode"},
  {"patBaseEntry",		"pat_base"},
  {"patPadNBytesEntry",		"pat_pad_nbytes"},
  {"patPadSubPatEntry",		"pat_pad_sub_pat"},
  {"patEtheraddrDecWayEntry",	"pat_etheraddr_dec_way"},
  {"patBootpDoVendorEntry",	"pat_bootp_do_vendor"},
  {"patBootpVendorGT64Entry","pat_bootp_vendor_gt_64"},
  {"nbgethostTimeoutEntry",	"nbgethost_timeout"},
  {"xipdumpDoOptsEntry",	"xipdump_do_opts"},
  NULL_XIP_CF_BOX_ENTRY
};

t_xip_cf_box_entry		ip_stack_entries[] = 
{
  {"myIpEntry",			"my_ip"},
  {"myEtherEntry",		"my_ether"},
  {"ipIdEntry",			"ip_id"},
  NULL_XIP_CF_BOX_ENTRY
};

t_xip_cf_box_entry		plugins_entries[] = 
{
  {"pluginsEntry",		"plugins"},
  NULL_XIP_CF_BOX_ENTRY
};

t_xip_cf_box_entry		debug_entries[] = 
{
  {"patVerbLevelEntry",		"pat_verb_level"},
  NULL_XIP_CF_BOX_ENTRY
};

t_xip_cf_box_group		xip_cf_box_groups[] =
{
  {"buffersGroup",		"buffers",	buffers_entries},
  {"patsGroup",			"patterns",	pats_entries},
  {"ipStackGroup",		"ip_stack",	ip_stack_entries},
  {"pluginsGroup",		"plugins",	plugins_entries},
  {"debugGroup",		"debug",	debug_entries},
  NULL_XIP_CF_BOX_GROUP
};

t_vec			*xip_cf_box_groups_vec = NULL;

t_status		xip_cf_box_group_add(xcbg)
t_xip_cf_box_group	*xcbg;
{
  t_xip_cf_box_group	*own_xcbg;
  t_status		status;
  
  if ((own_xcbg = XIP_ALLOC_PROC(sizeof (t_xip_cf_box_group),
			       "xip",
			       "xip_pkt_bar_add:ptr",
			       &status)) == NULL)
    return (status);
  FBCOPY(xcbg,own_xcbg,sizeof (t_xip_cf_box_group));
  if ((status = vec_add(xip_cf_box_groups_vec,
			own_xcbg)) != 0)
    {
      XIP_FREE_PROC(own_xcbg,
		    "xip",
		    "*:ptr");
      return (status);
    }
  return (0);
}

t_status		xip_cf_box_groups_init(VOID_DECL)
{
  t_status		status;
  t_xip_cf_box_group	*xcbg;

  if ((xip_cf_box_groups_vec = XIP_VEC_NEW(VEC_ONE_BASE,
				 &status)) == NULL)
    return (status);
  xcbg = xip_cf_box_groups;
  while (xcbg->widget_name)
    {
      if ((status = xip_cf_box_group_add(xcbg)) != 0)
	return (status);
      xcbg++;
    }
  return (0);
}

VOID_FUNC		xip_cf_box_groups_destroy(VOID_DECL)
{
  vec_ptr_delete(xip_cf_box_groups_vec);
}

/* creates a configuration panel from variables */
VOID_FUNC		XipCreateCfBox(parent)
Widget			parent;
{
  Widget		shell;
  t_status		status;
  Widget		cancelCommand;
  Widget		okCommand;
  Widget		tabs;

  INTERFACE(parent,
            GETCHILDSHELL(shell)
            ("xipCfBox",transientShellWidgetClass,
             XtNmappedWhenManaged,	False,
	     XtNtransientFor,		parent,
	     XtNallowShellResize,	True,
	     XtNiconPixmap,		xipicon_pixmap,
	     CHILD("form",formWidgetClass,
		   GETCHILD(tabs)
		   ("tabs",tabsWidgetClass,
		    INSERTCODE(
			      VEC_FOR(xip_cf_box_groups_vec,
				      t_xip_cf_box_group *xcbg)
			      {
				t_xip_cf_box_entry	*xcbe;
				
				INTERFACE(GETPARENT,
					  CHILD(xcbg->widget_name,
						groupWidgetClass,
						XtNnumCols,	2,
						XtNtabLabel,	xcbg->label,
						INSERTCODE(
						     xcbe = xcbg->entries;
						     while (xcbe->widget_name)
						     {
						       t_xip_var	*xv;
						       t_vec		*choices;
						       t_boolean	do_sort;

						       if (!(xv = xip_var_get_from_name(xcbe->var)))
							 {
							   err_print(ERR_XIP_NO_ENT,"xip_var_get_from_name");
							   xcbe++;
							   continue ;
							 }
						       if ((choices = XIP_VEC_NEW(VEC_BASE,
										  &status)) == NULL)
							 {
							   err_print(status,"XIP_VEC_NEW");
							   xcbe++;
							   continue ;
							 }
						       do_sort = TRUE;
						       if (xv->pat->get_choices_proc)
							 {
							   if ((status = xv->pat->get_choices_proc(xv->data,
												   (char *)xv->offset,
												   xv->size,
												   choices,
												   &do_sort)) != 0)
							     {
							       err_print(status,"get_choices_proc");
							       vec_str_delete(choices);
							       xcbe++;
							       continue ;
							     }
							 }
						       else
							 status = 0;
						       if (status == 0 && VEC_COUNT(choices) > 0)
							 {
							   Widget	pulldown;

							   if (do_sort)
							     vec_str_sort(choices);
							   XtVaCreateManagedWidget(xcbe->widget_name,
										   labelWidgetClass,
										   GETPARENT,
										   XtNlabel,
										   xcbe->var,
										   NULL);
							   pulldown = 
							     XmgCreateComplexPulldownFromVecStr(xcbe->var,
												GETPARENT,
												choices,
												(XtCallbackProc)XmgPulldownMenuCallback);
							   XtVaSetValues(pulldown,
									 XtNlabel,	xip_var_get_static(xcbe->var),
									 NULL);
							 }
						       else
							 {
							   INTERFACE(GETPARENT,
								     CHILD(xcbe->widget_name,
									   labelWidgetClass,
									   XtNlabel,
									   xcbe->var,
									   END),
								     CHILD(xcbe->var,textfieldWidgetClass,
									   XtNstring,   xip_var_get_static(xcbe->var),
									   END),
								     ENDINTERFACE);
							 }
						       vec_str_delete(choices);
						       xcbe++;
						     }
						     ),
						END),
					  ENDINTERFACE);
			      }
			      VEC_ENDFOR;
			      ),
		    END),
		   GETCHILD(okCommand)
		   ("ok",commandWidgetClass,
		    XtNfromVert,	tabs,
		    CALLBACK(XtNcallback,
			     (XtCallbackProc)XipCfOkCommand,
			     shell),
		    END),
		   GETCHILD(cancelCommand)
		   ("cancel",commandWidgetClass,
		    XtNfromVert,	tabs,
		    XtNfromHoriz,	okCommand,
		    CALLBACK(XtNcallback,
			     (XtCallbackProc)XipCfCancelCommand,
			     shell),
		    END),
		   END),
	     REALIZE,
	     END),
	    ENDINTERFACE);
  XmgCenterWidget(shell);
  XmgSetDestroyOnDelete(shell);
  XtPopup(shell,XtGrabExclusive);
}
