/*
** main.c for  in 
** 
** Made by 
** Login   <vianney@epita.fr>
** 
** Started on  Wed Sep  1 08:27:33 1999 
** Last update Fri Nov 12 04:02:50 1999 
*/
#include "config.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <libnet.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xmu/Converters.h>
#include <X11/Xaw/Box.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/MenuButton.h>
#include <X11/Xaw/Paned.h>
#include <X11/Xaw/Scrollbar.h>
#include <X11/Xaw/SimpleMenu.h>
#include <X11/Xaw/SmeBSB.h>
#include <X11/Xaw/SmeLine.h>
#include <X11/Xaw/Toggle.h>
#include <X11/Xaw/Panner.h>
#include <X11/Xaw/Porthole.h>
#include "SmeBSBM.h"
#include "Xws.h"
#include "Xmg.h"
#include "XmgFancy.h"
#include "XmgFileGet.h"
#include "XmgMacro.h"
#include "pkt.h"
#include "pat.h"
#include "pat_data.h"
#include "pat_ip.h"
#include "pat_tcp.h"
#include "xipvar.h"
#include "xipres.h"
#include "xippkt.h"
#include "xipimg.h"
#include "xipshcut.h"
#include "xipicon.xbm"
#include "xipnet.h"
#include "gray.xbm"
#include "check.xbm"

extern pcap_t		*pcap;
extern int		packets_treated;
extern int		packets_dropped;
extern pid_t		pcap_pid;

char			xiprc[MAXPATHLEN];
char			*xip_cf = NULL;
t_boolean		verbose = FALSE;
t_boolean		big_packets = FALSE;
#ifdef DEBUG_MALLOC
t_boolean		debug_malloc = FALSE;
t_boolean		debug_malloc_verbose = FALSE;
#endif
t_boolean		help = FALSE;
char			*pcap_dev = NULL;
char			hard_filter[STR_BUFSIZ];
t_s32			pcap_snaplen = 68;
t_boolean		pcap_promisc = TRUE;
t_s32			pcap_to_ms = 10;
t_boolean		pcap_optimize = TRUE;
char			*pcap_fname = NULL;
t_s32			pcap_cnt = -1;
t_boolean		pcap_fork = FALSE;

XtAppContext		app_context;
Widget			toplevel;
Widget			pktbox;
Widget			pannershell;
Widget			pannerbox;
Widget			porthole;    
Widget			aboutshell = NULL;
Widget			aboutfancy;
Widget			xphelpshell;
Widget			xphelpfancy;
Widget			flowbsb = NULL;
Widget			helpmodebsb = NULL;
Widget			pinmodebsb = NULL;
Widget			scrollmodebsb = NULL;
Pixmap			xipicon_pixmap;
Pixmap			gray_pixmap;
Pixmap			check_pixmap;
t_boolean		xphelpvisible;
t_boolean		capflow = TRUE;

t_opt			main_opts[] = 
{
  {"-h",	(t_opt_proc)opt_boolean_true,	(t_off)&help,
   NULL,"this help",0},
  {"-v",	(t_opt_proc)opt_boolean_true,	(t_off)&verbose,
   NULL,"turn on verbose mode",0},
#ifdef DEBUG_MALLOC
  {"-dm",	(t_opt_proc)opt_boolean_true,	(t_off)&debug_malloc,
   NULL,"turn on debug_malloc",0},
  {"-dmv",	(t_opt_proc)opt_boolean_true,	(t_off)&debug_malloc_verbose,
   NULL,"turn on debug_malloc_verbose",0},
#endif
  {"-cf",	(t_opt_proc)opt_str,		(t_off)&xip_cf,
   "xip_cf",NULL,0},
  {"-i",	(t_opt_proc)opt_str,		(t_off)&pcap_dev,
   "dev",NULL,0},
  {"-s",	(t_opt_proc)opt_s32,		(t_off)&pcap_snaplen,
   "snaplen",NULL,0},
  {"-p",	(t_opt_proc)opt_boolean_false,	(t_off)&pcap_promisc,
   NULL,"no promiscuous mode",0},
  {"-t",	(t_opt_proc)opt_s32,		(t_off)&pcap_to_ms,
   "to_ms","timeout in milliseconds",0},
  {"-O",	(t_opt_proc)opt_boolean_false,	(t_off)&pcap_optimize,
   NULL,"don't optimize",0},
  {"-r",	(t_opt_proc)opt_str,		(t_off)&pcap_fname,
   "tcpdump_file_name",NULL,0},
  {"-c",	(t_opt_proc)opt_s32,		(t_off)&pcap_cnt,
   "count","used with -r",0},
  {"-n",	(t_opt_proc)opt_boolean_false,	(t_off)&pat_resolve,
   NULL,"turn off resolve mode",0},
  {"-B",	(t_opt_proc)opt_boolean_true,	(t_off)&big_packets,
   NULL,"big packets",0},
  {"-F",	(t_opt_proc)opt_boolean_true,	(t_off)&pcap_fork,
   NULL,"fork pcap stuff",0},
};

char			*fallback[] = 
{
  /* Make check bitmaps visible */
  "*flow.rightMargin:			16",
  "*help_mode.rightMargin:		16",
  "*pin_mode.rightMargin:		16",
  "*scroll_mode.rightMargin:		16",
  
  /* Make file selector decently visible */
  "*xmgFileGetShell.width:		400",
  "*xmgFileGetShell.height:		400",
  "*FileNominator.filterDirectoryNames:	False",

  /* Adapt panner to slow graphic systems */
  "*Panner.rubberBand:			True",
  
  /* Make toggle as a square */
  "*XmgFancy*Toggle.font:               5x8",

  /* Make "view source" decently visible */
  "*XmgFancy*xmgFancyViewSource.width:	400",
  "*XmgFancy*xmgFancyViewSource.height:	400",
  
  /* Spare memory by making small packets not keeping HTML source */
  "*smallPktFancy.debugKeepSource:	False",
  
  /* Make small packets "small" by using tiny fonts */
  "*smallPktFancy.normalFontName: -*-*-*-*-*-*-2-*-*-*-*-*-*-*",
  "*smallPktFancy.smallFontName: -*-*-*-*-*-*-2-*-*-*-*-*-*-*",
  "*smallPktFancy._tinyFontName: -*-*-*-*-*-*-2-*-*-*-*-*-*-*",
  "*smallPktFancy.italicFontName: -*-*-*-*-*-*-2-*-*-*-*-*-*-*",
  "*smallPktFancy.boldFontName: -*-*-*-*-*-*-2-*-*-*-*-*-*-*",
  "*smallPktFancy.header1FontName: -*-*-*-*-*-*-2-*-*-*-*-*-*-*",
  "*smallPktFancy.header2FontName: -*-*-*-*-*-*-2-*-*-*-*-*-*-*",
  "*smallPktFancy.fixedFontName: -*-*-*-*-*-*-2-*-*-*-*-*-*-*",
  
  /* Make main window decently visible */
  "*Xipdump.width:			500",
  "*Xipdump.height:			300",

  NULL
};

/* gives a short option list then exits. */
VOID_FUNC		usage(VOID_DECL)
{
  opt_usage(stderr,main_opts,ARRAY_COUNT(main_opts),TRUE);
  exit(1);
}

/* check eventual options coherency, prints out a recall of options. */
t_status		check_options(VOID_DECL)
{
  return (0);
}

/* destroys (mostly) everything then exits.
   Note: XtDestroyWidget(toplevel) and 
   XtDestroyApplicationContext(app_context) don't take effect since there
   are 2 phases of destroy in Xt. It's why we have to "clear" "packet box"
   manually before debugging memory. */
VOID_FUNC		myexit(code)
int			code;	/* exit(2) code */
{
  if (pcap_fork)
    kill(pcap_pid,SIGKILL);
  signal(SIGINT,SIG_DFL);
  xip_cf_box_groups_destroy();
  xip_vars_destroy();
  xip_extra_vars_destroy();
  xip_pkt_bar_destroy();
  xip_methods_destroy();
  xip_pkt_cut_destroy();
  xip_shortcuts_destroy();
  pat_destroy();
  XmgDestroy();
  XmgMacroDestroy();
#ifdef NOTDEF  
  XtDestroyWidget(toplevel);
  XtDestroyApplicationContext(app_context);
#endif
#ifdef DEBUG_MALLOC
  if (debug_malloc)
    {
      gdm_status();
    }
#endif
  exit(code);
}

/* is an XtCallbackProc.
   Does nothing. */
VOID_FUNC			bsb_nop(w,closure,ptr)
Widget				w;
XtPointer			closure;
XtPointer			ptr;
{
  /* nop */
}

VOID_FUNC			XipCreateSmallPkt(pkt,own_pkt)
t_pkt				*pkt;
t_boolean			own_pkt;
{
  Widget			w;

  if (w = XipCreatePkt(pktbox,
		       pkt,
		       own_pkt))
    {
      XtInstallAccelerators(w,pktbox);
    }
}

/* is an XtCallbackProc.
   set/unset "check" bitmap to the specified BSB */
VOID_FUNC			XipSetCheck(w,value)
Widget				w;
Boolean				value;
{
  XtVaSetValues(w,
		XtNrightBitmap,	value?check_pixmap:None,
		NULL);
}

extern t_pkt			*xip_cut_pkt;

/* is an XtCallbackProc.
   Empty cut buffer */
VOID_FUNC			bsb_empty_cut_buf(w,closure,ptr)
Widget				w;
VOID_PTR			closure;
XtPointer			ptr;
{
  t_status			status;
  char				*nbuf;

  if ((nbuf = XIP_ALLOC_PROC(sizeof (char),
			     "xip",
			     "bsb_empty_cut_buf:buf",
			     &status)) == NULL)
    {
      XmgErrPrint(status,"XIP_ALLOC_PROC");
      return ;
    }
  XIP_FREE_PROC(xip_cut_pkt->buf,
		"xip",
		"*:buf");
  bzero(nbuf,1);
  xip_cut_pkt->buf = nbuf;
  xip_cut_pkt->len = xip_cut_pkt->netlen = 1;
  xip_cut_pkt->pat = &data_pat;
}

/* is an XtCallbackProc.
   Make packet from cut buffer. */
VOID_FUNC			bsb_make_pkt_from_cut_buf(w,closure,ptr)
Widget				w;
VOID_PTR			closure;
XtPointer			ptr;
{
  t_status			status;
  t_pkt				*npkt;

  if ((npkt = pkt_dup(xip_cut_pkt,
		      XIP_ALLOC_PROC,
		      XIP_FREE_PROC,
		      &status)) == NULL)
    {
      XmgErrPrint(toplevel,status,"pkt_dup");
      return ;
    }
  XipCreatePkt(toplevel,
	       npkt,
	       TRUE);
}

/* is an XtCallbackProc.
   Controls flow from GUI. */
VOID_FUNC			bsb_flow(w,closure,ptr)
Widget				w;
VOID_PTR			closure;
XtPointer			ptr;
{
  capflow = !capflow;
  XipSetCheck(flowbsb,capflow);
}

/* is an XtCallbackProc.
   Calls myexit(3) */
VOID_FUNC			bsb_quit(w,closure,ptr)
Widget				w;
XtPointer			closure;
XtPointer			ptr;
{
  myexit(0);
}

/* is an XtCallbackProc.
   Clears the packet box */
VOID_FUNC			bsb_clear(w,closure,ptr)
Widget				w;
XtPointer			closure;
XtPointer			ptr;
{
  WidgetList			children;
  Cardinal			num_children;
  int				i;

  XtVaGetValues(pktbox,
		XtNchildren,	&children,
		XtNnumChildren,	&num_children,
		NULL);
  XtUnmanageChildren(children,
		     num_children);
  i = 0;
  while (i < num_children)
    {
      XtDestroyWidget(children[i]);
      i++;
    }
}

/* is a XtCallbackProc.
   Open a new packet of one byte in a new frame */
VOID_FUNC			bsb_new_pkt(w,closure,ptr)
Widget				w;
XtPointer			closure;
XtPointer			ptr;
{
  t_pkt				*npkt;
  t_status			status;
  
  if ((npkt = pkt_new(1,
		      &data_pat,
		      XIP_ALLOC_PROC,
		      XIP_FREE_PROC,
		      &status)) == NULL)
    {
      XmgErrPrint(toplevel,status,"pkt_new");
      return ;
    }
  XipCreatePkt(toplevel,npkt,TRUE);
}

/* is a XtCallbackProc.
   Calls XipCreateCfBox(3) */
VOID_FUNC			bsb_edit_config(w,closure,ptr)
Widget				w;
XtPointer			closure;
XtPointer			ptr;
{
  XipCreateCfBox(toplevel);
}

/* is an XtCallbackProc.
   Asks for a file name via a file selector then save config */
VOID_FUNC			bsb_save_config_as(w,closure,ptr)
Widget				w;
XtPointer			closure;
XtPointer			ptr;
{
  t_status			status;
  char				*fname;

  if (fname = XmgFileGet("*.cf",FALSE))
    {
      if ((status = xip_cf_save(fname)) != 0)
	XmgErrPrint(toplevel,status,"xip_cf_save %s",fname);
    }
}

/* loads a collection of packet to a file (.pkc files).
   Returns 0 if OK. ERR_XIP_OPEN if open(2) fails. 
   Might return various errors. */
t_status			xip_load_collection(fname)
char				*fname;
{
  FILE				*f;
  char				pktfname[MAXPATHLEN];
  t_status			status;
  char				dirname[MAXPATHLEN];
  char				tmpfname[MAXPATHLEN];
  char				*ptr;

  dirname[0] = 0;
  if ((status = str_cat_str(dirname,
			    sizeof (dirname),
			    fname)) != 0)
    return (status);
  if (ptr = rindex(dirname,'/'))
    *ptr++ = 0;
  if ((f = fopen(fname,"r")) == NULL)
    return (ERR_XIP_OPEN);
  while (!feof(f))
    {
      t_pkt			*npkt;
      t_status			status;
      int			len;

      fgets(pktfname,sizeof (pktfname),f);
      if ((len = strlen(pktfname)) == 1)
	continue ;
      if (pktfname[len - 1] == '\n')
	pktfname[len - 1] = 0;
      tmpfname[0] = 0;
      if (index(pktfname,'/'))
	{
	  if ((status = str_cat_str(tmpfname,
				    sizeof (tmpfname),
				    pktfname)) != 0)
	    return (status);
	}
      else
	{
	  if ((status = str_cat_fmt_va(tmpfname,
				       sizeof (tmpfname),
				       "%s/%s",
				       dirname,
				       pktfname)) != 0)
	    return (status);
	}
      if ((npkt = pkt_new(1,
			  &data_pat,
			  XIP_ALLOC_PROC,
			  XIP_FREE_PROC,
			  &status)) == NULL)
	goto bad;
      if ((status = pkt_load(npkt,
			     tmpfname,
			     XIP_ALLOC_PROC,
			     XIP_FREE_PROC)) != 0)
	{
	  err_print(status,"pkt_load %s",tmpfname);
	  pkt_delete(npkt,
		     XIP_FREE_PROC);
	  continue ;
	}
      XipCreateSmallPkt(npkt,TRUE);
    }
  fclose(f);
  return (0);
bad:
  fclose(f);
  return (status);
}

/* is an XtCallbackProc.
   Asks for a file name via a file selector then open a packets collection */
VOID_FUNC			bsb_load_collection(w,closure,ptr)
Widget				w;
XtPointer			closure;
XtPointer			ptr;
{
  t_status			status;
  char				*fname;

  if (fname = XmgFileGet("*.pkc",TRUE))
    {
      if ((status = xip_load_collection(fname)) != 0)
	XmgErrPrint(toplevel,status,"xip_load_collection %s",fname);
    }
}

/* saves a collection of packets to a file.
   Returns 0 if OK. ERR_XIP_OPEN if open(2) fails.
   Might return various errors. */
t_status			xip_save_collection(fname)
char				*fname;
{
  FILE				*f;
  char				basename[MAXPATHLEN];
  char				pktfname[MAXPATHLEN];
  char				*without_slash;
  t_status			status;
  char				*ptr;
  WidgetList			children;
  Cardinal			num_children;
  int				i;

  XtVaGetValues(pktbox,
		XtNchildren,	&children,
		XtNnumChildren,	&num_children,
		NULL);
  basename[0] = 0;
  if ((status = str_cat_str(basename,
			    sizeof (basename),
			    fname)) != 0)
    return (status);
  if (ptr = rindex(basename,'.'))
    *ptr++ = 0;
  if (without_slash = rindex(basename,'/'))
    without_slash++;
  else
    without_slash = basename;
  if ((f = fopen(fname,"w+")) == NULL)
    return (ERR_XIP_OPEN);
  i = 0;
  while (i < num_children)
    {
      t_xip_pkt			*xp;

      XtVaGetValues(children[i],
		    XtNuserData,	&xp,
		    NULL);
      pktfname[0] = 0;
      if ((status = str_cat_fmt_va(pktfname,
				   sizeof (pktfname),
				   "%s_%d.pkt",
				   basename,
				   i)) != 0)
	goto bad;
      if ((status = pkt_save(xp->pkt,pktfname)) != 0)
	goto bad;
      pktfname[0] = 0;
      if ((status = str_cat_fmt_va(pktfname,
				   sizeof (pktfname),
				   "%s_%d.pkt",
				   without_slash,
				   i)) != 0)
	goto bad;
      fprintf(f,"%s\n",pktfname);
      i++;
    }
  fclose(f);
  return (0);
bad:
  fclose(f);
  return (status);
}

/* is an XtCallbackProc.
   Asks for a file name via a file selector then save a packets collection */
VOID_FUNC			bsb_save_collection(w,closure,ptr)
Widget				w;
XtPointer			closure;
XtPointer			ptr;
{
  t_status			status;
  char				*fname;

  if (fname = XmgFileGet("*.pkc",FALSE))
    {
      if ((status = xip_save_collection(fname)) != 0)
	XmgErrPrint(toplevel,status,"xip_save_collection %s",fname);
    }
}

/* is a XtCallbackProc.
   Popups the packet box panner */
VOID_FUNC			bsb_remote_control(w,closure,ptr)
Widget				w;
XtPointer			closure;
XtPointer			ptr;
{
  XtPopup(pannershell,XtGrabNone);
}

/* is an XtCallbackProc.
   Make the packet help sticky or not */
VOID_FUNC			bsb_pin_mode(w,closure,ptr)
Widget				w;
XtPointer			closure;
XtPointer			ptr;
{
  if (xip_resources.pinMode)
    {
      xip_resources.pinMode = FALSE;
      xphelpvisible = FALSE;
      XtPopdown(xphelpshell);
    }
  else
    xip_resources.pinMode = TRUE;
  XipSetCheck(pinmodebsb,xip_resources.pinMode);
}

/* is an XtCallbackProc.
   Make the packet help active or not */
VOID_FUNC			bsb_help_mode(w,closure,ptr)
Widget				w;
XtPointer			closure;
XtPointer			ptr;
{
  if (xip_resources.helpMode)
    {
      xip_resources.helpMode = FALSE;
      xphelpvisible = FALSE;
      XtPopdown(xphelpshell);
    }
  else
    {
      xip_resources.helpMode = TRUE;
    }
  XipSetCheck(helpmodebsb,xip_resources.helpMode);
}

/* is an XtCallbackProc.
   Set scroll mode for packet box. */
VOID_FUNC			bsb_scroll_mode(w,closure,ptr)
Widget				w;
XtPointer			closure;
XtPointer			ptr;
{
  if (xip_resources.scrollMode)
    {
      xip_resources.scrollMode = FALSE;
      xphelpvisible = FALSE;
      XtPopdown(xphelpshell);
    }
  else
    {
      xip_resources.scrollMode = TRUE;
    }
  XipSetCheck(scrollmodebsb,xip_resources.scrollMode);
}

char				*about_tmpl = 
"\n\
<html>\n\
<head>\n\
<title>Xipdump: about</title>\n\
</head>\n\
<body>\n\
<h1>About</h1>\n\
<hr>\n\
<br>\n\
<form>\n\
<table width=100%%>\n\
<tr>\n\
<td align=center>\n\
<img src=\"internal:xipicon.xbm\">\n\
</td>\n\
<td align=center>\n\
<table>\n\
<tr>\n\
<td align=center width=100%%>\n\
<b>\n\
%product% %version% %vendor%\n\
</b>\n\
</td>\n\
</tr>\n\
<tr>\n\
<td align=center width=100%%>\n\
<small>\n\
Report any problems to \n\
<a href=\"%mail_url%\">\n\
%mail_url%\n\
</a>.<br>\n\
Find additional documentation at <br>\n\
<a href=\"%http_url%\">\n\
%http_url%\n\
</a>.\n\
</small>\n\
</td>\n\
</tr>\n\
</table>\n\
</td>\n\
</tr>\n\
<tr>\n\
<td align=center width=100%%>\n\
<small>\n\
<li> Packets: %packets_treated% treated, %packets_dropped% dropped.\n\
<li> Pcap: %ps_recv% received, %ps_drop% dropped, \n\
%ps_ifdrop% dropped by interface.\n\
<li> Net: %net_if% %net_linktype%, offset %net_linkoffset%\n\
<li> Memory: %mem_in_use%b used, %mem_cumulated%b cumulated.\n\
</small>\n\
</td>\n\
</tr>\n\
</table>\n\
<br>\n\
<hr>\n\
<table width=100%%>\n\
<tr>\n\
<td align=center>\n\
<input type=submit value=\"Dismiss\">\n\
</td>\n\
</tr>\n\
</table>\n\
</form>\n\
</body>\n\
</html>\n\
\n\
";

/* is a t_tmpl_do_proc.
   It is used by XipShowAboutBox(3). */ 
t_status			about_format_do(bs,var,i)
t_bridled_str			*bs;
char				*var;
int				i;
{
  int				varlen;
  long				signedvalue;
  t_status			status;

  varlen = strlen(var);
  if (varlen == 0)
    return (str_cat_char(bs->str,bs->max_len,'%'));
  else
    {
      struct pcap_stat		ps;

      if (!strcmp(var,"product"))
	return (str_cat_str(bs->str,bs->max_len,PRODUCT));
      if (!strcmp(var,"version"))
	return (str_cat_str(bs->str,bs->max_len,VERSION));
      if (!strcmp(var,"vendor"))
	return (str_cat_str(bs->str,bs->max_len,VENDOR));
      if (!strcmp(var,"mail_url"))
	return (str_cat_str(bs->str,bs->max_len,MAIL_URL));
      if (!strcmp(var,"http_url"))
	return (str_cat_str(bs->str,bs->max_len,HTTP_URL));
      if (!strcmp(var,"packets_treated"))
	return (ulong_to_str((unsigned long)packets_treated,
			     pat_base,
			     bs->str,
			     bs->max_len));
      if (!strcmp(var,"packets_dropped"))
	return (ulong_to_str((unsigned long)packets_dropped,
			     pat_base,
			     bs->str,
			     bs->max_len));
      if (!strcmp(var,"mem_in_use"))
	return (ulong_to_str((unsigned long)gdm.in_use,
			     pat_base,
			     bs->str,
			     bs->max_len));
      if (!strcmp(var,"mem_cumulated"))
	return (ulong_to_str((unsigned long)gdm.cumulated,
			     pat_base,
			     bs->str,
			     bs->max_len));
      if (libnet)
	{
#ifdef NOTDEF
	  if (!strcmp(var,"net_if"))
	    return (str_cat_str(bs->str,
				bs->max_len,
				libnet->device));
#endif
	  if (!strcmp(var,"net_linktype"))
	    return (u32assoc_pat_extract((VOID_PTR)dlt_assocs,
					 (char *)&libnet->linktype,
					 sizeof (libnet->linktype),
					 bs->str,
					 bs->max_len));
	  if (!strcmp(var,"net_linkoffset"))
	    return (long_to_str((long)libnet->linkoffset,
				pat_base,
				bs->str,
				bs->max_len));
	}
      if (pcap_stats(pcap,&ps) < 0)
	return (0);
      if (!strcmp(var,"ps_recv"))
	return (ulong_to_str((unsigned long)ps.ps_recv,
			     pat_base,
			     bs->str,
			     bs->max_len));
      if (!strcmp(var,"ps_drop"))
	return (ulong_to_str((unsigned long)ps.ps_drop,
			     pat_base,
			     bs->str,
			     bs->max_len));
      if (!strcmp(var,"ps_ifdrop"))
	return (ulong_to_str((unsigned long)ps.ps_ifdrop,
			     pat_base,
			     bs->str,
			     bs->max_len));
    }
  return (0);
}

/* is an XtCallbackProc.
   Does "magic" things! */
VOID_FUNC			XipAboutFancyLink(w,xp,cbs)
Widget				w;
t_xip_pkt			*xp;
XmgFancyLinkCallbackStruct	*cbs;
{
  Window			win;

  if (win = XwsFindNetscapeWindow(XtDisplay(w),
				  DefaultRootWindow(XtDisplay(w)),
				  NULL,
				  XwsNetscapeWindowCompareFunc))
     {
       char			buf[STR_BUFSIZ];
       t_status			status;

       buf[0] = 0;
       if ((status = str_cat_fmt_va(buf,
				    sizeof (buf),
				    "OpenURL(%s)",
				    cbs->href)) != 0)
	 return ;
       XwsSendNavigatorCommand(XtDisplay(w),
			       win,
			       buf);
     }
}

/* is an XtCallbackProc.
   Popdowns about shell */
VOID_FUNC			XipAboutFancyForm(w,shell,cbs)
Widget				w;
Widget				shell;
XmgFancyFormCallbackStruct	*cbs;
{
  XtPopdown(shell);
}

/* is an XtCallbackProc.
   Set title of about shell */
VOID_FUNC			XipAboutFancyTitle(w,shell,cbs)
Widget				w;
Widget				shell;
XmgFancyTitleCallbackStruct	*cbs;
{
  XtVaSetValues(shell,
		XtNtitle,	cbs->title,
		XtNiconName,	cbs->title,
		NULL);
}

/* shows about, creates it if it doesn't exist */
VOID_FUNC			XipShowAboutBox(parent)
Widget				parent;
{
  char				html[BUFSIZ];
  t_status			status;

  if (!aboutshell)
    {
      INTERFACE(parent,
		GETCHILDSHELL(aboutshell)
		("xipAboutBox",transientShellWidgetClass,
		 XtNmappedWhenManaged,	False,
		 XtNtransientFor,	parent,
		 XtNallowShellResize,	True,
		 XtNiconPixmap,		xipicon_pixmap,
		 GETCHILD(aboutfancy)
		 ("aboutFancy",xmgFancyWidgetClass,
		  XtNdoCutAndPaste,	True,
		  CALLBACK(XtNlinkCallback,
			   (XtCallbackProc)XipAboutFancyLink,
			   aboutshell),
		  CALLBACK(XtNtitleCallback,
			   (XtCallbackProc)XipAboutFancyTitle,
			   aboutshell),
		  CALLBACK(XtNformCallback,
			   (XtCallbackProc)XipAboutFancyForm,
			   aboutshell),
		  CALLBACK(XtNimgCallback,
			   (XtCallbackProc)XipFancyImg,
			   aboutshell),
		  END),
		 MANAGE,
		 END),
		ENDINTERFACE);
      XmgSetPopdownOnDelete(aboutshell);
    }
  else
    {
      XmgFancyReset(aboutfancy);
    }
  html[0] = 0;
  if ((status = tmpl_str_to_str(about_tmpl,
				(t_tmpl_do_proc)about_format_do,
				NULL,
				html,
				sizeof (html))) != 0)
    return ;
  XmgFancyParseBuf(aboutfancy,
		   html,
		   strlen(html));
  XmgCenterWidget(aboutshell);
  XtPopup(aboutshell,XtGrabExclusive);
}

/* is an XtCallbackProc.
   Shows about */
VOID_FUNC			bsb_about(w,closure,ptr)
Widget				w;
XtPointer			closure;
XtPointer			ptr;
{
  XipShowAboutBox(toplevel);
}

t_boolean			move_pktbox_by_hand = FALSE;

/* is an XtCallbackProc.
   Moves the packet box */
VOID_FUNC			report_panner(w,closure,report)
Widget				w;
XtPointer			closure;
XawPannerReport			*report;
{
  move_pktbox_by_hand = TRUE;
  XtVaSetValues(pktbox,
		XtNx,		-report->slider_x,
		XtNy,		-report->slider_y,
		NULL);
  move_pktbox_by_hand = FALSE;
}

/* performs the action of moving "packet box" to a new value */
VOID_FUNC			move_pktbox(VOID_DECL)
{
  Dimension			width;
  Dimension			height;
  Dimension			port_width;
  Dimension			port_height;
  static t_boolean		selfex = FALSE;

  if (selfex)
    return ;
  XtVaGetValues(pktbox,
		XtNwidth,	&width,
		XtNheight,	&height,
		NULL);
  XtVaGetValues(porthole,
		XtNwidth,	&port_width,
		XtNheight,	&port_height,
		NULL);
  if (xip_resources.smoothScrollMode)
    {
      int			i;
      Position			x;
      Position			y;
      Position			final_x;
      Position			final_y;
      int			offset;

      selfex = TRUE;
      XtVaGetValues(pktbox,
		    XtNx,	&x,
		    XtNy,	&y,
		    NULL);
      
      final_x = -width + port_width;
      final_y = -height + port_height;
      if (x > final_x)
	{
	  i = x;
	  while (i > final_x)
	    {
	      
	      XtVaSetValues(pktbox,
			    XtNx,i,
			    NULL);
	      offset = (i - final_x) / 2;
	      i -= (offset < xip_resources.smoothScrollLimit)?
		xip_resources.smoothScrollOffset:offset;
	    }
	}
      if (y > final_y)
	{
	  i = y;
	  while (i > final_y)
	    {
	      XtVaSetValues(pktbox,
			    XtNy,i,
			    NULL);
	      offset = (i - final_y) / 2;
	      i -= (offset < xip_resources.smoothScrollLimit)?
		xip_resources.smoothScrollOffset:offset;
	    }
	}
      selfex = FALSE;
    }
  else
    {
      XtVaSetValues(pktbox,
		    XtNx,	-width + port_width,
		    XtNy,	-height + port_height,
		    NULL);
    }
}

/* is an XtCallbackProc.
   Resizes the packet box panner */
VOID_FUNC			report_porthole(w,panner,report)
Widget				w;
Widget				panner;
XawPannerReport			*report;
{
  if (!move_pktbox_by_hand && xip_resources.scrollMode)
    move_pktbox();
  XtVaSetValues(panner,
		XtNsliderX,	report->slider_x,
		XtNsliderY,	report->slider_y,
		NULL);
  if (report->changed != (XawPRSliderX | XawPRSliderY))
    {
      XtVaSetValues(panner,
		    XtNsliderWidth,	report->slider_width,
		    XtNsliderHeight,	report->slider_height,
		    XtNcanvasWidth,	report->canvas_width,
		    XtNcanvasHeight,	report->canvas_height,
		    NULL);
    }
}

/* is an XtActionProc.
   Popups packet help */
VOID_FUNC		XipActionPopupPktHelp(w,event,params,num_params)
Widget			w;
XEvent			*event;
String			*params;
Cardinal		*num_params;
{
  t_xip_pkt		*xp;
  t_status		status;
  Position		x;
  Position		y;
  Position		xroot;
  Position		yroot;
  Dimension		height;
  char			html[STR_BUFSIZ];

  if (!xip_resources.helpMode)
    return ;
  if (!xip_resources.pinMode)
    if (xphelpvisible)
      return ;
  xphelpvisible = TRUE;
  assert(event->type == EnterNotify);
  XtVaGetValues(w,
		XtNuserData,	&xp,
		NULL);
  XtVaGetValues(w,
		XtNx,		&x,
		XtNy,		&y,
		XtNheight,	&height,
		NULL);
  XtTranslateCoords(pktbox,
		    x,
		    y,
		    &xroot,
		    &yroot);
  XtVaSetValues(xphelpshell,
		XtNx,	xroot + xip_resources.pktHelpOffset,
		XtNy,	yroot + height + xip_resources.pktHelpOffset,
		NULL);
  html[0] = 0;
  if ((status = xip_shortcuts_html_do(xp->pkt,
				      html,
				      sizeof (html))) != 0)
    ;
  XmgFancyReset(xphelpfancy);
  XmgFancyParseBuf(xphelpfancy,html,strlen(html));
  XmgTallyWidgetWithScreen(xphelpshell);
  XtPopup(xphelpshell,XtGrabNone);
}

/* is an XtActionProc.
   Popdowns packet help */
VOID_FUNC		XipActionPopdownPktHelp(w,event,params,num_params)
Widget			w;
XEvent			*event;
String			*params;
Cardinal		*num_params;
{
  if (xip_resources.pinMode)
    return ;
  xphelpvisible = FALSE;
  XtPopdown(xphelpshell);
}

char			*xip_pkt_menu_table_name = NULL;
Widget			xip_pkt_menu_fancy = NULL;

VOID_FUNC		bsb_pkt_menu(w,keyword,cbs)
Widget			w;
char			*keyword;
VOID_PTR		cbs;
{
  char			str[STR_BUFSIZ];
  t_status		status;
  t_xip_method_data	xmd;
  t_xip_pkt		*xp;

  str[0] = 0;
  if (!strcmp(keyword,"extract") || !strcmp(keyword,"extract_new_window"))
    {
      if ((status = str_cat_fmt_va(str,
				   sizeof (str),
				   "extract(%s)",
				   xip_pkt_menu_table_name)) != 0)
	{
	  err_print(status,"str_cat_fmt_va");
	  return ;
	}
      xmd.new_window = !strcmp(keyword,"extract_new_window");
    }
  else
    if (!strcmp(keyword,"extract_to_pkt_box"))
      {
	if ((status = str_cat_fmt_va(str,
				     sizeof (str),
				     "extract_to_pkt_box(%s)",
				     xip_pkt_menu_table_name)) != 0)
	  {
	    err_print(status,"str_cat_fmt_va");
	    return ;
	  }
	xmd.new_window = TRUE;
      }
    else
      if (!strcmp(keyword,"trunc") || !strcmp(keyword,"trunc_new_window"))
	{
	  if ((status = str_cat_fmt_va(str,
				       sizeof (str),
				       "trunc(%s)",
				       xip_pkt_menu_table_name)) != 0)
	    {
	      err_print(status,"str_cat_fmt_va");
	      return ;
	    }
	  xmd.new_window = !strcmp(keyword,"trunc_new_window");
	}
      else
	if (!strcmp(keyword,"cut") || !strcmp(keyword,"fine_cut"))
	  {
	    if ((status = str_cat_fmt_va(str,
					 sizeof (str),
					 "cut(%s,%s)",
					 !strcmp(keyword,"cut")?"false":"true",
					 xip_pkt_menu_table_name)) != 0)
	      {
		err_print(status,"str_cat_fmt_va");
		return ;
	      }
	    xmd.new_window = FALSE;
	  }
	else
	  if (!strcmp(keyword,"paste") || !strcmp(keyword,"fine_paste"))
	    {
	      if ((status = str_cat_fmt_va(str,
					   sizeof (str),
					   "paste(%s,%s)",
				     !strcmp(keyword,"paste")?"false":"true",
					   xip_pkt_menu_table_name)) != 0)
		{
		  err_print(status,"str_cat_fmt_va");
		  return ;
		  }
	      xmd.new_window = FALSE;
	    }
	  else
	    {
	      err_print(ERR_XIP_INTERNAL,"no such keyword %s",keyword);
	      return ;
	    }
  XtVaGetValues(xip_pkt_menu_fancy,
		XtNuserData,	&xp,
		NULL);
  xmd.xp = xp;
  xip_method_parse(&xmd,str);
}

/* is an XtActionProc. */
VOID_FUNC		XipActionPopupPktMenu(w,event,params,num_params)
Widget			w;
XEvent			*event;
String			*params;
Cardinal		*num_params;
{
  static Widget		menu = NULL;
  static Widget		label;
  Position		x;
  Position		y;
  char			*name;
  Widget		fancy;
  char			labelstr[STR_BUFSIZ];
  t_off			length;
  t_status		status;
  t_xip_pkt		*xp;

  if (event->type != ButtonPress)
    {
      err_print(ERR_XIP_BAD_EVENT,"%d",event->type);
      return ;
    }
  if (!strcmp(XtName(w),"bigPktFancy") || !strcmp(XtName(w),"smallPktFancy"))
    fancy = w;
  else
    if (!(fancy = XtNameToWidget(w,"*bigPktFancy")))
      {
	if (!(fancy = XtNameToWidget(w,"*smallPktFancy")))
	  {
	    XmgWarning(
   "XipActionApplyMethodToPkt: don't find any bigPktFancy nor smallPktFancy");
	    return ;
	  }
      }
  if ((name = XmgFancyGetTableName(fancy,
				   event->xbutton.x,
				   event->xbutton.y)) == NULL)
    return ;
  if (!strcmp(name,""))
    return ;
  xip_pkt_menu_fancy = fancy;
  xip_pkt_menu_table_name = name;
  if (!menu)
    {
#define KEYWORD(Keyword) CHILD(Keyword,smeBSBObjectClass,\
		         CALLBACK(XtNcallback,\
				  bsb_pkt_menu,\
				  Keyword),\
			 END)

      INTERFACE(toplevel,
                GETCHILDSHELL(menu)
                ("menu",simpleMenuWidgetClass,
		 GETCHILD(label)
		 ("label",smeBSBMObjectClass,
		  XtNflipColors,	False,
		  END),
		 CHILD("line",smeLineObjectClass,
		       END),
		 KEYWORD("extract"),
		 KEYWORD("extract_new_window"),
		 KEYWORD("extract_to_pkt_box"),
		 KEYWORD("trunc"),
		 KEYWORD("trunc_new_window"),
		 KEYWORD("cut"),
		 KEYWORD("paste"),
		 KEYWORD("fine_cut"),
		 KEYWORD("fine_paste"),
		 END),
		REALIZE,
		ENDINTERFACE);
    }
  XtVaGetValues(fancy,
		XtNuserData,	&xp,
		NULL);
  labelstr[0] = 0;
  if ((status = pkt_pat_length(xp->pkt,
			       name,
			       &length)) != 0)
    {
      if ((status = str_cat_str(labelstr,
				sizeof (labelstr),
				name)) != 0)
	{
	  /* FALLTHROUGH */
	}
    }
  else
    {
      if ((status = str_cat_fmt_va(labelstr,
				   sizeof (labelstr),
				   "%s - %d byte%s",
				   name,
				   length,
				   (length > 1)?"s":"")) != 0)
	{
	  /* FALLTHROUGH */
	}
    }
  XtVaSetValues(label,
		XtNlabel,	labelstr,
		NULL);
  XtTranslateCoords(w,
                    event->xbutton.x,
                    event->xbutton.y,
                    &x,
                    &y);
  XtVaSetValues(menu,
                XtNx,   x,
                XtNy,   y,
                NULL);
  XmgTallyWidgetWithScreen(menu);
  XtPopupSpringLoaded(menu);
}

/* is an XtActionProc.
   Parse param as a packet method */
VOID_FUNC		XipActionApplyMethodToPkt(w,event,params,num_params)
Widget			w;
XEvent			*event;
String			*params;
Cardinal		*num_params;
{
  t_xip_pkt		*xp;
  t_status		status;
  Widget		fancy;
  t_xip_method_data	xmd;

  if (*num_params != 2)
    {
   XmgWarning("usage: XipApplyMethodToPkt(\"xipmethod(new_window,params))\"");
      return ;
    } 
  if (!strcmp(XtName(w),"bigPktFancy") || !strcmp(XtName(w),"smallPktFancy"))
    fancy = w;
  else
    if (!(fancy = XtNameToWidget(w,"*bigPktFancy")))
      {
	if (!(fancy = XtNameToWidget(w,"*smallPktFancy")))
	  {
	    XmgWarning(
   "XipActionApplyMethodToPkt: don't find any bigPktFancy nor smallPktFancy");
	    return ;
	  }
      }
  XtVaGetValues(fancy,
		XtNuserData,	&xp,
		NULL);
  xmd.xp = xp;
  if ((status = atobooleanstrict(params[0],&xmd.new_window)) != 0)
    {
      XmgWarning("XipActionApplyMethodToPkt: %s",err_msg(status));
      return ;
    }
  xip_method_parse(&xmd,params[1]);
}

/* is an XtActionProc.
   Moves packet box */
VOID_FUNC		XipActionMovePktBox(w,event,params,num_params)
Widget			w;
XEvent			*event;
String			*params;
Cardinal		*num_params;
{
  Position		x;
  Position		y;
  Position		xoff;
  Position		yoff;
  t_boolean		xsign;
  t_boolean		ysign;

  if (*num_params != 2)
    {
  XmgWarning("XipActionMovePktBox: usage: XipMovePktBox([+-]xoff,[+-]yoff)");
      return ;
    }
  XtVaGetValues(pktbox,
		XtNx,	&x,
		XtNy,	&y,
		NULL);
  xoff = atoi(params[0]);
  yoff = atoi(params[1]);
  xsign = str_find(params[0],"+-") != NULL;
  ysign = str_find(params[1],"+-") != NULL;
  if (xsign)
    x += xoff;
  else
    x = xoff;
  if (ysign)
    y += yoff;
  else
    y = yoff;
  move_pktbox_by_hand = TRUE;
  XtVaSetValues(pktbox,
		XtNx,	x,
		XtNy,	y,
		NULL);
  move_pktbox_by_hand = FALSE;
}

XtActionsRec            XipActions[] =
{
  {"XipPopupPktHelp",	XipActionPopupPktHelp},
  {"XipPopdownPktHelp",	XipActionPopdownPktHelp},
  {"XipApplyMethodToPkt",XipActionApplyMethodToPkt},
  {"XipPopupPktMenu",	XipActionPopupPktMenu},
  {"XipMovePktBox",	XipActionMovePktBox},
};

/* is a signal handler.
   Controls the flow and re-arms */
RETSIGTYPE		sigint(unused)
int			unused;
{
  capflow = !capflow;
  if (flowbsb)
    XipSetCheck(flowbsb,capflow);
  fprintf(stderr,"flow %s\n",capflow?"on":"off");
  signal(SIGINT,sigint);
}

/* sets the flow-control procedure.
   
   Warning: It modifies the SIGINT signal handler for the process. */
VOID_FUNC		handle_signals(VOID_DECL)
{
  signal(SIGINT,sigint);
}

t_status		make_hard_filter_from_trailing_args(argc,argv)
int			argc;
char			**argv;
{
  int			i;
  t_status		status;

  hard_filter[0] = 0;
  i = 1;
  while (i < argc)
    {
      if ((status = str_cat_str(hard_filter,
				sizeof (hard_filter),
				argv[i])) != 0)
	return (status);
      if ((status = str_cat_char(hard_filter,
				 sizeof (hard_filter),
				 ' ')) != 0)
	return (status);
      i++;
    }
  return (0);
}

VOID_FUNC		doit(argc,argv)
int			argc;
char			**argv;
{
  t_status		status;
  char			title[STR_BUFSIZ];
  Widget		panner;
  char			*home;
  char			*appclass;
  
  if (verbose)
    {
      fprintf(stderr,"\t%s %s %s\n",PRODUCT,VERSION,VENDOR);
#ifdef DEBUG
      fprintf(stderr,"\tcompiled with DEBUG\n");
#endif
#ifdef DEBUG_MALLOC
      fprintf(stderr,"\tcompiled with DEBUG_MALLOC\n");
#endif
    }
  if ((status = check_options()) != 0)
    {
      err_print(status,"check_options");
      exit(1);
    }
  handle_signals();
  if ((status = pat_init()) != 0)
    {
      err_print(status,"pat_init");
      exit(1);
    }
  if ((status = xip_vars_init()) != 0)
    {
      err_print(status,"xip_vars_init");
      exit(1);
    }
  if ((status = xip_cf_box_groups_init()) != 0)
    {
      err_print(status,"xip_cf_box_groups_init");
      exit(1);
    }
  if ((status = xip_pkt_cut_init()) != 0)
    {
      err_print(status,"xip_pkt_cut_init");
      exit(1);
    }
  if ((status = xip_methods_init()) != 0)
    {
      err_print(status,"xip_methods_init");
      exit(1);
    }
  if ((status = xip_pkt_bar_init()) != 0)
    {
      err_print(status,"xip_pkt_bar_init");
      exit(1);
    }
  if ((status = XmgMacroInit()) != 0)
    {
      err_print(status,"xt_macro_init");
      exit(1);
    }
  appclass = "Xipdump";
  toplevel = XmgMacroAppInitialize(&app_context,
				   appclass,
				   NULL,
				   0,
				   &argc,
				   argv,
				   fallback,
				   NULL);
  XmgInit();
  XtGetApplicationResources(toplevel,
			    &xip_resources,
			    resources,
			    num_resources,
			    NULL,
			    0);
  xipicon_pixmap = 
    XCreateBitmapFromData(XtDisplay(toplevel),
			  DefaultRootWindow(XtDisplay(toplevel)),
			  (char *)xipicon_bits,
			  xipicon_width,
			  xipicon_height);
  if ((status = XmgPixmapDictAdd("internal:xipicon.xbm",
				 1,
				 xipicon_pixmap)) != 0)
    {
      err_print(status,"XmgPixmapDictAdd");
      exit(1);
    }
  gray_pixmap = 
    XCreateBitmapFromData(XtDisplay(toplevel),
			  DefaultRootWindow(XtDisplay(toplevel)),
			  gray_bits,
			  gray_width,
			  gray_height);
  if ((status = XmgPixmapDictAdd("internal:gray.xbm",
				 1,
				 gray_pixmap)) != 0)
    {
      err_print(status,"XmgPixmapDictAdd");
      exit(1);
    }
  check_pixmap = 
    XCreateBitmapFromData(XtDisplay(toplevel),
			  DefaultRootWindow(XtDisplay(toplevel)),
			  check_bits,
			  check_width,
			  check_height);
  if ((status = XmgPixmapDictAdd("internal:check.xbm",
				 1,
				 check_pixmap)) != 0)
    {
      err_print(status,"XmgPixmapDictAdd");
      exit(1);
    }
  if ((status = xip_extra_vars_get_from_xrm()) != 0)
    {
      err_print(status,"xip_extra_vars_get_from_xrm");
      exit(1);
    }
  XtAppAddActions(app_context,XipActions,XtNumber(XipActions));
  XtRegisterGrabAction(XipActionPopupPktMenu,
                       True,
                       ButtonPressMask|ButtonReleaseMask,
                       GrabModeAsync,
                       GrabModeAsync);
  INTERFACE(toplevel,
	    CHILD("paned",panedWidgetClass,
		  CHILD("box",boxWidgetClass,
			XtNshowGrip,	False,
			XtNorientation,	XtorientHorizontal,
			CHILD("file",menuButtonWidgetClass,
			      CHILDSHELL("menu",simpleMenuWidgetClass,
					 XtNtranslations,
					 XtParseTranslationTable(
"<EnterWindow>:     XmgPopdownAllMenusAfterMe() highlight()  \n\
<LeaveWindow>:     unhighlight()           \n\
<BtnMotion>:       highlight()             \n\
<BtnUp>:           XmgPopdownAllMenus() notify() unhighlight()\n"),
					 CHILD("new_pkt",smeBSBObjectClass,
					       CALLBACK(XtNcallback,
							bsb_new_pkt,
							NULL),
					       END),
					 CHILD("smeline",smeLineObjectClass,
					       END),
					 CHILD("edit_config",smeBSBObjectClass,
					       CALLBACK(XtNcallback,
							bsb_edit_config,
							NULL),
					       END),
					 CHILD("save_config_as",
					       smeBSBObjectClass,
					       CALLBACK(XtNcallback,
							bsb_save_config_as,
							NULL),
					       END),
					 CHILD("smeline",smeLineObjectClass,
					       END),
				    CHILD("load_collection",smeBSBObjectClass,
					       CALLBACK(XtNcallback,
							bsb_load_collection,
							NULL),
					       END),
				   CHILD("save_collection",smeBSBObjectClass,
					       CALLBACK(XtNcallback,
							bsb_save_collection,
							NULL),
					       END),
					 CHILD("smeline",smeLineObjectClass,
					       END),
					 CHILD("clear",smeBSBObjectClass,
					       CALLBACK(XtNcallback,
							bsb_clear,
							NULL),
					       END),
					 CHILD("smeline",smeLineObjectClass,
					       END),
					 CHILD("quit",smeBSBMObjectClass,
					       XtNmenuName,	"quitmenu",
					       END),
					 END),
			      CHILDSHELL("quitmenu",simpleMenuWidgetClass,
					 XtNtranslations,
					 XtParseTranslationTable(
"<EnterWindow>:     XmgPopdownAllMenusAfterMe() highlight()  \n\
<LeaveWindow>:     unhighlight()           \n\
<BtnMotion>:       highlight()             \n\
<BtnUp>:	   notify() unhighlight()\n"),
					 CHILD("are_you_sure?",
					       smeBSBMObjectClass,
					       XtNmenuName,"quityesnomenu",
					       END),
					 END),
			      CHILDSHELL("quityesnomenu",simpleMenuWidgetClass,
					 XtNtranslations,
					 XtParseTranslationTable(
"<EnterWindow>:     XmgPopdownAllMenusAfterMe() highlight()  \n\
<LeaveWindow>:     unhighlight()           \n\
<BtnMotion>:       highlight()             \n\
<BtnUp>:	   notify() unhighlight()\n"),
					 CHILD("no",
					       smeBSBObjectClass,
					       CALLBACK(XtNcallback,
							bsb_nop,
							NULL),
					       END),
					 CHILD("yes",
					       smeBSBObjectClass,
					       CALLBACK(XtNcallback,
							bsb_quit,
							NULL),
					       END),
					 END),
			      END),
			CHILD("edit",menuButtonWidgetClass,
			      CHILDSHELL("menu",simpleMenuWidgetClass,
			      CHILD("empty_cut_buf",smeBSBObjectClass,
				 CALLBACK(XtNcallback,
					  bsb_empty_cut_buf,
					  NULL),
				 END),
			   CHILD("make_pkt_from_cut_buf",smeBSBObjectClass,
				 CALLBACK(XtNcallback,
					  bsb_make_pkt_from_cut_buf,
					  NULL),
				 END),
					 END),
			      END),
			CHILD("options",menuButtonWidgetClass,
			      CHILDSHELL("menu",simpleMenuWidgetClass,
				     GETCHILD(flowbsb)
					 ("flow",smeBSBObjectClass,
					  CALLBACK(XtNcallback,
						   bsb_flow,
						   NULL),
					  END),
					 CHILD("line",smeLineObjectClass,
					       END),
				     CHILD("remote_control",smeBSBObjectClass,
					       CALLBACK(XtNcallback,
							bsb_remote_control,
							NULL),
					   END),
					 CHILD("line",smeLineObjectClass,
					       END),
					 GETCHILD(helpmodebsb)
					 ("help_mode",smeBSBObjectClass,
					  CALLBACK(XtNcallback,
						   bsb_help_mode,
						   NULL),
					   END),
					 GETCHILD(pinmodebsb)
					 ("pin_mode",smeBSBObjectClass,
					  CALLBACK(XtNcallback,
						   bsb_pin_mode,
						   NULL),
					   END),
					 CHILD("line",smeLineObjectClass,
					       END),
					 GETCHILD(scrollmodebsb)
					 ("scroll_mode",smeBSBObjectClass,
					  CALLBACK(XtNcallback,
						   bsb_scroll_mode,
						   NULL),
					   END),
					 END),
			      END),
			CHILD("help",menuButtonWidgetClass,
			      CHILDSHELL("menu",simpleMenuWidgetClass,
				     CHILD("about",smeBSBObjectClass,
					       CALLBACK(XtNcallback,
							bsb_about,
							NULL),
					       END),
					 END),
			      END),
			END),
		  GETCHILD(porthole)
		  ("porthole",portholeWidgetClass,
		   GETCHILD(pktbox)
		   ("pktBox",boxWidgetClass,
		    END),
		   END),
		  END),
	    REALIZE,
	    ENDINTERFACE);
  XtInstallAccelerators(pktbox,pktbox);
  INTERFACE(toplevel,
	    GETCHILDSHELL(pannershell)
	    ("xipPannerShell",transientShellWidgetClass,
	     XtNmappedWhenManaged,	False,
	     XtNallowShellResize,	True,
	     XtNtransientFor,		toplevel,
	     XtNiconPixmap,		xipicon_pixmap,
	     GETCHILD(panner)
	     ("panner",pannerWidgetClass,
	      END),
	     MANAGE,
	     END),
	    ENDINTERFACE);
  XmgSetPopdownOnDelete(pannershell);
  XtAddCallback(porthole,
		XtNreportCallback,
		(XtCallbackProc)report_porthole,
		panner);
  XtAddCallback(panner,
		XtNreportCallback,
		(XtCallbackProc)report_panner,
		porthole);
  INTERFACE(toplevel,
	    GETCHILDSHELL(xphelpshell)
	    ("xipPktHelpShell",overrideShellWidgetClass,
	     XtNmappedWhenManaged,	False,
	     XtNallowShellResize,	True,
	     XtNtransientFor,		toplevel,
	     GETCHILD(xphelpfancy)
	     ("xipPktHelpFancy",xmgFancyWidgetClass,
	      END),
	     MANAGE,
	     END),
	    ENDINTERFACE);
  if ((status = make_hard_filter_from_trailing_args(argc,argv)) != 0)
    {
      err_print(status,"make_hard_filter_from_trailing_args");
      exit(1);
    }
  title[0] = 0;
  if ((status = str_cat_fmt_va(title,
			       sizeof (title),
			       "xipdump: \"%s\"",
			       hard_filter)) != 0)
    if (status != ERR_MG_BO)
      {
	err_print(status,"str_cat_fmt_va");
	exit(1);
      }
  XipSetCheck(flowbsb,capflow);
  XipSetCheck(pinmodebsb,xip_resources.pinMode);
  XipSetCheck(helpmodebsb,xip_resources.helpMode);
  XipSetCheck(scrollmodebsb,xip_resources.scrollMode);
  XtVaSetValues(toplevel,
		XtNtitle,	title,
		XtNiconPixmap,	xipicon_pixmap,
		NULL);
  if ((status = xip_cap_init()) != 0)
    {
      err_print(status,"xip_cat_init");
      exit(1);
    }
  if ((status = xip_shortcuts_init()) != 0)
    {
      err_print(status,"xip_shortcuts_init");
      exit(1);
    }
  if ((status = xip_net_init()) != 0)
    {
      err_print(status,"xip_net_init");
      exit(1);
    }
  xiprc[0] = 0;
  if ((home = getenv("HOME")) == NULL)
    home = ".";
  if ((status = str_cat_fmt_va(xiprc,
			       sizeof (xiprc),
			       "%s/.xipdumprc",
			       home)) != 0)
    {
      err_print(status,"str_cat_fmt_va");
      exit(1);
    }
  if (xip_cf)
    {
      if ((status = xip_cf_load(xip_cf)) != 0)
	{
	  err_print(status,"loading %s",xip_cf);
	  exit(1);
	}
    }
  else
    {
      if (access(xiprc,R_OK) == 0)
	{
	  if ((status = xip_cf_load(xiprc)) != 0)
	    {
	      err_print(status,"loading %s",xiprc);
	    }
	}
    }
  XtAppMainLoop(app_context);
}

int			main(argc,argv)
int			argc;
char			**argv;
{
  t_opt_context		oc;
  t_boolean		trargsbuf[256];
  t_boolean		troptsbuf[256];
  t_status              status;
  int			nmatch;

  assert(opt_check(main_opts,ARRAY_COUNT(main_opts)) == 0);
  if ((status = err_init()) != 0)
    {
      err_print(status,"err_init");
      exit(1);
    }
  if ((status = err_pat_init()) != 0)
    {
      err_print(status,"err_pat_init");
      exit(1);
    }
  if ((status = err_xmg_init()) != 0)
    {
      err_print(status,"err_xmg_init");
      exit(1);
    }
  if ((status = err_xip_init()) != 0)
    {
      err_print(status,"err_xip_init");
      exit(1);
    }
#ifdef NOTDEF
  id_str_show(err_id);
#endif
  oc.tr_args = trargsbuf;
  oc.nb_tr_args = sizeof (trargsbuf);
  oc.tr_opts = troptsbuf;
  oc.nb_tr_opts = sizeof (troptsbuf);
  oc.argc = &argc;
  oc.argv = argv;
  oc.opts = main_opts;
  oc.nb_opts = ARRAY_COUNT(main_opts);
  oc.base_addr = NULL;
  if ((nmatch = opt_get(&oc,&status)) < 0)
    {
      err_print(status,"couldn't get options");
      usage();
    }
  if (help)
    usage();
  gdm_init();
#ifdef DEBUG_MALLOC
  if (debug_malloc)
    {
      global_debug_malloc = TRUE;
      gdm.verbose = debug_malloc_verbose;
    }
#endif
#ifdef DEBUG
  a_debug_init();
  pat_debug_init();
  XmgDebugInit();
  xip_debug_init();
#endif
  doit(argc,argv);
}
