[comp.sources.sun] v01i078: A Sunview console message hander, v2.2, Part03/03

mcgrew@dartagnan.rutgers.edu (Charles Mcgrew) (10/20/89)

Submitted-by: chuck@trantor.harris-atd.com (Chuck Musciano)
Posting-number: Volume 1, Issue 78
Archive-name: contool2.2/part03

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 3 (of 3)."
# Contents:  contool.c contool.man dialog.c
# Wrapped by chuck@melmac on Wed Sep 20 09:46:42 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'contool.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'contool.c'\"
else
echo shar: Extracting \"'contool.c'\" \(18607 characters\)
sed "s/^X//" >'contool.c' <<'END_OF_FILE'
X/************************************************************************/
X/*	Copyright 1988, 1989 by Chuck Musciano and Harris Corporation	*/
X/*									*/
X/*	Permission to use, copy, modify, and distribute this software	*/
X/*	and its documentation for any purpose and without fee is	*/
X/*	hereby granted, provided that the above copyright notice	*/
X/*	appear in all copies and that both that copyright notice and	*/
X/*	this permission notice appear in supporting documentation, and	*/
X/*	that the name of Chuck Musciano and Harris Corporation not be	*/
X/*	used in advertising or publicity pertaining to distribution	*/
X/*	of the software without specific, written prior permission.	*/
X/*	Chuck Musciano and Harris Corporation make no representations	*/
X/*	about the suitability of this software for any purpose.  It is	*/
X/*	provided "as is" without express or implied warranty.  This 	*/
X/*	software may not be sold without the prior explicit permission	*/
X/*	of Harris Corporation.						*/
X/************************************************************************/
X
X#include	<stdio.h>
X#include	<fcntl.h>
X#include	<sys/ioctl.h>
X#include	<sys/file.h>
X#include	<sys/types.h>
X#include	<sys/stat.h>
X
X#include	<suntool/sunview.h>
X#include	<suntool/textsw.h>
X#include	<suntool/icon_load.h>
X
X#include	"contool.h"
X
XEXPORT	char	filter_path[256];		/* -c */
XEXPORT	int	blink_icon = TRUE;		/* -i */
XEXPORT	int	pop_open = FALSE;		/* -p */
XEXPORT	int	beep_amount = BEEP_COUNT;	/* -s */
XEXPORT	int	do_time_stamp = TRUE;
XEXPORT	f_ptr	filters = NULL;
XEXPORT	f_ptr	curr_filter = NULL;
X
XPUBLIC	Frame	confirmer;
X
XPRIVATE	char	*ct_usage = "usage: contool [-b <file>] [-c <file>] [-d <size>] [-f <file>] [-g <file>] [-l <size>] [-n] [-o <logfile>] [-p] [-r <amt>] [-s <amt>]\n";
X
XPRIVATE	Frame	bf;
XPRIVATE	Frame	dialog = NULL;
XPRIVATE	Textsw	text;
XPRIVATE	Icon	good, bad, inverse;
XPRIVATE	struct	pixrect	*good_pr, *bad_pr, *inv_pr;
XPRIVATE	Menu_item	stop_blink;
XPRIVATE	Rect	open_rect;
X
XPRIVATE	char	bad_icon[512];			/* -b */
XPRIVATE	int	delete_amt = TEXT_DELETE_SIZE;	/* -d */
XPRIVATE	char	inv_icon[512];			/* -f */
XPRIVATE	char	good_icon[512];			/* -g */
XPRIVATE	int	size_limit = TEXT_SIZE_LIMIT;	/* -l */
XPRIVATE	int	resolution = TS_INTERVAL;	/* -r */
X
XPRIVATE	int	bad_is_up;
XPRIVATE	int	beep_count;
XPRIVATE	int	blinking = FALSE;
XPRIVATE	int	event_in_progress = FALSE;
XPRIVATE	int	explicit_filters = FALSE;
XPRIVATE	int	icon_height;
XPRIVATE	int	icon_width;
XPRIVATE	FILE	*master = NULL;
XPRIVATE	int	old_time = 0;
XPRIVATE	char	*program;
XPRIVATE	FILE	*slave = NULL;
XPRIVATE	FILE	*logfile = NULL;
X
XPRIVATE	struct	itimerval	timer = {{0, 500000}, {0, 500000}};
X
X/************************************************************************/
X/*	First, some basic console utility routines 			*/
X/************************************************************************/
X
X/************************************************************************/
XPRIVATE	acquire_console(path)
X
Xchar	*path;
X
X{
X	if (ioctl(fileno(slave), TIOCCONS, NULL) == -1) {
X	   fprintf(stderr, "%s: could not attach %s to /dev/console\n", program, path);
X	   exit(1);
X	   }
X}
X
X/************************************************************************/
XPRIVATE	clear_messages()
X
X{
X	textsw_reset(text, 0, 0);
X	old_time = 0;
X}
X
X/************************************************************************/
XPRIVATE	stop_blinking()
X
X{
X	notify_set_itimer_func(bf, NULL, ITIMER_REAL, NULL, NULL);
X	window_set(bf, FRAME_ICON, good, 0);
X	blinking = FALSE;
X	menu_set(stop_blink, MENU_INACTIVE, TRUE, 0);
X}
X
X/************************************************************************/
XPRIVATE	edit_filters()
X
X{	Frame	create_dialog_box();
X
X	if (dialog == NULL)
X	   dialog = create_dialog_box(bf);
X	update_edit_dialog(filters);
X	update_defaults();
X	window_set(dialog, WIN_SHOW, TRUE, 0);
X}
X
X/************************************************************************/
X/*	Now, filter and regular expression handlers			*/
X/************************************************************************/
X
X/************************************************************************/
XPRIVATE	internal_message(a, b, c, d, e, f)
X
Xint	a, b, c, d, e, f;
X
X{	char	buf[512];
X
X	sprintf(buf, a, b, c, d, e, f);
X	time_stamp();
X	write_log(buf);
X	do_insertion(buf, strlen(buf));
X}
X
X/************************************************************************/
XPRIVATE	internal_error(a, b, c, d, e, f)
X
Xint	a, b, c, d, e, f;
X
X{	char	buf[512];
X
X	sprintf(buf, a, b, c, d, e, f);
X	time_stamp();
X	fprintf(stderr, "*** %s: %s\n", program, buf);
X}
X
X/************************************************************************/
XPRIVATE	load_filters()
X
X{	char	*result;
X	struct	stat	sb;
X	f_ptr	new_filters, f;
X	static	int	load_time = 0;
X
X	if (access(filter_path, R_OK) == -1) {
X	   if (explicit_filters && load_time == 0) {
X	      internal_error("filter file %s cannot be accessed", filter_path);
X	      load_time = 1;
X	      }
X	   return;
X	   }
X	if (stat(filter_path, &sb) == 0 && sb.st_mtime > load_time)
X	   if (new_filters = read_filters(filter_path, internal_error)) {
X	      for (f = new_filters; f->start; f++)
X	         if (result = compile_exp(f, f->start, f->end)) {
X	            internal_error(result);
X	            f->valid = FALSE;
X	            }
X	      free_filters(filters);
X	      filters = new_filters;
X	      internal_message("*** filters loaded from %s\n", filter_path);
X	      load_time = sb.st_mtime;
X	      }
X}
X
X/************************************************************************/
X/*	Various event handlers for the console frame			*/
X/************************************************************************/
X
X/************************************************************************/
XPRIVATE	Notify_value	blink_proc(me, which)
X
Xint	*me;
Xint	which;
X
X{
X	if (event_in_progress)
X	   return(NOTIFY_DONE);
X	if (beep_count > 0) {
X	   window_bell(bf);
X	   beep_count--;
X	   }
X	if (blinking) {
X	   if (bad_is_up)
X	      window_set(bf, FRAME_ICON, inverse, 0);
X	   else
X	      window_set(bf, FRAME_ICON, bad, 0);
X	   bad_is_up = !bad_is_up;
X	   }
X	if (beep_count == 0 && !blinking)
X	   notify_set_itimer_func(bf, blink_proc, ITIMER_REAL, NULL, NULL);
X	return(NOTIFY_DONE);
X}
X
X/************************************************************************/
XPRIVATE	Notify_value	close_proc(frame, event, arg, type)
X
XFrame	frame;
XEvent	*event;
XNotify_arg	arg;
XNotify_event_type	type;
X
X{	int	init_closed, curr_closed, is_resize;
X	Notify_value	value;
X	Rect	*temp;
X
X	event_in_progress = TRUE;
X	init_closed = (int) window_get(frame, FRAME_CLOSED);
X	is_resize = (event_id(event) == WIN_RESIZE);
X	value = notify_next_event_func(frame, event, arg, type);
X	curr_closed = (int) window_get(frame, FRAME_CLOSED);
X	if (init_closed != curr_closed)
X	   if (!curr_closed && blinking) {
X	      notify_set_itimer_func(bf, blink_proc, ITIMER_REAL, NULL, NULL);
X	      window_set(bf, FRAME_ICON, good, 0);
X	      blinking = FALSE;
X	      menu_set(stop_blink, MENU_INACTIVE, TRUE, 0);
X	      }
X	event_in_progress = FALSE;
X	if (is_resize) {
X	   temp = (Rect *) window_get(frame, FRAME_OPEN_RECT);
X	   if (temp->r_width <= icon_width && temp->r_height <= icon_height) { /* override spurious resize request */
X	      window_set(frame, FRAME_OPEN_RECT, &open_rect, FRAME_CLOSED, FALSE, 0);
X	      notify_set_itimer_func(bf, blink_proc, ITIMER_REAL, NULL, NULL);
X	      window_set(bf, FRAME_ICON, good, 0);
X	      blinking = FALSE;
X	      menu_set(stop_blink, MENU_INACTIVE, TRUE, 0);
X	      }
X	   else /* save away new open rect */
X	      open_rect = *temp;
X	   }
X	return(value);
X}
X
X/************************************************************************/
XPRIVATE	Notify_value	destroy_proc(frame, status)
X
XFrame	frame;
XDestroy_status	status;
X
X{
X	if (status == DESTROY_CHECKING) {
X	   if (dialog)
X	      window_destroy(dialog);
X	   if (confirmer)
X	      window_destroy(confirmer);
X	   textsw_reset(text, 0, 0);
X	   return(NOTIFY_DONE);
X	   }
X	else
X	   return(notify_next_destroy_func(frame, status));
X}
X
X/************************************************************************/
X/*	Routines which handle capturing and displaying messages		*/
X/************************************************************************/
X
X/************************************************************************/
XPRIVATE	write_log(s)
X
Xchar	*s;
X
X{	int	t;
X	static	char	hostname[100] = "";
X
X	if (logfile) {
X	   if (*hostname == NULL) 
X	      if (gethostname(hostname, 99) != 0)
X	         strcpy(hostname, "(unknown)");
X	   t = time(0);
X	   fseek(logfile, 0L, 2);
X	   fprintf(logfile, "%s\t%.16s\t%s", hostname, ctime(&t) + 4, s);
X	   fflush(logfile);
X	   }
X}
X
X/************************************************************************/
XPRIVATE	do_insertion(buf, len)
X
Xchar	*buf;
Xint	len;
X
X{	int	first, last;
X
X	while (len > size_limit - ((int) window_get(text, TEXTSW_LENGTH) - TEXT_SIZE_FUZZ)) { /* make some room */
X	   first = 1;
X	   last = TEXTSW_INFINITY;
X	   if (textsw_find_bytes(text, &first, &last, "\n<<<", 4, 0) == -1)
X	      if (textsw_find_bytes(text, &first, &last, "\n", 1, 0) == -1)
X	         first = delete_amt;
X	   textsw_delete(text, 0, first + 1);
X	   }
X	window_set(text, TEXTSW_INSERTION_POINT, TEXTSW_INFINITY, 0);
X	textsw_insert(text, buf, len);
X}
X
X/************************************************************************/
XPRIVATE	time_stamp()
X
X{	int	t, pos;
X	char	buf[5];
X
X	   t = time(0);
X	   if (t - old_time >= resolution) {
X	      window_set(text, TEXTSW_INSERTION_POINT, TEXTSW_INFINITY, 0);
X	      pos = (int) window_get(text, TEXTSW_LENGTH);
X	      if (pos != 0) {
X	         window_get(text, TEXTSW_CONTENTS, pos - 1, buf, 1);
X	         if (buf[0] != '\n')
X	            do_insertion("\n", 1);
X	         }
X	      do_insertion("\n<<< ", 5);
X	      do_insertion(ctime(&t), 24);
X	      do_insertion(" >>>\n", 5);
X	      old_time = t;
X	      }
X}
X
X/************************************************************************/
XPRIVATE	Notify_value	input_func(me, fd)
X
Xint	*me;
Xint	fd;
X
X{	char	old_c, *s, *t;
X	f_ptr	f;
X	int	count, do_blink = FALSE, do_open = FALSE;
X	static	char	in_buf[INPUT_BUFFER_SIZE + 2];
X
X	while ((count = read(fileno(master), in_buf, INPUT_BUFFER_SIZE)) >= 0) {
X	   in_buf[count] = '\0';
X	   while (s = index(in_buf, '\015')) {
X	      strcpy(s, s + 1);
X	      count--;
X	      }
X	   for (t = in_buf; *t; *s = old_c, t = s) {
X	      if (s = index(t, '\n')) {
X	         old_c = *++s;
X	         *s = '\0';
X	         }
X	      else {
X	         s = t + strlen(t);
X	         old_c = '\0';
X	         }
X	      if (curr_filter == NULL) {
X	         load_filters();
X	         for (f = filters; f && f->start; f++)
X	            if (f->valid && match_exp(f->start_re, f->scircf, t)) {
X	               if (f->save) {
X	                  do_blink = f->flash;
X	                  beep_count = f->beep;
X	                  do_open = f->open;
X	                  if (f->stamp)
X	                     time_stamp();
X			  write_log(t);
X	                  do_insertion(t, strlen(t));
X	                  }
X	               if (f->end) {
X	                  curr_filter = f;
X	                  }
X	               break;
X	               }
X	         if (f == NULL || f->start == NULL) {
X	            if (do_time_stamp)
X	               time_stamp();
X		    write_log(t);
X	            do_insertion(t, strlen(t));
X	            do_blink = blink_icon;
X	            do_open = pop_open;
X	            beep_count = beep_amount;
X	            }
X	         }
X	      else {
X	         if (curr_filter->save) {
X	            if (curr_filter->stamp)
X	               time_stamp();
X		    write_log(t);
X		    do_insertion(t, strlen(t));
X	            }
X	         if (match_exp(curr_filter->end_re, curr_filter->ecircf, t))
X	            curr_filter = NULL;
X	         }
X	      }
X	   }
X	window_set(text, TEXTSW_UPDATE_SCROLLBAR, 0);
X	if (do_open)
X	   window_set(bf, FRAME_CLOSED, FALSE, 0);
X	if (do_blink)
X	   if (window_get(bf, FRAME_CLOSED) && !blinking) {
X	      window_set(bf, FRAME_ICON, bad, WIN_SHOW, TRUE, 0);
X	      blinking = TRUE;
X	      bad_is_up = TRUE;
X	      menu_set(stop_blink, MENU_INACTIVE, FALSE, 0);
X	      notify_set_itimer_func(bf, blink_proc, ITIMER_REAL, &timer, NULL);
X	      }
X	if (beep_count > 0 || blinking)
X	   notify_set_itimer_func(bf, blink_proc, ITIMER_REAL, &timer, NULL);
X	return(NOTIFY_DONE);
X}
X
X/************************************************************************/
X/*	Routines which parse options, create windows, and main()	*/
X/************************************************************************/
X
X/************************************************************************/
XPRIVATE	parse_options(argc, argv)
X
Xint	*argc;
Xchar	**argv;
X
X{	char	*s, c;
X
X	strcpy(good_icon, ICON_DIRECTORY);
X	strcat(good_icon, GOOD_ICON);
X	strcpy(bad_icon, ICON_DIRECTORY);
X	strcat(bad_icon, BAD_ICON);
X	strcpy(inv_icon, ICON_DIRECTORY);
X	strcat(inv_icon, INVERSE_ICON);
X
X	strcpy(filter_path, getenv("HOME"));
X	strcat(filter_path, "/.contool");
X
X	while ((c = getopt(argc, argv, "b:c:d:f:g:l:no:pr:s:?", &s)) != EOF)
X	   switch (c) {
X	      case 'b' : strcpy(bad_icon, s);
X	      		 break;
X	      case 'c' : strcpy(filter_path, s);
X	      		 explicit_filters = TRUE;
X	      		 break;
X	      case 'd' : if (verify(s, "0123456789"))
X	      		    delete_amt = atoi(s);
X	      		 else {
X	      		    fprintf(stderr, "%s: invalid delete amount: %s\n", program, s);
X	      		    exit(1);
X	      		    }
X	      		 break;
X	      case 'f' : strcpy(inv_icon, s);
X	      		 break;
X	      case 'g' : strcpy(good_icon, s);
X	      		 break;
X	      case 'l' : if (verify(s, "0123456789"))
X	      		    window_set(text, TEXTSW_MEMORY_MAXIMUM, (size_limit = atoi(s)) + TEXT_SIZE_FUZZ, 0);
X	      		 else {
X	      		    fprintf(stderr, "%s: invalid message limit: %s\n", program, s);
X	      		    exit(1);
X	      		    }
X	      		 break;
X	      case 'n' : blink_icon = FALSE;
X	      		 break;
X	      case 'o':  if ((logfile = fopen(s, "a")) == NULL) {
X		   	    fprintf(stderr, "%s : can't open logfile: %s\n", program, s);
X			    exit(1);
X		         }
X		         break;
X	      case 'p' : pop_open = TRUE;
X	      		 break;
X	      case 'r' : if (verify(s, "0123456789"))
X	      		    resolution = atoi(s);
X	      		 else {
X	      		    fprintf(stderr, "%s: invalid timestamp resolution: %s\n", program, s);
X	      		    exit(1);
X	      		    }
X	      		 break;
X	      case 's' : if (verify(s, "0123456789"))
X	      		    beep_amount = atoi(s);
X	      		 else {
X	      		    fprintf(stderr, "%s: invalid beep count: %s\n", program, s);
X	      		    exit(1);
X	      		    }
X	      		 break;
X	      case '?' : fprintf(stderr, ct_usage);
X	      		 exit(0);
X	      		 break;
X	      default  : fprintf(stderr, ct_usage);
X	                 exit(1);
X	      }
X}
X
X/************************************************************************/
Xstruct	pixrect	*load_icon(path, message)
X
Xchar	*path;
Xchar	*message;
X
X{	char	new_path[512], *real_path;
X
X	if (access(path, R_OK) == -1) {
X	   strcpy(new_path, ICON_DIRECTORY);
X	   strcat(new_path, path);
X	   if (access(new_path, R_OK) == -1) {
X	      sprintf(message, "cannot read icon file %s", path);
X	      return(NULL);
X	      }
X	   real_path = new_path;
X	   }
X	else
X	   real_path = path;
X	return(icon_load_mpr(real_path, message));
X}
X
X/************************************************************************/
XPRIVATE	load_icons()
X
X{	char	msg[IL_ERRORMSG_SIZE];
X
X	if ((good_pr = load_icon(good_icon, msg)) == NULL) {
X	   fprintf(stderr, "%s: %s\n", program, msg);
X	   exit(1);
X	   }
X	good = icon_create(ICON_IMAGE, good_pr,
X			   ICON_LABEL, "",
X			   ICON_WIDTH, good_pr->pr_size.x,
X			   ICON_HEIGHT, good_pr->pr_size.y,
X			   0);
X	icon_width = good_pr->pr_size.x;
X	icon_height = good_pr->pr_size.y;
X	if ((bad_pr = load_icon(bad_icon, msg)) == NULL) {
X	   fprintf(stderr, "%s: %s\n", program, msg);
X	   exit(1);
X	   }
X	bad = icon_create(ICON_IMAGE, bad_pr,
X			  ICON_LABEL, "",
X			  ICON_WIDTH, bad_pr->pr_size.x,
X			  ICON_HEIGHT, bad_pr->pr_size.y,
X			  0);
X	if (bad_pr->pr_size.x > icon_width)
X	   icon_width = bad_pr->pr_size.x;
X	if (bad_pr->pr_size.y > icon_height)
X	   icon_height = bad_pr->pr_size.y;
X	if (*inv_icon == '\0')
X	   strcpy(inv_icon, bad_icon);
X	if ((inv_pr = load_icon(inv_icon, msg)) == NULL) {
X	   fprintf(stderr, "%s: %s\n", program, msg);
X	   exit(1);
X	   }
X	inverse = icon_create(ICON_IMAGE, inv_pr,
X			      ICON_LABEL, "",
X			      ICON_WIDTH, inv_pr->pr_size.x,
X			      ICON_HEIGHT, inv_pr->pr_size.y,
X			      0);
X	if (inv_pr->pr_size.x > icon_width)
X	   icon_width = inv_pr->pr_size.x;
X	if (inv_pr->pr_size.y > icon_height)
X	   icon_height = inv_pr->pr_size.y;
X	window_set(bf, FRAME_ICON, good, 0);
X}
X
X/************************************************************************/
Xmain(argc, argv)
X
Xint	argc;
Xchar	**argv;
X
X{	char	*path;
X	int	i;
X	Menu	menu;
X
X	program = strsave(argv[0]);
X
X	bf = window_create(NULL, FRAME, 
X			      FRAME_ARGC_PTR_ARGV, &argc, argv,
X			      FRAME_LABEL, TOOL_LABEL,
X			   0);
X	text = window_create(bf, TEXTSW, 
X			        TEXTSW_DISABLE_CD, TRUE,
X			        TEXTSW_DISABLE_LOAD, TRUE, 
X			        TEXTSW_AGAIN_RECORDING, FALSE,
X			        TEXTSW_IGNORE_LIMIT, TEXTSW_INFINITY,
X			        TEXTSW_HISTORY_LIMIT, 0,
X			        TEXTSW_MEMORY_MAXIMUM, size_limit + TEXT_SIZE_FUZZ,
X			     0);
X	open_rect = *((Rect *) window_get(bf, FRAME_OPEN_RECT));
X
X	argv = saveargs(argc, argv);
X	parse_options(&argc, argv);
X	if (argc != 1) {
X	   fprintf(stderr, ct_usage);
X	   exit(1);
X	   }
X
X	load_icons();
X
X	path = open_psuedo_tty(&master, "r", &slave, "w");
X	if (master == NULL) {
X	   fprintf(stderr, "%s: couldn't open any psuedo-tty\n");
X	   exit(1);
X	   }
X	if (slave == NULL) {
X	   fprintf(stderr, "%s: couldn't open slave side of %s\n", program, path);
X	   exit(1);
X	   }
X
X	i = fcntl(fileno(master), F_GETFL, 0);
X	i |= FNDELAY;
X	if (fcntl(fileno(master), F_SETFL, i) == -1) {
X	   fprintf(stderr, "%s: could not force %s to non-blocking i/o\n", program, path);
X	   exit(1);
X	   }
X
X	acquire_console(path);
X
X	stop_blink = menu_create_item(MENU_STRING, "Stop Blinking",
X				      MENU_INACTIVE, TRUE,
X				      MENU_ACTION_PROC, stop_blinking,
X				      0);
X	menu = menu_create(MENU_APPEND_ITEM, stop_blink,
X			   MENU_ACTION_ITEM, "Become Console", acquire_console,
X			   MENU_ACTION_ITEM, "Clear Messages", clear_messages,
X			   MENU_ACTION_ITEM, "Edit Filters", edit_filters,
X			   MENU_PULLRIGHT_ITEM, "Frame", window_get(bf, WIN_MENU),
X			   0);
X	window_set(bf, WIN_MENU, menu, 0);
X
X	notify_set_input_func(bf, input_func, fileno(master));
X	notify_interpose_destroy_func(bf, destroy_proc);
X	notify_interpose_event_func(bf, close_proc, NOTIFY_SAFE);
X
X	load_filters();
X
X	window_main_loop(bf);
X}
END_OF_FILE
if test 18607 -ne `wc -c <'contool.c'`; then
    echo shar: \"'contool.c'\" unpacked with wrong size!
fi
# end of 'contool.c'
fi
if test -f 'contool.man' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'contool.man'\"
else
echo shar: Extracting \"'contool.man'\" \(14027 characters\)
sed "s/^X//" >'contool.man' <<'END_OF_FILE'
X.TH CONTOOL 1 "10 December 1986"
X.SH NAME
Xcontool \- capture and display console output
X.SH SYNOPSIS
Xcontool [\fB\(hyb\fP \fIfile\fP] [\fB\(hyc\fP \fIfile\fP] [\fB\(hyd\fP \fIsize\fP] [\fB\(hyf\fP \fIfile\fP] [\fB\(hyg\fP \fIfile\fP] [\fB\(hyl\fP \fIsize\fP] [\f3\(hyn\fP] [\f3\(hyo\fP \f2logfile\fP] [\f3\(hyp\fP] [\f3\(hyr\fP \f2amount\fP] [\f3\(hys\fP \f2count\fP] 
X.SH DESCRIPTION
X.LP
X\f2Contool\fP captures and displays any messages sent to the system console.
XEach message is timestamped as it arrives.  The messages are displayed in a
Xscrolling text window, so the user can scroll through old messages.
X.LP
XWhen a message arrives, \f3contool\fP will beep and, if closed, begin
Xblinking its icon until the user opens the tool.  This behavior can be changed
Xwith the various options, described below.
X.LP
X\f2Contool\fP must be run under \f2suntools\fP(1), and accepts the
Xstandard window command line options.
X.SH OPTIONS
X.IP "\fB\\(hyb\fP \fIfile\fP"
Xspecifies the \*(lqbad\*(rq icon to be displayed when a message
Xhas arrived on the console.  The \f2file\fP must be in the format used by
X\f2iconedit\fP(1).
X.IP "\fB\\(hyc\fP \fIfile\fP"
Xspecifies a different filter file.  If \fB\(hyc\fP is not used, \fIcontool\fP
Xwill read filters from ~/.contool, if it exists.
X.IP "\fB\\(hyd\fP \fIsize\fP"
Xsets the amount of text that will be removed from the front of the message
Xlog when the message size limit (see \fB\(hyl\fP, below) is exceeded.  At
Xleast \fIsize\fP bytes will be removed, along with any text up to the start
Xof the next message.  The default value is 1024 bytes.
X.IP "\fB\\(hyf\fP \fIfile\fP"
Xspecifies the \*(lqflash\*(rq icon which is alternated with the
Xbad icon (see \f3\(hyb\fP, above) when a message has arrived on the console.
XLike \f3\(hyb\fP, the file must be in the format used by \f2iconedit\fP(1).
XTo disable the flashing feature, specify \*(lq\*(rq as the \f2file\fP, or use the 
X\f3\(hyn\fP option, below.
X.IP "\fB\\(hyg\fP \fIfile\fP"
Xspecifies the \*(lqgood\*(rq icon which is displayed when
Xno unviewed messages are present on the console.  This icon is displayed
Xwhen the user closes \f2contool\fP, and remains displayed until a new message
Xarrives.  Like \f3\(hyb\fP and \f3\(hyf\fP, the \f2file\fP must be in the
Xformat used by \f2iconedit\fP(1).
X.IP "\fB\\(hyl\fP \fIsize\fP"
Xsets the limit, in bytes, on the number of messages that will be saved.
XWhen a message would exceed this limit, some number of bytes of text (see 
X\fB\(hyd\fP, above) will be deleted from the start of the message log.  The
Xdefault value is 32768 bytes.
X.IP "\fB\\(hyn\fP \fIfile\fP"
Xdisables icon flashing when a message arrives.
X.IP "\fB\\(hyo\fP \fIfile\fP"
Xinstructs contool to keep a log of all messages (that are accepted by
Xthe filters) into the named \fIfile\fP.  This is useful for logging the
Xconsole messages from a network of workstations into files on a
Xserver, making administration of such a network much simpler.  Care
Xshould be exercised in logging multiple machines to the one file; NFS
Xsometimes doesn't append if concurrent updates are done.  This may be
Xan NFS bug.
X.IP "\f3\(hyp\fP"
Xcauses contool to pop open when a message arrives.  By default,
Xcontool stays closed and blinks when messages arrive.
X.IP "\f3\(hyr\fP \f2amount\fP"
Xcontrols the resolution of the timestamps placed in the 
Xmessage display.  Normally, a message is not timestamped if it has arrived
Xwithin sixty seconds of the last timestamp.  This prevents a cascade
Xof messages from receiving several, identical timestamps.  If this option
Xis specified, the \f2amount\fP indicates the time, in seconds, to allow
Xbetween timestamps.
X.IP "\f3\(hys\fP \f2count\fP"
Xchanges the number of times \f2contool\fP will sound the bell
Xwhen a message arrives.  To disable the bell, set the \f2count\fP to zero.
X.SH ROOT MENU
X.LP
X\f2Contool\fP replaces the normal frame root menu with a new menu containing
Xfive entries.  These entries are
X.IP "\f2Stop Blinking\fP"
XIf \f2contool\fP is blinking its icon, this entry will be enabled.  If selected,
Xthe blinking will stop.  Blinking is also stopped if the user opens the \f2contool\fP window.
X.IP "\f2Become Console\fP"
XSunOS only allows one psuedo-terminal to have the console attribute.  If some other
Xprogram (including another invocation of \f2contool\fP) takes this attribute from the 
Xpsuedo-terminal \f2contool\fP is using, subsequent console messages will not be
Xprocessed by \f2contool\fP.  Selecting this menu entry will restore the console
Xattribute to the psuedo-terminal \f2contool\fP is using.
X.IP "\f2Clear Messages\fP"
XAny messages currently held in \f2contool\fP's text window are removed.
X.IP "\f2Edit Filters\fP"
XThis selection brings up a dialog box which allows the user to modify the 
Xfilters \f2contool\fP uses to process incoming console messages.  See EDITING
XFILTERS, below.
X.IP "\f2Frame\fP"
XThis selection provides access to the original frame menu, so that the window
Xcan be opened, closed, moved, resized, etc.
X.SH EDITING FILTERS
X.LP
XWhen the \f2Edit Filters\fP item is selected from the frame menu, the filter
Xediting dialog box appears.  This dialog box is divided into three windows:
Xthe control panel, the filter display, and the default message actions.
X.LP
XThe control panel has several buttons which allow filters to be loaded, saved,
Xand modified.  These buttons are:
X.IP "\f2Load\fP"
XThis button causes filters to be read from the file indicated in the \*(lqFile\*(rq
Xfield in the control panel.  The loaded filters are displayed in the filter
Xdisplay, replacing any previous filters.  The loaded filters \f2do not\fP
Xreplace the active filter set used by \f2contool\fP until the \f2Done\fP
Xbutton is clicked.
X.IP "\f2Save\fP"
XThe current set of filters in the filter display is written to the file specified
Xin the \*(lqFile\*(rq field in the control.
X.IP "\f2Cut\fP"
XAny filters in the filter display which are selected (see below) are removed from the
Xfilter display and placed in the clipboard.  They can be retrieved with the 
X\f2Paste\fP button.  Any previous contents of the clipboard are lost.  If no filters
Xare selected, the clipboard is cleared.
X.IP "\f2Copy\fP"
XAny filters in the filter display which are selected (see below) are copied to
Xthe clipboard.  They can be retrieved with the 
X\f2Paste\fP button.  Any previous contents of the clipboard are lost.  If no filters
Xare selected, the clipboard is cleared.
X.IP "\f2Paste\fP"
XThe contents of the clipboard are pasted into the filter display \f2before\fP the
Xfirst selected filter.  If no filter is selected, they are pasted at the end of the filter list.
XIf the clipboard is empty, a single empty filter is pasted.  A simple way to create a
Xnew blank filter is to clear any filter selections, click \f2Cut\fP and then \f2Paste\fP.
X.IP "\f2Reset\fP"
XThe current set of \f2contool\fP filters are displayed in the filter display, and the
Xdefault message settings are displayed in the default message actions.  Any previous
Xfilters or default actions are lost.
X.IP "\f2Done\fP"
XThe filters in the filter display, and the default message actions, are made the
Xcurrent \f2contool\fP filter set, and the edit dialog box is closed.  If there are
Xany errors in the displayed filters, they must be corrected before the filters will be accepted.
X.IP "\f2Cancel\fP"
XThe edit dialog box is closed, and any filters in the filter display are discarded.
XClicking \f2Cancel\fP will always exit the edit dialog box, even if filter errors exist.
X.LP
XThe filter display window shows one filter per line.  Each filter is preceded by several
Xsmall icons, representing the selection handle and filter attributes.
X.LP
XThe filter handle is a small hollow triangle pointing at the filter.  When a filter
Xis selected, the triangle will become solid.  The left and middle mouse buttons
Xallow filters to be selected by clicking on the filter handles.  Clicking the left
Xmouse button on a handle clears any previously selected handles, and selects
Xthe current handle.  Clicking the middle mouse button on a handle toggles that
Xfilter's selection handle, without changing any existing selections.  This is
Xin keeping with the Sun standard mouse actions: left button makes an initial
Xselection, middle button modifies the existing selection.  To clear all selections,
Xfirst click left on any handle to clear existing selections, and then click middle to clear
Xthe current selection.
X.LP
XEach filter has several icons representing the action \f2contool\fP should take when
Xa message matches that filter.  These icons are:
X.IP "\f2Save Message\fP"
XThis icon toggles between an arrow pointing into a small window (indicating that
Xthe message will be saved) and an arrow pointing into a trash container (indicating
Xthat the message is to be discarded).  If a message is to be saved, several other
Xicons become visible, controlling beeping, flashing, and window behavior.
X.IP "\f2Time Stamp\fP"
XThis icon controls whether saved messages will be time stamped when written to 
Xthe console window.  A clock face with hands indicates that a message will be
Xtime stamped; a question mark in place of the hands prevents time stamping.
X.IP "\f2Open Window\fP"
XWhen this icon is set to the \*(lqpop open\*(rq image (a small dot expanding
Xinto a window), \f2contool\fP will open its window when a message matches
Xthe associated filter pattern.  If set to the \*(lqdon't open\*(rq image (just 
Xa small dot), the window will not change its state when the message arrives.
X.IP "\f2Flash Icon\fP"
XWhen this icon is a dot surrounded by radiating lines, \f2contool\fP will flash
Xits icon when the appropriate message arrives.  If the radiating lines are
Xnot present, no flashing behavior will occur.
X.IP "\f2Beep\fP"
XThis icon switches between \*(lqquiet\*(rq (an image of a person holding
Xtheir finger to their lips) and from one to four \*(lqbeep\*(rq symbols
X(sounds waves radiating from a small dot).  When set to \*(lqquiet\*(rq,
X\f2contool\fP will not sound the terminal bell when a message arrives.
XIf one or more \*(lqbeep\*(rq symbols are shown, \f2contool\fP will sound
Xthe bell the indicated number of times.
X.IP "\f2Message Length\fP"
XThis icon toggles between \*(lqsingle line\*(rq (one small line of text)
Xand \*(lqmulti-line\*(rq (several small lines of text) mode.  In single
Xline mode, one text field is provided for the filter pattern, and the
Xfilter will be applied to each line of each console message that
Xarrives.  In multi-line mode, two fields, one for the starting pattern
Xand another for the ending pattern, are provided.  If a console message
Xmatches the starting pattern, that filter will be in effect until a
Xsubsequent line matches the ending pattern.  All of the lines will be
Xsaved or discarded as indicated by the \f2Save Message\fP icon.  The
Xbeeping, flashing, and window behaviors will only occur when the
Xfirst line of the message is received.
X.IP ""
XSince it is not possible imbed newline characters in a filter pattern,
Xsingle line filters should be used for single line messages (like someone
Xbecoming superuser, for example) and multi-line filters should be
Xused for block messages (like certain window system messages).
X.LP
XAn incoming message is tested against each filter in the order in which
Xthey are displayed in the filter display.  The first filter to match 
Xis used to control the message.  If a multi-line filter matches, all
Xsubsequent console messages will be controlled by that filter until the 
Xending pattern is matched.
X.LP
XFilter patterns can contain regular expressions like those accepted by \f2ed\fP(1).
X.LP
XIf a message does not match any filter, the actions in the default message
Xwindow are applied to the filter.  The icons in this window have the same
Xmeaning as those in the filter display, with the exception that the \f2Save Message\fP
Xicon cannot be switched to \*(lqignore\*(rq mode, and there is no single line/multi-line
Xcnotrol.
X.SH FILE FORMAT
X.LP
X\f2Contool\fP reads and writes filters in a human-readable format.
XIn the filter file, blank lines and comments (starting with \*(lq#\*(rq
Xand continuing to the end of line) are ignored.  Remaining lines
Xdescribe filters, one per line.  Except within the actual filter
Xpattern, case is not significant when reading the filter file.
X.LP
XThe first token on each line must be either \*(lqsave\*(rq or \*(lqignore\*(rq.
XIf the first token is \*(lqsave\*(rq, one or more of the following filter
Xattributes can follow the word \*(lqsave\*(rq:
X.IP "\f2beep\fP <n>"
Xdetermines the number of times the terminal bell will be sounded for this filter.
XThe default is zero.
X.IP "\f2flash\fP or \f2noflash\fP"
Xdetermines if the icon will flash for this filter.
XThe default is \f2noflash\fP.
X.IP "\f2open\fP or \f2noopen\fP"
Xdetermines if the window will pop open for this filter.
XThe default is \f2noopen\fP.
X.IP "\f2stamp\fP or \f2nostamp\fP"
Xdetermines if messages matching this filter will be timestamped in the console window.
XThe default is \f2nostamp\fP.
X.LP
XIf the first token is \*(lqignore\*(rq, none of these attributes are allowed.
X.LP
XAfter the \*(lqignore\*(rq token, or the \*(lqsave\*(rq token and optional
Xattributes, are quoted strings representing the filter.  For single line
Xfilters, one quote string is permitted.  For multi-line filters, two
Xquoted strings, separated by the word \*(lqto\*(rq, are expected.
X.LP
XAs an example, the following two lines are valid filter specifications:
X.IP ""
XSAVE BEEP 0 NOFLASH NOOPEN STAMP 'SU: chuck'
X.br
XIGNORE 'Window data lock' TO 'The offending process'
X.SH FILES
X.ta 2i
Xcontool.icon	default \f3\(hyg\fP icon
X.br
Xstopsign.icon	default \f3\(hyb\fP icon
X.br
Xstopsign_inv.icon	default \f3\(hyf\fP icon
X.br
X~/.contool	filter pattern file
X.SH SEE ALSO
Xed(1), suntools(1)
X.SH AUTHOR
X.LP
XChuck Musciano
X.br
XAdvanced Technology Department
X.br
XHarris Corporation
X.br
XPO Box 37, MS 3A/1912
X.br
XMelbourne, FL 32902
X.br
X(407) 727-6131
X.br
XARPA: chuck@trantor.harris-atd.com
X.SH BUGS
X.LP
X\f2Contool\fP is a view-only tool, and there is no way to type commands
Xon the console.
END_OF_FILE
if test 14027 -ne `wc -c <'contool.man'`; then
    echo shar: \"'contool.man'\" unpacked with wrong size!
fi
# end of 'contool.man'
fi
if test -f 'dialog.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'dialog.c'\"
else
echo shar: Extracting \"'dialog.c'\" \(11074 characters\)
sed "s/^X//" >'dialog.c' <<'END_OF_FILE'
X/************************************************************************/
X/*	Copyright 1988, 1989 by Chuck Musciano and Harris Corporation	*/
X/*									*/
X/*	Permission to use, copy, modify, and distribute this software	*/
X/*	and its documentation for any purpose and without fee is	*/
X/*	hereby granted, provided that the above copyright notice	*/
X/*	appear in all copies and that both that copyright notice and	*/
X/*	this permission notice appear in supporting documentation, and	*/
X/*	that the name of Chuck Musciano and Harris Corporation not be	*/
X/*	used in advertising or publicity pertaining to distribution	*/
X/*	of the software without specific, written prior permission.	*/
X/*	Chuck Musciano and Harris Corporation make no representations	*/
X/*	about the suitability of this software for any purpose.  It is	*/
X/*	provided "as is" without express or implied warranty.  This 	*/
X/*	software may not be sold without the prior explicit permission	*/
X/*	of Harris Corporation.						*/
X/************************************************************************/
X
X#include	<stdio.h>
X
X#include	<sys/file.h>
X
X#include	<suntool/sunview.h>
X#include	<suntool/panel.h>
X
X#include	"contool.h"
X#include	"entry.h"
X
XPUBLIC	Pixrect	*better_button_image();
X
XEXPORT	Pixfont	*bold, *regular;
XEXPORT	Panel	other;
X
XPRIVATE	Frame	dialog = NULL;
XPRIVATE	Panel	items;
XPRIVATE	Panel_item	file_name;
X
X/************************************************************************/
X/*	Support routines for edit buttons				*/
X/************************************************************************/
X
X/************************************************************************/
XPRIVATE	f_ptr	update_filters()
X
X{	int	i, curr, limit, errors = FALSE;
X	char	*result, *start, *end;
X	f_ptr	f, new_filters = NULL;
X	
X	for (i = 0; i < entries; i++)
X	   panel_set(entry[i].handle, PANEL_VALUE, 0, 0);
X	for (i = 0; i < entries; i++)
X	   if (entry[i].created) {
X	      start = (char *) panel_get(entry[i].start, PANEL_VALUE);
X	      if (panel_get(entry[i].lines, PANEL_VALUE)) {
X	         if (*start == '\0') {
X	            window_error("Filter error:\n   Filter %d is missing its starting pattern", i + 1);
X	            panel_set(entry[i].handle, PANEL_VALUE, 1, 0);
X	            panel_set(items, PANEL_CARET_ITEM, entry[i].start, 0);
X	            errors = TRUE;
X	            continue;
X	            }
X	         end = (char *) panel_get(entry[i].end, PANEL_VALUE);
X	         if (*end == '\0') {
X	            window_error("Filter error:\n   Filter %d is missing its ending pattern", i + 1);
X	            panel_set(entry[i].handle, PANEL_VALUE, 1, 0);
X	            panel_set(items, PANEL_CARET_ITEM, entry[i].end, 0);
X	            errors = TRUE;
X	            continue;
X	            }
X	         }
X	      else {
X	         end = NULL;
X	         if (*start == '\0') {
X	            window_error("Filter error:\n   Filter %d has no pattern specified", i + 1);
X	            panel_set(entry[i].handle, PANEL_VALUE, 1, 0);
X	            panel_set(items, PANEL_CARET_ITEM, entry[i].start, 0);
X	            errors = TRUE;
X	            continue;
X	            }
X	         }
X	      if (result = compile_exp(NULL, start, end)) {
X	         window_error("Error in filter %d:\n   %s", i + 1, result);
X	         panel_set(entry[i].handle, PANEL_VALUE, 1, 0);
X	         panel_set(items, PANEL_CARET_ITEM, entry[i].start, 0);
X	         errors = TRUE;
X	         continue;
X	         }
X	      }
X	if (errors)
X	   return(NULL);
X	for (i = 0, f = next_filter(&new_filters, &curr, &limit); i < MAX_ENTRIES; i++)
X	   if (entry[i].created) {
X	      f->save  = (int) panel_get(entry[i].save,  PANEL_VALUE);
X	      f->beep  = (int) panel_get(entry[i].beep,  PANEL_VALUE);
X	      f->open  = (int) panel_get(entry[i].open,  PANEL_VALUE);
X	      f->flash = (int) panel_get(entry[i].flash, PANEL_VALUE);
X	      f->stamp = (int) panel_get(entry[i].stamp, PANEL_VALUE);
X	      f->start = strsave(panel_get(entry[i].start, PANEL_VALUE));
X	      f->end   = panel_get(entry[i].lines, PANEL_VALUE)? strsave(panel_get(entry[i].end, PANEL_VALUE)) : NULL;
X	      f->valid = TRUE;
X	      compile_exp(f, f->start, f->end);
X	      f = next_filter(&new_filters, &curr, &limit);
X	      }
X	return(new_filters);
X}
X
X/************************************************************************/
X/*	Edit button action routines					*/
X/************************************************************************/
X
X/************************************************************************/
XPRIVATE	load_filters()
X
X{	char	*path, *result;
X	f_ptr	new_filters, f;
X
X	path = (char *) panel_get(file_name, PANEL_VALUE);
X	if (access(path, R_OK) == 0) {
X	   if (new_filters = read_filters(path, window_error)) {
X	      for (f = new_filters; f->start; f++)
X	         if (result = compile_exp(f, f->start, f->end)) {
X	            window_error("Bad filter encountered in %s:\n   %s", path, result);
X	            f->valid = FALSE;
X	            }
X	      update_edit_dialog(new_filters);
X	      free_filters(new_filters);
X	      }
X	   }
X	else
X	   window_error("Cannot read filter file %s", path);
X}
X
X/************************************************************************/
XPRIVATE	save_filters()
X
X{	char	*path;
X	f_ptr	filters;
X
X	if (filters = update_filters()) {
X	   path = (char *) panel_get(file_name, PANEL_VALUE);
X	   write_filters(filters, path, window_error);
X	   }
X	else
X	   window_error("Filters have not been saved");
X}
X
X/************************************************************************/
XPRIVATE	cut_filter()
X
X{	int	i, j;
X
X	clear_clipboard();
X	for (i = 0; i < entries; i++)
X	   if (panel_get(entry[i].handle, PANEL_VALUE)) {
X	      add_to_clipboard(entry + i);
X	      delete_entry(entry + i);
X	      }
X	   else if (cb_size > 0)
X	      move_entry(entry, i, i - cb_size);
X	entries -= cb_size;
X	panel_update_scrolling_size(items);
X	panel_paint(items, PANEL_NO_CLEAR);
X}
X
X/************************************************************************/
XPRIVATE	copy_filter()
X
X{	int	i;
X	f_ptr	f;
X
X	clear_clipboard();
X	for (i = cb_size = 0; i < entries; i++)
X	   if (panel_get(entry[i].handle, PANEL_VALUE))
X	      add_to_clipboard(entry + i);
X}
X
X/************************************************************************/
XPRIVATE	paste_filter()
X
X{	int	i, j, count;
X
X	for (i = 0; i < entries; i++)
X	   if (panel_get(entry[i].handle, PANEL_VALUE))
X	      break;
X	for (j = entries - 1, count = cb_size? cb_size : 1; j >= i; j--)
X	   move_entry(entry, j, j + count);
X	if (cb_size == 0) {
X	   update_entry(items, entry, i, NULL);
X	   panel_set(items, PANEL_CARET_ITEM, entry[i].start, 0);
X	   entries++;
X	   }
X	else {
X	   for (j = 0; j < cb_size; j++)
X	      update_entry(items, entry, i + j, clipboard + j);
X	   entries += cb_size;
X	   }
X	panel_update_scrolling_size(items);
X	panel_paint(items, PANEL_NO_CLEAR);
X}
X
X/************************************************************************/
XPRIVATE	done_edit()
X
X{	f_ptr	new_filters;
X
X	if (new_filters = update_filters()) {
X	   free_filters(filters);
X	   filters = new_filters;
X	   curr_filter = NULL;
X	   install_defaults();
X	   strcpy(filter_path, panel_get(file_name, PANEL_VALUE));
X	   window_set(dialog, WIN_SHOW, FALSE, 0);
X	   }
X}
X
X/************************************************************************/
XPRIVATE	reset_edit()
X
X{
X	update_edit_dialog(filters);
X	update_defaults();
X}
X
X/************************************************************************/
XPRIVATE	quit_edit()
X
X{
X	window_set(dialog, WIN_SHOW, FALSE, 0);
X}
X
X/************************************************************************/
X/*	External entry points to this module				*/
X/************************************************************************/
X
X/************************************************************************/
XEXPORT	update_edit_dialog(filters)
X
Xf_ptr	filters;
X
X{	register	int	i;
X	register	e_ptr	e;
X	register	f_ptr	f;
X
X	for (i = 0, f = filters; f->start; i++, e++, f++) {
X	   create_entry(items, entry, i);
X	   update_entry(items, entry, i, f);
X	   }
X	for (entries = i, e = entry + i; i < MAX_ENTRIES; i++, e++)
X	   delete_entry(e);
X	panel_update_scrolling_size(items);
X	panel_paint(items, PANEL_CLEAR);
X}
X
X/************************************************************************/
XEXPORT	Frame	create_dialog_box(base)
X
XFrame	base;
X
X{	Panel	control;
X	Panel_item	c, p, d;
X	int	i;
X
X	if (dialog)
X	   return;
X	bold = pf_open(BOLD_FONT);
X	regular = pf_open(REGULAR_FONT);
X	dialog = window_create(base, FRAME,
X				  FRAME_LABEL, "<< Contool Filter Editor >>",
X				  FRAME_SHOW_LABEL, TRUE,
X				  FRAME_NO_CONFIRM, TRUE,
X				  FRAME_DONE_PROC, done_edit,
X			       0);
X	control = window_create(dialog, PANEL,
X				   WIN_WIDTH, ITEM_WIDTH,
X				0);
X	panel_create_item(control, PANEL_BUTTON,
X			     PANEL_LABEL_IMAGE, better_button_image(control, "Load", 12, 4, bold),
X			     PANEL_NOTIFY_PROC, load_filters,
X			  0);
X	panel_create_item(control, PANEL_BUTTON,
X			     PANEL_LABEL_IMAGE, better_button_image(control, "Save", 12, 3, bold),
X			     PANEL_NOTIFY_PROC, save_filters,
X			  0);
X	c = panel_create_item(control, PANEL_BUTTON,
X			     PANEL_LABEL_IMAGE, better_button_image(control, "Cut", 12, 4, bold),
X			     PANEL_NOTIFY_PROC, cut_filter,
X			  0);
X	p = panel_create_item(control, PANEL_BUTTON,
X			     PANEL_LABEL_IMAGE, better_button_image(control, "Paste", 12, 3, bold),
X			     PANEL_NOTIFY_PROC, paste_filter,
X			  0);
X	d = panel_create_item(control, PANEL_BUTTON,
X			     PANEL_LABEL_IMAGE, better_button_image(control, "Done", 12, 4, bold),
X			     PANEL_NOTIFY_PROC, done_edit,
X			  0);
X	file_name = panel_create_item(control, PANEL_TEXT,
X					 PANEL_ITEM_Y, 35,
X					 PANEL_LABEL_STRING, "File:",
X					 PANEL_LABEL_FONT, bold,
X					 PANEL_VALUE_STORED_LENGTH, 256,
X					 PANEL_VALUE_DISPLAY_LENGTH, 24,
X					 PANEL_VALUE_FONT, regular,
X					 PANEL_VALUE, filter_path,
X				      0);
X	panel_create_item(control, PANEL_BUTTON,
X			     PANEL_ITEM_Y, 31,
X			     PANEL_LABEL_IMAGE, better_button_image(control, "Copy", 12, 4, bold),
X			     PANEL_NOTIFY_PROC, copy_filter,
X			     PANEL_ITEM_X, panel_get(c, PANEL_ITEM_X),
X			  0);
X	panel_create_item(control, PANEL_BUTTON,
X			     PANEL_ITEM_Y, 31,
X			     PANEL_LABEL_IMAGE, better_button_image(control, "Reset", 12, 4, bold),
X			     PANEL_NOTIFY_PROC, reset_edit,
X			     PANEL_ITEM_X, panel_get(p, PANEL_ITEM_X),
X			  0);
X	panel_create_item(control, PANEL_BUTTON,
X			     PANEL_ITEM_Y, 31,
X			     PANEL_LABEL_IMAGE, better_button_image(control, "Cancel", 12, 4, bold),
X			     PANEL_NOTIFY_PROC, quit_edit,
X			     PANEL_ITEM_X, panel_get(d, PANEL_ITEM_X),
X			  0);
X	window_fit_height(control);
X	items = window_create(dialog, PANEL,
X				 WIN_X, 0,
X				 WIN_BELOW, control,
X				 WIN_WIDTH, ITEM_WIDTH,
X				 WIN_HEIGHT, ROW_HEIGHT * 10 + ROW_MARGIN * 11,
X				 WIN_VERTICAL_SCROLLBAR, scrollbar_create(0),
X			      0);
X	other = window_create(dialog, PANEL,
X				 WIN_X, 0,
X				 WIN_BELOW, items,
X				 WIN_WIDTH, ITEM_WIDTH,
X				 WIN_HEIGHT, ROW_HEIGHT + ROW_MARGIN * 2,
X			      0);
X	window_fit(dialog);
X	for (i = 0; i < MAX_ENTRIES; i++)
X	   entry[i].created = FALSE;
X	return(dialog);
X}
END_OF_FILE
if test 11074 -ne `wc -c <'dialog.c'`; then
    echo shar: \"'dialog.c'\" unpacked with wrong size!
fi
# end of 'dialog.c'
fi
echo shar: End of archive 3 \(of 3\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 3 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

Chuck Musciano				ARPA  : chuck@trantor.harris-atd.com
Harris Corporation 			Usenet: ...!uunet!x102a!trantor!chuck
PO Box 37, MS 3A/1912			AT&T  : (407) 727-6131
Melbourne, FL 32902			FAX   : (407) 727-{5118,5227,4004}

Gee, Beaver, everything that's fun can get you in trouble.  Haven't you
learned that yet? --Gilbert