/*
 * Copyright 1993, 1994, 1995, 1999, 2000 by Paul Mattes.
 *  Permission to use, copy, modify, and distribute this software and its
 *  documentation for any purpose and without fee is hereby granted,
 *  provided that the above copyright notice appear in all copies and that
 *  both that copyright notice and this permission notice appear in
 *  supporting documentation.
 */

/*
 *	trace_ds.c
 *		3270 data stream tracing.
 *
 */

#include "globals.h"

#if defined(X3270_TRACE) /*[*/

#if defined(X3270_DISPLAY) /*[*/
#include <X11/StringDefs.h>
#include <X11/Xaw/Dialog.h>
#endif /*]*/
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <stdarg.h>
#include <fcntl.h>
#include "3270ds.h"
#include "appres.h"
#include "objects.h"
#include "resources.h"
#include "ctlr.h"

#include "ctlrc.h"
#include "menubarc.h"
#include "popupsc.h"
#include "printc.h"
#include "savec.h"
#include "tablesc.h"
#include "telnetc.h"
#include "trace_dsc.h"
#include "utilc.h"

/* Statics */
static int      dscnt = 0;
static int      tracewindow_pid = -1;

/* Globals */
FILE           *tracef = (FILE *) 0;
struct timeval  ds_ts;
Boolean         trace_skipping = False;

static const char *
unknown(unsigned char value)
{
	static char buf[64];

	(void) sprintf(buf, "unknown[0x%x]", value);
	return buf;
}

/* display a (row,col) */
const char *
rcba(int baddr)
{
	static char buf[16];

	(void) sprintf(buf, "(%d,%d)", baddr/COLS + 1, baddr%COLS + 1);
	return buf;
}

const char *
see_ebc(unsigned char ch)
{
	static char buf[8];

	switch (ch) {
	    case FCORDER_NULL:
		return "NULL";
	    case FCORDER_SUB:
		return "SUB";
	    case FCORDER_DUP:
		return "DUP";
	    case FCORDER_FM:
		return "FM";
	    case FCORDER_FF:
		return "FF";
	    case FCORDER_CR:
		return "CR";
	    case FCORDER_NL:
		return "NL";
	    case FCORDER_EM:
		return "EM";
	    case FCORDER_EO:
		return "EO";
	}
	if (ebc2asc[ch])
		(void) sprintf(buf, "%c", ebc2asc[ch]);
	else
		(void) sprintf(buf, "\\%o", ch);
	return buf;
}

const char *
see_aid(unsigned char code)
{
	switch (code) {
	case AID_NO: 
		return "NoAID";
	case AID_ENTER: 
		return "Enter";
	case AID_PF1: 
		return "PF1";
	case AID_PF2: 
		return "PF2";
	case AID_PF3: 
		return "PF3";
	case AID_PF4: 
		return "PF4";
	case AID_PF5: 
		return "PF5";
	case AID_PF6: 
		return "PF6";
	case AID_PF7: 
		return "PF7";
	case AID_PF8: 
		return "PF8";
	case AID_PF9: 
		return "PF9";
	case AID_PF10: 
		return "PF10";
	case AID_PF11: 
		return "PF11";
	case AID_PF12: 
		return "PF12";
	case AID_PF13: 
		return "PF13";
	case AID_PF14: 
		return "PF14";
	case AID_PF15: 
		return "PF15";
	case AID_PF16: 
		return "PF16";
	case AID_PF17: 
		return "PF17";
	case AID_PF18: 
		return "PF18";
	case AID_PF19: 
		return "PF19";
	case AID_PF20: 
		return "PF20";
	case AID_PF21: 
		return "PF21";
	case AID_PF22: 
		return "PF22";
	case AID_PF23: 
		return "PF23";
	case AID_PF24: 
		return "PF24";
	case AID_OICR: 
		return "OICR";
	case AID_MSR_MHS: 
		return "MSR_MHS";
	case AID_SELECT: 
		return "Select";
	case AID_PA1: 
		return "PA1";
	case AID_PA2: 
		return "PA2";
	case AID_PA3: 
		return "PA3";
	case AID_CLEAR: 
		return "Clear";
	case AID_SYSREQ: 
		return "SysReq";
	case AID_QREPLY:
		return "QueryReplyAID";
	default: 
		return unknown(code);
	}
}

const char *
see_attr(unsigned char fa)
{
	static char buf[256];
	const char *paren = "(";

	buf[0] = '\0';

	if (fa & 0x04) {
		(void) strcat(buf, paren);
		(void) strcat(buf, "protected");
		paren = ",";
		if (fa & 0x08) {
			(void) strcat(buf, paren);
			(void) strcat(buf, "skip");
			paren = ",";
		}
	} else if (fa & 0x08) {
		(void) strcat(buf, paren);
		(void) strcat(buf, "numeric");
		paren = ",";
	}
	switch (fa & 0x03) {
	case 0:
		break;
	case 1:
		(void) strcat(buf, paren);
		(void) strcat(buf, "detectable");
		paren = ",";
		break;
	case 2:
		(void) strcat(buf, paren);
		(void) strcat(buf, "intensified");
		paren = ",";
		break;
	case 3:
		(void) strcat(buf, paren);
		(void) strcat(buf, "nondisplay");
		paren = ",";
		break;
	}
	if (fa & 0x20) {
		(void) strcat(buf, paren);
		(void) strcat(buf, "modified");
		paren = ",";
	}
	if (strcmp(paren, "("))
		(void) strcat(buf, ")");
	else
		(void) strcpy(buf, "(default)");

	return buf;
}

static const char *
see_highlight(unsigned char setting)
{
	switch (setting) {
	    case XAH_DEFAULT:
		return "default";
	    case XAH_NORMAL:
		return "normal";
	    case XAH_BLINK:
		return "blink";
	    case XAH_REVERSE:
		return "reverse";
	    case XAH_UNDERSCORE:
		return "underscore";
	    case XAH_INTENSIFY:
		return "intensify";
	    default:
		return unknown(setting);
	}
}

#if !defined(X3270_DISPLAY) /*[*/
static
#endif /*]*/
const char *
see_color(unsigned char setting)
{
	static const char *color_name[] = {
	    "neutralBlack",
	    "blue",
	    "red",
	    "pink",
	    "green",
	    "turquoise",
	    "yellow",
	    "neutralWhite",
	    "black",
	    "deepBlue",
	    "orange",
	    "purple",
	    "paleGreen",
	    "paleTurquoise",
	    "grey",
	    "white"
	};

	if (setting == XAC_DEFAULT)
		return "default";
	else if (setting < 0xf0 || setting > 0xff)
		return unknown(setting);
	else
		return color_name[setting - 0xf0];
}

static const char *
see_transparency(unsigned char setting)
{
	switch (setting) {
	    case XAT_DEFAULT:
		return "default";
	    case XAT_OR:
		return "or";
	    case XAT_XOR:
		return "xor";
	    case XAT_OPAQUE:
		return "opaque";
	    default:
		return unknown(setting);
	}
}

static const char *
see_validation(unsigned char setting)
{
	static char buf[64];
	const char *paren = "(";

	(void) strcpy(buf, "");
	if (setting & XAV_FILL) {
		(void) strcat(buf, paren);
		(void) strcat(buf, "fill");
		paren = ",";
	}
	if (setting & XAV_ENTRY) {
		(void) strcat(buf, paren);
		(void) strcat(buf, "entry");
		paren = ",";
	}
	if (setting & XAV_TRIGGER) {
		(void) strcat(buf, paren);
		(void) strcat(buf, "trigger");
		paren = ",";
	}
	if (strcmp(paren, "("))
		(void) strcat(buf, ")");
	else
		(void) strcpy(buf, "(none)");
	return buf;
}

static const char *
see_outline(unsigned char setting)
{
	static char buf[64];
	const char *paren = "(";

	(void) strcpy(buf, "");
	if (setting & XAO_UNDERLINE) {
		(void) strcat(buf, paren);
		(void) strcat(buf, "underline");
		paren = ",";
	}
	if (setting & XAO_RIGHT) {
		(void) strcat(buf, paren);
		(void) strcat(buf, "right");
		paren = ",";
	}
	if (setting & XAO_OVERLINE) {
		(void) strcat(buf, paren);
		(void) strcat(buf, "overline");
		paren = ",";
	}
	if (setting & XAO_LEFT) {
		(void) strcat(buf, paren);
		(void) strcat(buf, "left");
		paren = ",";
	}
	if (strcmp(paren, "("))
		(void) strcat(buf, ")");
	else
		(void) strcpy(buf, "(none)");
	return buf;
}

const char *
see_efa(unsigned char efa, unsigned char value)
{
	static char buf[64];

	switch (efa) {
	    case XA_ALL:
		(void) sprintf(buf, " all(%x)", value);
		break;
	    case XA_3270:
		(void) sprintf(buf, " 3270%s", see_attr(value));
		break;
	    case XA_VALIDATION:
		(void) sprintf(buf, " validation%s", see_validation(value));
		break;
	    case XA_OUTLINING:
		(void) sprintf(buf, " outlining(%s)", see_outline(value));
		break;
	    case XA_HIGHLIGHTING:
		(void) sprintf(buf, " highlighting(%s)", see_highlight(value));
		break;
	    case XA_FOREGROUND:
		(void) sprintf(buf, " foreground(%s)", see_color(value));
		break;
	    case XA_CHARSET:
		(void) sprintf(buf, " charset(%x)", value);
		break;
	    case XA_BACKGROUND:
		(void) sprintf(buf, " background(%s)", see_color(value));
		break;
	    case XA_TRANSPARENCY:
		(void) sprintf(buf, " transparency(%s)", see_transparency(value));
		break;
	    default:
		(void) sprintf(buf, " %s[0x%x]", unknown(efa), value);
	}
	return buf;
}

const char *
see_efa_only(unsigned char efa)
{
	switch (efa) {
	    case XA_ALL:
		return "all";
	    case XA_3270:
		return "3270";
	    case XA_VALIDATION:
		return "validation";
	    case XA_OUTLINING:
		return "outlining";
	    case XA_HIGHLIGHTING:
		return "highlighting";
	    case XA_FOREGROUND:
		return "foreground";
	    case XA_CHARSET:
		return "charset";
	    case XA_BACKGROUND:
		return "background";
	    case XA_TRANSPARENCY:
		return "transparency";
	    default:
		return unknown(efa);
	}
}

const char *
see_qcode(unsigned char id)
{
	static char buf[64];

	switch (id) {
	    case QR_CHARSETS:
		return "CharacterSets";
	    case QR_IMP_PART:
		return "ImplicitPartition";
	    case QR_SUMMARY:
		return "Summary";
	    case QR_USABLE_AREA:
		return "UsableArea";
	    case QR_COLOR:
		return "Color";
	    case QR_HIGHLIGHTING:
		return "Highlighting";
	    case QR_REPLY_MODES:
		return "ReplyModes";
	    case QR_ALPHA_PART:
		return "AlphanumericPartitions";
	    case QR_DDM:
		return "DistributedDataManagement";
	    default:
		(void) sprintf(buf, "unknown[0x%x]", id);
		return buf;
	}
}

/* Data Stream trace print, handles line wraps */

static char *tdsbuf = CN;
#define TDS_LEN	75

static void
trace_ds_s(char *s)
{
	int len = strlen(s);
	Boolean nl = False;

	if (!toggled(DS_TRACE) || !len)
		return;

	if (s && s[len-1] == '\n') {
		len--;
		nl = True;
	}
	while (dscnt + len >= 75) {
		int plen = 75-dscnt;

		(void) fprintf(tracef, "%.*s ...\n... ", plen, s);
		dscnt = 4;
		s += plen;
		len -= plen;
	}
	if (len) {
		(void) fprintf(tracef, "%.*s", len, s);
		dscnt += len;
	}
	if (nl) {
		(void) fprintf(tracef, "\n");
		dscnt = 0;
	}
}

void
trace_ds(const char *fmt, ...)
{
	va_list args;

	va_start(args, fmt);

	/* allocate buffer */
	if (tdsbuf == CN)
		tdsbuf = Malloc(4096);

	/* print out remainder of message */
	(void) vsprintf(tdsbuf, fmt, args);
	trace_ds_s(tdsbuf);
	va_end(args);
}

/* Conditional event trace. */
void
trace_event(const char *fmt, ...)
{
	va_list args;

	if (!toggled(EVENT_TRACE) || tracef == (FILE *)NULL)
		return;

	va_start(args, fmt);

	/* print out message */
	(void) vfprintf(tracef, fmt, args);
	va_end(args);
}

#if defined(X3270_DISPLAY) /*[*/
static Widget trace_shell = (Widget)NULL;
#endif
static int trace_reason;

/* Callback for "OK" button on trace popup */
static void
tracefile_callback(Widget w, XtPointer client_data, XtPointer call_data unused)
{
	char *tfn;
	char *tracecmd;
	char *full_cmd;
	time_t clk;
	Boolean piped = False;
	int pipefd[2];

#if defined(X3270_DISPLAY) /*[*/
	if (w)
		tfn = XawDialogGetValueString((Widget)client_data);
	else
#endif /*]*/
		tfn = (char *)client_data;
	tfn = do_subst(tfn, True, True);
	if (strchr(tfn, '\'') ||
	    ((int)strlen(tfn) > 0 && tfn[strlen(tfn)-1] == '\\')) {
		popup_an_error("Illegal file name: %s\n", tfn);
		Free(tfn);
		return;
	}
	if (!strcmp(tfn, "stdout")) {
		tracef = stdout;
	} else if (!strcmp(tfn, "none") || !tfn[0]) {

#ifndef AMIGA
		if (pipe(pipefd) < 0) {
#else
		if (1) {
#endif
			popup_an_errno(errno, "pipe() failed");
			Free(tfn);
			return;
		}
		tracef = fdopen(pipefd[1], "w");
		if (tracef == (FILE *)NULL) {
			popup_an_errno(errno, "fdopen() failed");
			Free(tfn);
			return;
		}
		(void) SETLINEBUF(tracef);
		(void) fcntl(pipefd[1], F_SETFD, 1);
		piped = True;
	} else {
		tracef = fopen(tfn, "a");
		if (tracef == (FILE *)NULL) {
			popup_an_errno(errno, tfn);
			Free(tfn);
			return;
		}
		(void) SETLINEBUF(tracef);
		(void) fcntl(fileno(tracef), F_SETFD, 1);
	}

	/* Start the monitor window */
	tracecmd = get_resource(ResTraceCommand);
	if (tracecmd == CN || !strcmp(tracecmd, "none") || tracef == stdout)
		goto done;
#ifndef AMIGA
	switch (tracewindow_pid = fork()) {
	    case 0:	/* child process */
		if (piped) {
			char cmd[64];

			(void) sprintf(cmd, "cat /dev/fd/%d", pipefd[0]);
			(void) execlp("xterm", "xterm", "-title", "trace",
			    "-sb", "-e", "/bin/sh", "-c", cmd, CN);
		} else {
			full_cmd = xs_buffer("%s <'%s'", tracecmd, tfn);
			(void) execlp("xterm", "xterm", "-title", tfn,
			    "-sb", "-e", "/bin/sh", "-c", full_cmd, CN);
		}
		(void) perror("exec(xterm)");
		_exit(1);
	    default:	/* parent */
		if (piped)
			(void) close(pipefd[0]);
		++children;
		break;
	    case -1:	/* error */
		popup_an_errno(errno, "fork()");
		break;
	}
#endif
    done:
	Free(tfn);

	/* Display current status */
	clk = time((time_t *)0);
	(void) fprintf(tracef, "Trace started %s", ctime(&clk));
	(void) fprintf(tracef, " Version: %s\n", build);
	save_yourself();
	(void) fprintf(tracef, " Command: %s\n", command_string);
	(void) fprintf(tracef, " Model %s", model_name);
	(void) fprintf(tracef, ", %s display", 
	    appres.mono ? "monochrome" : "color");
	if (appres.extended)
		(void) fprintf(tracef, ", extended data stream");
	if (!appres.mono)
		(void) fprintf(tracef, ", %scolor",
		    appres.m3279 ? "full " : "pseudo-");
	if (appres.charset)
		(void) fprintf(tracef, ", %s charset", appres.charset);
	if (appres.apl_mode)
		(void) fprintf(tracef, ", APL mode");
	(void) fprintf(tracef, "\n");
	if (CONNECTED)
		(void) fprintf(tracef, " Connected to %s, port %u\n",
		    current_host, current_port);


	/* We're really tracing, turn the flag on. */
	appres.toggle[trace_reason].value = True;
	appres.toggle[trace_reason].changed = True;
	menubar_retoggle(&appres.toggle[trace_reason]);

#if defined(X3270_DISPLAY) /*[*/
	if (w)
		XtPopdown(trace_shell);
#endif /*]*/

	/* Snap the current TELNET options. */
	if (net_snap_options()) {
		(void) fprintf(tracef, " TELNET state:\n");
		trace_netdata('<', obuf, obptr - obuf);
	}

	/* Dump the screen contents and modes into the trace file. */
	if (CONNECTED) {
		/*
		 * Note that if the screen is not formatted, we do not
		 * attempt to save what's on it.  However, if we're in
		 * 3270 SSCP-LU or NVT mode, we'll do a dummy, empty
		 * write to ensure that the display is in the right
		 * mode.
		 */
		if (formatted) {
			(void) fprintf(tracef, " Screen contents:\n");
			obptr = obuf;
#if defined(X3270_TN3270E) /*[*/
			(void) net_add_dummy_tn3270e();
#endif /*]*/
			ctlr_snap_buffer();
			space3270out(2);
			net_add_eor(obuf, obptr - obuf);
			obptr += 2;
			trace_netdata('<', obuf, obptr - obuf);

			obptr = obuf;
#if defined(X3270_TN3270E) /*[*/
			(void) net_add_dummy_tn3270e();
#endif /*]*/
			if (ctlr_snap_modes()) {
				(void) fprintf(tracef, " 3270 modes:\n");
				space3270out(2);
				net_add_eor(obuf, obptr - obuf);
				obptr += 2;
				trace_netdata('<', obuf, obptr - obuf);
			}
		}
#if defined(X3270_TN3270E) /*[*/
		else if (IN_E) {
			obptr = obuf;
			if (net_add_dummy_tn3270e()) {
				(void) fprintf(tracef,
					" Screen contents:\n");
				space3270out(2);
				net_add_eor(obuf, obptr - obuf);
				obptr += 2;
				trace_netdata('<', obuf, obptr - obuf);
			}
		}
#endif /*]*/
	}

	(void) fprintf(tracef, " Data stream:\n");
}

#if defined(X3270_DISPLAY) /*[*/
/* Callback for "No File" button on trace popup */
static void
no_tracefile_callback(Widget w, XtPointer client_data,
	XtPointer call_data unused)
{
	tracefile_callback((Widget)NULL, "", PN);
	XtPopdown(trace_shell);
}
#endif /*]*/

/* Open the trace file. */
static void
tracefile_on(int reason, enum toggle_type tt)
{
	char tracefile_buf[256];
	char *tracefile;

	if (tracef)
		return;

	trace_reason = reason;
	if (appres.secure) {
		tracefile_callback((Widget)NULL, "none", PN);
		return;
	}
	if (appres.trace_file)
		tracefile = appres.trace_file;
	else {
		(void) sprintf(tracefile_buf, "%s/x3trc.%d", appres.trace_dir,
			getpid());
		tracefile = tracefile_buf;
	}

#if defined(X3270_DISPLAY) /*[*/
	if (tt == TT_INITIAL)
#endif /*]*/
	{
		tracefile_callback((Widget)NULL, tracefile, PN);
		return;
	}
#if defined(X3270_DISPLAY) /*[*/
	if (trace_shell == NULL) {
		trace_shell = create_form_popup("trace",
		    tracefile_callback, no_tracefile_callback, FORM_NO_WHITE);
		XtVaSetValues(XtNameToWidget(trace_shell, ObjDialog),
		    XtNvalue, tracefile,
		    NULL);
	}

	/* Turn the toggle _off_ until the popup succeeds. */
	appres.toggle[reason].value = False;
	appres.toggle[reason].changed = True;

	popup_popup(trace_shell, XtGrabExclusive);
#endif /*]*/
}

/* Close the trace file. */
static void
tracefile_off(void)
{
	time_t clk;

	clk = time((time_t *)0);
	(void) fprintf(tracef, "Trace stopped %s", ctime(&clk));
	if (tracewindow_pid != -1)
#ifndef AMIGA
		(void) kill(tracewindow_pid, SIGKILL);
#else
;/*		(void) kill(tracewindow_pid, SIGTERM);*/
#endif
	tracewindow_pid = -1;
	if (tracef != stdout)
		(void) fclose(tracef);
	tracef = (FILE *) NULL;
}

void
toggle_dsTrace(struct toggle *t unused, enum toggle_type tt)
{
	/* If turning on trace and no trace file, open one. */
	if (toggled(DS_TRACE) && !tracef)
		tracefile_on(DS_TRACE, tt);

	/* If turning off trace and not still tracing events, close the
	   trace file. */
	else if (!toggled(DS_TRACE) && !toggled(EVENT_TRACE))
		tracefile_off();

	if (toggled(DS_TRACE))
		(void) gettimeofday(&ds_ts, (struct timezone *)NULL);
}

void
toggle_eventTrace(struct toggle *t unused, enum toggle_type tt)
{
	/* If turning on event debug, and no trace file, open one. */
	if (toggled(EVENT_TRACE) && !tracef)
		tracefile_on(EVENT_TRACE, tt);

	/* If turning off event debug, and not tracing the data stream,
	   close the trace file. */
	else if (!toggled(EVENT_TRACE) && !toggled(DS_TRACE))
		tracefile_off();
}

/* Screen trace file support. */

#if defined(X3270_DISPLAY) /*[*/
static Widget screentrace_shell = (Widget)NULL;
#endif /*]*/
static FILE *screentracef = (FILE *)0;

/*
 * Screen trace function, called when the host clears the screen.
 */
static void
do_screentrace(void)
{
	register int i;

	if (fprint_screen(screentracef, False)) {
		for (i = 0; i < COLS; i++)
			(void) fputc('=', screentracef);
		(void) fputc('\n', screentracef);
	}
}

void
trace_screen(void)
{
	trace_skipping = False;

	if (!toggled(SCREEN_TRACE) || !screentracef)
		return;
	do_screentrace();
}

/* Called from ANSI emulation code to log a single character. */
void
trace_char(char c)
{
	if (!toggled(SCREEN_TRACE) || !screentracef)
		return;
	(void) fputc(c, screentracef);
}

/*
 * Called when disconnecting in ANSI mode, to finish off the trace file
 * and keep the next screen clear from re-recording the screen image.
 * (In a gross violation of data hiding and modularity, trace_skipping is
 * manipulated directly in ctlr_clear()).
 */
void
trace_ansi_disc(void)
{
	int i;

	(void) fputc('\n', screentracef);
	for (i = 0; i < COLS; i++)
		(void) fputc('=', screentracef);
	(void) fputc('\n', screentracef);

	trace_skipping = True;
}

/*
 * Screen tracing callback.
 * Returns True for success, False for failure.
 */
static Boolean
screentrace_cb(char *tfn)
{
	tfn = do_subst(tfn, True, True);
	screentracef = fopen(tfn, "a");
	if (screentracef == (FILE *)NULL) {
		popup_an_errno(errno, tfn);
		Free(tfn);
		return False;
	}
	Free(tfn);
	(void) SETLINEBUF(screentracef);
	(void) fcntl(fileno(screentracef), F_SETFD, 1);

	/* We're really tracing, turn the flag on. */
	appres.toggle[SCREEN_TRACE].value = True;
	appres.toggle[SCREEN_TRACE].changed = True;
	menubar_retoggle(&appres.toggle[SCREEN_TRACE]);
	return True;
}

#if defined(X3270_DISPLAY) /*[*/
/* Callback for "OK" button on screentrace popup */
static void
screentrace_callback(Widget w unused, XtPointer client_data,
    XtPointer call_data unused)
{
	if (screentrace_cb(XawDialogGetValueString((Widget)client_data)))
		XtPopdown(screentrace_shell);
}

/* Callback for second "OK" button on screentrace popup */
static void
onescreen_callback(Widget w, XtPointer client_data, XtPointer call_data unused)
{
	char *tfn;

	if (w)
		tfn = XawDialogGetValueString((Widget)client_data);
	else
		tfn = (char *)client_data;
	tfn = do_subst(tfn, True, True);
	screentracef = fopen(tfn, "a");
	if (screentracef == (FILE *)NULL) {
		popup_an_errno(errno, tfn);
		XtFree(tfn);
		return;
	}
	(void) fcntl(fileno(screentracef), F_SETFD, 1);
	XtFree(tfn);

	/* Save the current image, once. */
	do_screentrace();

	/* Close the file, we're done. */
	(void) fclose(screentracef);
	screentracef = (FILE *)NULL;

	if (w)
		XtPopdown(screentrace_shell);
}
#endif /*]*/

void
toggle_screenTrace(struct toggle *t unused, enum toggle_type tt)
{
	char tracefile_buf[256];
	char *tracefile;

	if (toggled(SCREEN_TRACE)) {
		if (appres.screentrace_file)
			tracefile = appres.screentrace_file;
		else {
			(void) sprintf(tracefile_buf, "%s/x3scr.%d",
				appres.trace_dir, getpid());
			tracefile = tracefile_buf;
		}
		if (tt == TT_INITIAL) {
			(void) screentrace_cb(NewString(tracefile));
			return;
		}
#if defined(X3270_DISPLAY) /*[*/
		if (screentrace_shell == NULL) {
			screentrace_shell = create_form_popup("screentrace",
			    screentrace_callback, onescreen_callback,
			    FORM_NO_WHITE);
			XtVaSetValues(XtNameToWidget(screentrace_shell,
					ObjDialog),
			    XtNvalue, tracefile,
			    NULL);
		}
		appres.toggle[SCREEN_TRACE].value = False;
		appres.toggle[SCREEN_TRACE].changed = True;
		popup_popup(screentrace_shell, XtGrabExclusive);
#endif /*]*/
	} else {
		if (ctlr_any_data() && !trace_skipping)
			do_screentrace();
		(void) fclose(screentracef);
	}
}

#endif /*]*/
